/* See if wv is contained within wsu. If so, deallocate wv and return the index of the already-present copy. If not, add wv to both the vec2ix and ix2vec mappings and return its index. */ static WordSet add_or_dealloc_WordVec( WordSetU* wsu, WordVec* wv_new ) { Bool have; WordVec* wv_old; Word/*Set*/ ix_old = -1; /* Really WordSet, but need something that can safely be casted to a Word* in the lookupFM. Making it WordSet (which is 32 bits) causes failures on a 64-bit platform. */ tl_assert(wv_new->owner == wsu); have = HG_(lookupFM)( wsu->vec2ix, (Word*)&wv_old, (Word*)&ix_old, (Word)wv_new ); if (have) { tl_assert(wv_old != wv_new); tl_assert(wv_old); tl_assert(wv_old->owner == wsu); tl_assert(ix_old < wsu->ix2vec_used); tl_assert(wsu->ix2vec[ix_old] == wv_old); delete_WV( wv_new ); return (WordSet)ix_old; } else { ensure_ix2vec_space( wsu ); tl_assert(wsu->ix2vec); tl_assert(wsu->ix2vec_used < wsu->ix2vec_size); wsu->ix2vec[wsu->ix2vec_used] = wv_new; HG_(addToFM)( wsu->vec2ix, (Word)wv_new, (Word)wsu->ix2vec_used ); if (0) VG_(printf)("aodW %d\n", (Int)wsu->ix2vec_used ); wsu->ix2vec_used++; tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size); return (WordSet)(wsu->ix2vec_used - 1); } }
void HG_(record_error_LockOrder)( Thread* thr, Lock* shouldbe_earlier_lk, Lock* shouldbe_later_lk, ExeContext* shouldbe_earlier_ec, ExeContext* shouldbe_later_ec, ExeContext* actual_earlier_ec ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); tl_assert(HG_(clo_track_lockorders)); init_XError(&xe); xe.tag = XE_LockOrder; xe.XE.LockOrder.thr = thr; xe.XE.LockOrder.shouldbe_earlier_lk = mk_LockP_from_LockN(shouldbe_earlier_lk, False/*!allowed_to_be_invalid*/); xe.XE.LockOrder.shouldbe_earlier_ec = shouldbe_earlier_ec; xe.XE.LockOrder.shouldbe_later_lk = mk_LockP_from_LockN(shouldbe_later_lk, False/*!allowed_to_be_invalid*/); xe.XE.LockOrder.shouldbe_later_ec = shouldbe_later_ec; xe.XE.LockOrder.actual_earlier_ec = actual_earlier_ec; // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_LockOrder, 0, NULL, &xe ); }
void HG_(record_error_Race) ( Thread* thr, Addr data_addr, Int szB, Bool isWrite, Thread* h1_ct, ExeContext* h1_ct_segstart, ExeContext* h1_ct_mbsegendEC ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); # if defined(VGO_linux) /* Skip any races on locations apparently in GOTPLT sections. This is said to be caused by ld.so poking PLT table entries (or whatever) when it writes the resolved address of a dynamically linked routine, into the table (or whatever) when it is called for the first time. */ { VgSectKind sect = VG_(DebugInfo_sect_kind)( NULL, data_addr ); if (0) VG_(printf)("XXXXXXXXX RACE on %#lx %s\n", data_addr, VG_(pp_SectKind)(sect)); /* SectPLT is required on ???-linux */ if (sect == Vg_SectGOTPLT) return; /* SectPLT is required on ppc32/64-linux */ if (sect == Vg_SectPLT) return; /* SectGOT is required on arm-linux */ if (sect == Vg_SectGOT) return; } # endif init_XError(&xe); xe.tag = XE_Race; xe.XE.Race.data_addr = data_addr; xe.XE.Race.szB = szB; xe.XE.Race.isWrite = isWrite; xe.XE.Race.thr = thr; tl_assert(isWrite == False || isWrite == True); tl_assert(szB == 8 || szB == 4 || szB == 2 || szB == 1); /* Skip on the detailed description of the raced-on address at this point; it's expensive. Leave it for the update_extra function if we ever make it that far. */ xe.XE.Race.data_addrinfo.tag = Addr_Undescribed; // FIXME: tid vs thr // Skip on any of the conflicting-access info at this point. // It's expensive to obtain, and this error is more likely than // not to be discarded. We'll fill these fields in in // HG_(update_extra) just above, assuming the error ever makes // it that far (unlikely). xe.XE.Race.h2_ct_accSzB = 0; xe.XE.Race.h2_ct_accIsW = False; xe.XE.Race.h2_ct_accEC = NULL; xe.XE.Race.h2_ct = NULL; tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); xe.XE.Race.h1_ct = h1_ct; xe.XE.Race.h1_ct_mbsegstartEC = h1_ct_segstart; xe.XE.Race.h1_ct_mbsegendEC = h1_ct_mbsegendEC; VG_(maybe_record_error)( thr->coretid, XE_Race, data_addr, NULL, &xe ); }
static Word lock_unique_cmp ( UWord lk1W, UWord lk2W ) { Lock* lk1 = (Lock*)lk1W; Lock* lk2 = (Lock*)lk2W; tl_assert( HG_(is_sane_LockNorP)(lk1) ); tl_assert( HG_(is_sane_LockNorP)(lk2) ); if (lk1->unique < lk2->unique) return -1; if (lk1->unique > lk2->unique) return 1; return 0; }
void HG_(record_error_UnlockBogus) ( Thread* thr, Addr lock_ga ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); init_XError(&xe); xe.tag = XE_UnlockBogus; xe.XE.UnlockBogus.thr = thr; xe.XE.UnlockBogus.lock_ga = lock_ga; // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_UnlockBogus, 0, NULL, &xe ); }
void HG_(record_error_UnlockUnlocked) ( Thread* thr, Lock* lk ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); tl_assert( HG_(is_sane_LockN)(lk) ); init_XError(&xe); xe.tag = XE_UnlockUnlocked; xe.XE.UnlockUnlocked.thr = thr; xe.XE.UnlockUnlocked.lock = mk_LockP_from_LockN(lk); // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_UnlockUnlocked, 0, NULL, &xe ); }
void HG_(record_error_Misc) ( Thread* thr, HChar* errstr ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); tl_assert(errstr); init_XError(&xe); xe.tag = XE_Misc; xe.XE.Misc.thr = thr; xe.XE.Misc.errstr = string_table_strdup(errstr); // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_Misc, 0, NULL, &xe ); }
void HG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) { tl_assert(ai->tag == Addr_Undescribed); /* hctxt/haddr/hszB describe the addr if it is a heap block. */ ExeContext* hctxt; Addr haddr; SizeT hszB; /* First, see if it's in any heap block. Unfortunately this means a linear search through all allocated heap blocks. The assertion says that if it's detected as a heap block, then we must have an allocation context for it, since all heap blocks should have an allocation context. */ Bool is_heapblock = HG_(mm_find_containing_block)( &hctxt, &haddr, &hszB, a ); if (is_heapblock) { tl_assert(is_heapblock == (hctxt != NULL)); ai->tag = Addr_Block; ai->Addr.Block.block_kind = Block_Mallocd; 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.freed_at = VG_(null_ExeContext)();; } else { /* No block found. Search a non-heap block description. */ VG_(describe_addr) (a, ai); } }
/* Announce 'lk'. */ static void announce_LockP ( Lock* lk ) { tl_assert(lk); if (lk == Lock_INVALID) return; /* Can't be announced -- we know nothing about it. */ tl_assert(lk->magic == LockP_MAGIC); if (VG_(clo_xml)) { if (lk->appeared_at) { emit( " <auxwhat>Lock at %p was first observed</auxwhat>\n", (void*)lk ); VG_(pp_ExeContext)( lk->appeared_at ); } } else { if (lk->appeared_at) { VG_(umsg)( " Lock at %p was first observed\n", (void*)lk->guestaddr ); VG_(pp_ExeContext)( lk->appeared_at ); } else { VG_(umsg)( " Lock at %p : no stacktrace for first observation\n", (void*)lk->guestaddr ); } HG_(get_and_pp_addrdescr) (lk->guestaddr); VG_(umsg)("\n"); } }
void HG_(record_error_Misc_w_aux) ( Thread* thr, const HChar* errstr, const HChar* auxstr, ExeContext* auxctx ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); tl_assert(errstr); init_XError(&xe); xe.tag = XE_Misc; xe.XE.Misc.thr = thr; xe.XE.Misc.errstr = string_table_strdup(errstr); xe.XE.Misc.auxstr = auxstr ? string_table_strdup(auxstr) : NULL; xe.XE.Misc.auxctx = auxctx; // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_Misc, 0, NULL, &xe ); }
void HG_(deleteWordSetU) ( WordSetU* wsu ) { void (*dealloc)(void*) = wsu->dealloc; tl_assert(wsu->vec2ix); HG_(deleteFM)( wsu->vec2ix, delete_WV_for_FM, NULL/*val-finalizer*/ ); if (wsu->ix2vec) dealloc(wsu->ix2vec); dealloc(wsu); }
void HG_(record_error_UnlockForeign) ( Thread* thr, Thread* owner, Lock* lk ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); tl_assert( HG_(is_sane_Thread)(owner) ); tl_assert( HG_(is_sane_LockN)(lk) ); init_XError(&xe); xe.tag = XE_UnlockForeign; xe.XE.UnlockForeign.thr = thr; xe.XE.UnlockForeign.owner = owner; xe.XE.UnlockForeign.lock = mk_LockP_from_LockN(lk, False/*!allowed_to_be_invalid*/); // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_UnlockForeign, 0, NULL, &xe ); }
void HG_(record_error_PthAPIerror) ( Thread* thr, HChar* fnname, Word err, HChar* errstr ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); tl_assert(fnname); tl_assert(errstr); init_XError(&xe); xe.tag = XE_PthAPIerror; xe.XE.PthAPIerror.thr = thr; xe.XE.PthAPIerror.fnname = string_table_strdup(fnname); xe.XE.PthAPIerror.err = err; xe.XE.PthAPIerror.errstr = string_table_strdup(errstr); // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_PthAPIerror, 0, NULL, &xe ); }
void HG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) { tl_assert(ai->tag == Addr_Undescribed); /* hctxt/tnr/haddr/hszB describe the addr if it is a heap block. */ ExeContext* hctxt; UInt tnr; Addr haddr; SizeT hszB; /* First, see if it's in any heap block. Unfortunately this means a linear search through all allocated heap blocks. The assertion says that if it's detected as a heap block, then we must have an allocation context for it, since all heap blocks should have an allocation context. */ Bool is_heapblock = HG_(mm_find_containing_block)( &hctxt, &tnr, &haddr, &hszB, a ); if (is_heapblock) { tl_assert(is_heapblock == (hctxt != NULL)); ai->tag = Addr_Block; ai->Addr.Block.block_kind = Block_Mallocd; 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; VG_(initThreadInfo) (&ai->Addr.Block.alloc_tinfo); ai->Addr.Block.alloc_tinfo.tnr = tnr; ai->Addr.Block.freed_at = VG_(null_ExeContext)();; } else { /* No block found. Search a non-heap block description. */ VG_(describe_addr) (a, ai); /* In case ai contains a tid, set tnr to the corresponding helgrind thread number. */ if (ai->tag == Addr_Stack) { Thread* thr = get_admin_threads(); tl_assert(ai->Addr.Stack.tinfo.tid); while (thr) { if (thr->coretid == ai->Addr.Stack.tinfo.tid) { ai->Addr.Stack.tinfo.tnr = thr->errmsg_index; break; } thr = thr->admin; } } } }
static HChar* string_table_strdup ( const HChar* str ) { HChar* copy = NULL; HG_(stats__string_table_queries)++; if (!str) str = "(null)"; if (!string_table) { string_table = VG_(newFM)( HG_(zalloc), "hg.sts.1", HG_(free), string_table_cmp ); } if (VG_(lookupFM)( string_table, NULL, (UWord*)©, (UWord)str )) { tl_assert(copy); if (0) VG_(printf)("string_table_strdup: %p -> %p\n", str, copy ); return copy; } else { copy = HG_(strdup)("hg.sts.2", str); VG_(addToFM)( string_table, (UWord)copy, (UWord)copy ); return copy; } }
static Bool is_sane_Bag_of_Threads ( WordBag* bag ) { Thread* thr; Word count; VG_(initIterBag)( bag ); while (VG_(nextIterBag)( bag, (Word*)&thr, &count )) { if (count < 1) return False; if (!HG_(is_sane_Thread)(thr)) return False; } VG_(doneIterBag)( bag ); return True; }
void HG_(print_access) (StackTrace ips, UInt n_ips, Thr* thr_a, Addr ga, SizeT SzB, Bool isW, WordSetID locksHeldW ) { Thread* threadp; threadp = libhb_get_Thr_hgthread( thr_a ); tl_assert(threadp); if (!threadp->announced) { /* This is for interactive use. We announce the thread if needed, but reset it to not announced afterwards, because we want the thread to be announced on the error output/log if needed. */ announce_one_thread (threadp); threadp->announced = False; } announce_one_thread (threadp); VG_(printf) ("%s of size %d at %p by thread #%d", isW ? "write" : "read", (int)SzB, (void*)ga, threadp->errmsg_index); if (threadp->coretid == VG_INVALID_THREADID) VG_(printf)(" tid (exited)\n"); else VG_(printf)(" tid %u\n", threadp->coretid); { Lock** locksHeldW_P; locksHeldW_P = enumerate_WordSet_into_LockP_vector( HG_(get_univ_lsets)(), locksHeldW, True/*allowed_to_be_invalid*/ ); show_LockP_summary_textmode( locksHeldW_P, "" ); HG_(free) (locksHeldW_P); } VG_(pp_StackTrace) (ips, n_ips); VG_(printf) ("\n"); }
static Lock* mk_LockP_from_LockN ( Lock* lkn ) { Lock* lkp = NULL; HG_(stats__LockN_to_P_queries)++; tl_assert( HG_(is_sane_LockN)(lkn) ); if (!map_LockN_to_P) { map_LockN_to_P = VG_(newFM)( HG_(zalloc), "hg.mLPfLN.1", HG_(free), lock_unique_cmp ); tl_assert(map_LockN_to_P); } if (!VG_(lookupFM)( map_LockN_to_P, NULL, (Word*)&lkp, (Word)lkn)) { lkp = HG_(zalloc)( "hg.mLPfLN.2", sizeof(Lock) ); *lkp = *lkn; lkp->admin = NULL; lkp->magic = LockP_MAGIC; /* Forget about the bag of lock holders - don't copy that. Also, acquired_at should be NULL whenever heldBy is, and vice versa. Also forget about the associated libhb synch object. */ lkp->heldW = False; lkp->heldBy = NULL; lkp->acquired_at = NULL; lkp->hbso = NULL; VG_(addToFM)( map_LockN_to_P, (Word)lkp, (Word)lkp ); } tl_assert( HG_(is_sane_LockP)(lkp) ); return lkp; }
void HG_(record_error_LockOrder)( Thread* thr, Addr before_ga, Addr after_ga, ExeContext* before_ec, ExeContext* after_ec ) { XError xe; tl_assert( HG_(is_sane_Thread)(thr) ); if (!HG_(clo_track_lockorders)) return; init_XError(&xe); xe.tag = XE_LockOrder; xe.XE.LockOrder.thr = thr; xe.XE.LockOrder.before_ga = before_ga; xe.XE.LockOrder.before_ec = before_ec; xe.XE.LockOrder.after_ga = after_ga; xe.XE.LockOrder.after_ec = after_ec; // FIXME: tid vs thr tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); tl_assert( thr->coretid != VG_INVALID_THREADID ); VG_(maybe_record_error)( thr->coretid, XE_LockOrder, 0, NULL, &xe ); }
Bool HG_(get_and_pp_addrdescr) (Addr addr) { Bool ret; AddrInfo glai; glai.tag = Addr_Undescribed; HG_(describe_addr) (addr, &glai); VG_(pp_addrinfo) (addr, &glai); ret = glai.tag != Addr_Unknown; VG_(clear_addrinfo) (&glai); return ret; }
/* Given a normal Lock (LockN), convert it to a persistent Lock (LockP). In some cases the LockN could be invalid (if it's been freed), so we enquire, in hg_main.c's admin_locks list, whether it is in fact valid. If allowed_to_be_invalid is True, then it's OK for the LockN to be invalid, in which case Lock_INVALID is returned. In all other cases, we insist that the LockN is a valid lock, and return its corresponding LockP. Why can LockNs sometimes be invalid? Because they are harvested from locksets that are attached to the OldRef info for conflicting threads. By the time we detect a race, the some of the elements of the lockset may have been destroyed by the client, in which case the corresponding Lock structures we maintain will have been freed. So we check that each LockN is a member of the admin_locks double linked list of all Lock structures. That stops us prodding around in potentially freed-up Lock structures. However, it's not quite a proper check: if a new Lock has been reallocated at the same address as one which was previously freed, we'll wind up copying the new one as the basis for the LockP, which is completely bogus because it is unrelated to the previous Lock that lived there. Let's hope that doesn't happen too often. */ static Lock* mk_LockP_from_LockN ( Lock* lkn, Bool allowed_to_be_invalid ) { Lock* lkp = NULL; HG_(stats__LockN_to_P_queries)++; /* First off, let's do some sanity checks. If allowed_to_be_invalid is False, we _must_ be able to find 'lkn' in admin_locks; else we must assert. If it is True, it's OK for it not to be findable, but in that case we must return Lock_INVALID right away. */ Lock* lock_list = HG_(get_admin_locks)(); while (lock_list) { if (lock_list == lkn) break; lock_list = lock_list->admin_next; } if (lock_list == NULL) { /* We didn't find it. That possibility has to be OK'd by the caller. */ tl_assert(allowed_to_be_invalid); return Lock_INVALID; } /* So we must be looking at a valid LockN. */ tl_assert( HG_(is_sane_LockN)(lkn) ); if (!map_LockN_to_P) { map_LockN_to_P = VG_(newFM)( HG_(zalloc), "hg.mLPfLN.1", HG_(free), lock_unique_cmp ); } if (!VG_(lookupFM)( map_LockN_to_P, NULL, (UWord*)&lkp, (UWord)lkn)) { lkp = HG_(zalloc)( "hg.mLPfLN.2", sizeof(Lock) ); *lkp = *lkn; lkp->admin_next = NULL; lkp->admin_prev = NULL; lkp->magic = LockP_MAGIC; /* Forget about the bag of lock holders - don't copy that. Also, acquired_at should be NULL whenever heldBy is, and vice versa. Also forget about the associated libhb synch object. */ lkp->heldW = False; lkp->heldBy = NULL; lkp->acquired_at = NULL; lkp->hbso = NULL; VG_(addToFM)( map_LockN_to_P, (UWord)lkp, (UWord)lkp ); } tl_assert( HG_(is_sane_LockP)(lkp) ); return lkp; }
Bool HG_(eq_Error) ( VgRes not_used, Error* e1, Error* e2 ) { XError *xe1, *xe2; tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); xe1 = (XError*)VG_(get_error_extra)(e1); xe2 = (XError*)VG_(get_error_extra)(e2); tl_assert(xe1); tl_assert(xe2); switch (VG_(get_error_kind)(e1)) { case XE_Race: return xe1->XE.Race.szB == xe2->XE.Race.szB && xe1->XE.Race.isWrite == xe2->XE.Race.isWrite && (HG_(clo_cmp_race_err_addrs) ? xe1->XE.Race.data_addr == xe2->XE.Race.data_addr : True); case XE_UnlockUnlocked: return xe1->XE.UnlockUnlocked.thr == xe2->XE.UnlockUnlocked.thr && xe1->XE.UnlockUnlocked.lock == xe2->XE.UnlockUnlocked.lock; case XE_UnlockForeign: return xe1->XE.UnlockForeign.thr == xe2->XE.UnlockForeign.thr && xe1->XE.UnlockForeign.owner == xe2->XE.UnlockForeign.owner && xe1->XE.UnlockForeign.lock == xe2->XE.UnlockForeign.lock; case XE_UnlockBogus: return xe1->XE.UnlockBogus.thr == xe2->XE.UnlockBogus.thr && xe1->XE.UnlockBogus.lock_ga == xe2->XE.UnlockBogus.lock_ga; case XE_PthAPIerror: return xe1->XE.PthAPIerror.thr == xe2->XE.PthAPIerror.thr && 0==VG_(strcmp)(xe1->XE.PthAPIerror.fnname, xe2->XE.PthAPIerror.fnname) && xe1->XE.PthAPIerror.err == xe2->XE.PthAPIerror.err; case XE_LockOrder: return xe1->XE.LockOrder.thr == xe2->XE.LockOrder.thr; case XE_Misc: return xe1->XE.Misc.thr == xe2->XE.Misc.thr && 0==VG_(strcmp)(xe1->XE.Misc.errstr, xe2->XE.Misc.errstr); default: tl_assert(0); } /*NOTREACHED*/ tl_assert(0); }
/* Announce (that is, print the point-of-creation) of 'thr'. Only do this once, as we only want to see these announcements once per thread. Returned Bool indicates whether or not an announcement was made. */ static Bool announce_one_thread ( Thread* thr ) { tl_assert(HG_(is_sane_Thread)(thr)); tl_assert(thr->errmsg_index >= 1); if (thr->announced) return False; if (VG_(clo_xml)) { VG_(printf_xml)("<announcethread>\n"); VG_(printf_xml)(" <hthreadid>%d</hthreadid>\n", thr->errmsg_index); if (thr->errmsg_index == 1) { tl_assert(thr->created_at == NULL); VG_(printf_xml)(" <isrootthread></isrootthread>\n"); } else { tl_assert(thr->created_at != NULL); VG_(pp_ExeContext)( thr->created_at ); } VG_(printf_xml)("</announcethread>\n\n"); } else { VG_(umsg)("---Thread-Announcement----------" "--------------------------------" "\n"); VG_(umsg)("\n"); if (thr->errmsg_index == 1) { tl_assert(thr->created_at == NULL); VG_(message)(Vg_UserMsg, "Thread #%d is the program's root thread\n", thr->errmsg_index); } else { 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_(message)(Vg_UserMsg, "\n"); } thr->announced = True; return True; }
WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( SizeT ), void (*dealloc)(void*), Word cacheSize ) { WordSetU* wsu; WordVec* empty; wsu = alloc_nofail( sizeof(WordSetU) ); VG_(memset)( wsu, 0, sizeof(WordSetU) ); wsu->alloc = alloc_nofail; wsu->dealloc = dealloc; wsu->vec2ix = HG_(newFM)( alloc_nofail, dealloc, cmp_WordVecs_for_FM ); wsu->ix2vec_used = 0; wsu->ix2vec_size = 0; wsu->ix2vec = NULL; WCache_INIT(wsu->cache_addTo, cacheSize); WCache_INIT(wsu->cache_delFrom, cacheSize); WCache_INIT(wsu->cache_intersect, cacheSize); WCache_INIT(wsu->cache_minus, cacheSize); empty = new_WV_of_size( wsu, 0 ); wsu->empty = add_or_dealloc_WordVec( wsu, empty ); return wsu; }
WordSet HG_(isSubsetOf) ( WordSetU* wsu, WordSet small, WordSet big ) { wsu->n_isSubsetOf++; return small == HG_(intersectWS)( wsu, small, big ); }
void HG_(record_error_Misc) ( Thread* thr, const HChar* errstr ) { HG_(record_error_Misc_w_aux)(thr, errstr, NULL, NULL); }
void HG_(pp_Error) ( Error* err ) { const Bool xml = VG_(clo_xml); /* a shorthand, that's all */ XError *xe = (XError*)VG_(get_error_extra)(err); tl_assert(xe); switch (VG_(get_error_kind)(err)) { case XE_Misc: { tl_assert( HG_(is_sane_Thread)( xe->XE.Misc.thr ) ); if (xml) { emit( " <kind>Misc</kind>\n"); emit( " <xwhat>\n" ); emit( " <text>Thread #%d: %s</text>\n", (Int)xe->XE.Misc.thr->errmsg_index, xe->XE.Misc.errstr ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.Misc.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } else { emit( "Thread #%d: %s\n", (Int)xe->XE.Misc.thr->errmsg_index, xe->XE.Misc.errstr ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } break; } case XE_LockOrder: { tl_assert( HG_(is_sane_Thread)( xe->XE.LockOrder.thr ) ); if (xml) { emit( " <kind>LockOrder</kind>\n"); emit( " <xwhat>\n" ); emit( " <text>Thread #%d: lock order \"%p before %p\" " "violated</text>\n", (Int)xe->XE.LockOrder.thr->errmsg_index, (void*)xe->XE.LockOrder.before_ga, (void*)xe->XE.LockOrder.after_ga ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.LockOrder.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.LockOrder.before_ec && xe->XE.LockOrder.after_ec) { emit( " <auxwhat>Required order was established by " "acquisition of lock at %p</auxwhat>\n", (void*)xe->XE.LockOrder.before_ga ); VG_(pp_ExeContext)( xe->XE.LockOrder.before_ec ); emit( " <auxwhat>followed by a later acquisition " "of lock at %p</auxwhat>\n", (void*)xe->XE.LockOrder.after_ga ); VG_(pp_ExeContext)( xe->XE.LockOrder.after_ec ); } } else { emit( "Thread #%d: lock order \"%p before %p\" violated\n", (Int)xe->XE.LockOrder.thr->errmsg_index, (void*)xe->XE.LockOrder.before_ga, (void*)xe->XE.LockOrder.after_ga ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.LockOrder.before_ec && xe->XE.LockOrder.after_ec) { emit( " Required order was established by " "acquisition of lock at %p\n", (void*)xe->XE.LockOrder.before_ga ); VG_(pp_ExeContext)( xe->XE.LockOrder.before_ec ); emit( " followed by a later acquisition of lock at %p\n", (void*)xe->XE.LockOrder.after_ga ); VG_(pp_ExeContext)( xe->XE.LockOrder.after_ec ); } } break; } case XE_PthAPIerror: { tl_assert( HG_(is_sane_Thread)( xe->XE.PthAPIerror.thr ) ); if (xml) { emit( " <kind>PthAPIerror</kind>\n"); emit( " <xwhat>\n" ); emit_no_f_c( " <text>Thread #%d's call to %t failed</text>\n", (Int)xe->XE.PthAPIerror.thr->errmsg_index, xe->XE.PthAPIerror.fnname ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.PthAPIerror.thr->errmsg_index ); emit( " </xwhat>\n" ); emit( " <what>with error code %ld (%s)</what>\n", xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } else { emit_no_f_c( "Thread #%d's call to %t failed\n", (Int)xe->XE.PthAPIerror.thr->errmsg_index, 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) ); } break; } case XE_UnlockBogus: { tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockBogus.thr ) ); if (xml) { emit( " <kind>UnlockBogus</kind>\n"); emit( " <xwhat>\n" ); emit( " <text>Thread #%d unlocked an invalid " "lock at %p</text>\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index, (void*)xe->XE.UnlockBogus.lock_ga ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } else { emit( "Thread #%d unlocked an invalid lock at %p\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index, (void*)xe->XE.UnlockBogus.lock_ga ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } break; } case XE_UnlockForeign: { tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockForeign.lock ) ); tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.owner ) ); tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.thr ) ); if (xml) { emit( " <kind>UnlockForeign</kind>\n"); emit( " <xwhat>\n" ); emit( " <text>Thread #%d unlocked lock at %p " "currently held by thread #%d</text>\n", (Int)xe->XE.UnlockForeign.thr->errmsg_index, (void*)xe->XE.UnlockForeign.lock->guestaddr, (Int)xe->XE.UnlockForeign.owner->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockForeign.thr->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockForeign.owner->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.UnlockForeign.lock->appeared_at) { emit( " <auxwhat>Lock at %p was first observed</auxwhat>\n", (void*)xe->XE.UnlockForeign.lock->guestaddr ); VG_(pp_ExeContext)( xe->XE.UnlockForeign.lock->appeared_at ); } } else { emit( "Thread #%d unlocked lock at %p " "currently held by thread #%d\n", (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) ); if (xe->XE.UnlockForeign.lock->appeared_at) { emit( " Lock at %p was first observed\n", (void*)xe->XE.UnlockForeign.lock->guestaddr ); VG_(pp_ExeContext)( xe->XE.UnlockForeign.lock->appeared_at ); } } break; } case XE_UnlockUnlocked: { tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockUnlocked.lock ) ); tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockUnlocked.thr ) ); if (xml) { emit( " <kind>UnlockUnlocked</kind>\n"); emit( " <xwhat>\n" ); emit( " <text>Thread #%d unlocked a " "not-locked lock at %p</text>\n", (Int)xe->XE.UnlockUnlocked.thr->errmsg_index, (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockUnlocked.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.UnlockUnlocked.lock->appeared_at) { emit( " <auxwhat>Lock at %p was first observed</auxwhat>\n", (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); VG_(pp_ExeContext)( xe->XE.UnlockUnlocked.lock->appeared_at ); } } else { 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) ); if (xe->XE.UnlockUnlocked.lock->appeared_at) { emit( " Lock at %p was first observed\n", (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); VG_(pp_ExeContext)( xe->XE.UnlockUnlocked.lock->appeared_at ); } } break; } case XE_Race: { Addr err_ga; HChar* what; Int szB; what = xe->XE.Race.isWrite ? "write" : "read"; szB = xe->XE.Race.szB; err_ga = VG_(get_error_address)(err); tl_assert( HG_(is_sane_Thread)( xe->XE.Race.thr )); if (xe->XE.Race.h2_ct) tl_assert( HG_(is_sane_Thread)( xe->XE.Race.h2_ct )); if (xml) { /* ------ XML ------ */ emit( " <kind>Race</kind>\n" ); emit( " <xwhat>\n" ); emit( " <text>Possible data race during %s of size %d " "at %#lx by thread #%d</text>\n", what, szB, err_ga, (Int)xe->XE.Race.thr->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.Race.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.Race.h2_ct) { tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra emit( " <xauxwhat>\n"); emit( " <text>This conflicts with a previous %s of size %d " "by thread #%d</text>\n", xe->XE.Race.h2_ct_accIsW ? "write" : "read", xe->XE.Race.h2_ct_accSzB, xe->XE.Race.h2_ct->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", xe->XE.Race.h2_ct->errmsg_index); emit(" </xauxwhat>\n"); VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC ); } if (xe->XE.Race.h1_ct) { emit( " <xauxwhat>\n"); emit( " <text>This conflicts with a previous access " "by thread #%d, after</text>\n", xe->XE.Race.h1_ct->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", xe->XE.Race.h1_ct->errmsg_index ); emit(" </xauxwhat>\n"); if (xe->XE.Race.h1_ct_mbsegstartEC) { VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC ); } else { emit( " <auxwhat>(the start of the thread)</auxwhat>\n" ); } emit( " <auxwhat>but before</auxwhat>\n" ); if (xe->XE.Race.h1_ct_mbsegendEC) { VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC ); } else { emit( " <auxwhat>(the end of the the thread)</auxwhat>\n" ); } } } else { /* ------ Text ------ */ emit( "Possible data race during %s of size %d " "at %#lx by thread #%d\n", what, szB, err_ga, (Int)xe->XE.Race.thr->errmsg_index ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.Race.h2_ct) { tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra emit( " This conflicts with a previous %s of size %d " "by thread #%d\n", xe->XE.Race.h2_ct_accIsW ? "write" : "read", xe->XE.Race.h2_ct_accSzB, xe->XE.Race.h2_ct->errmsg_index ); VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC ); } if (xe->XE.Race.h1_ct) { emit( " This conflicts with a previous access by thread #%d, " "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 ); } 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 ); } else { emit( " (the end of the the thread)\n" ); } } } /* If we have a better description of the address, show it. Note that in XML mode, it will already by nicely wrapped up in tags, either <auxwhat> or <xauxwhat>, so we can just emit it verbatim. */ if (xe->XE.Race.descr1) emit( "%s%s\n", xml ? " " : " ", (HChar*)VG_(indexXA)( xe->XE.Race.descr1, 0 ) ); if (xe->XE.Race.descr2) emit( "%s%s\n", xml ? " " : " ", (HChar*)VG_(indexXA)( xe->XE.Race.descr2, 0 ) ); break; /* case XE_Race */ } /* case XE_Race */ default: tl_assert(0); } /* switch (VG_(get_error_kind)(err)) */ }
void HG_(pp_Error) ( const Error* err ) { const Bool xml = VG_(clo_xml); /* a shorthand, that's all */ if (!xml) { VG_(umsg)("--------------------------------" "--------------------------------" "\n"); VG_(umsg)("\n"); } XError *xe = (XError*)VG_(get_error_extra)(err); tl_assert(xe); if (xml) emit( " <kind>%s</kind>\n", HG_(get_error_name)(err)); switch (VG_(get_error_kind)(err)) { case XE_Misc: { tl_assert( HG_(is_sane_Thread)( xe->XE.Misc.thr ) ); if (xml) { emit( " <xwhat>\n" ); emit( " <text>Thread #%d: %s</text>\n", (Int)xe->XE.Misc.thr->errmsg_index, xe->XE.Misc.errstr ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.Misc.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.Misc.auxstr) { emit(" <auxwhat>%s</auxwhat>\n", xe->XE.Misc.auxstr); if (xe->XE.Misc.auxctx) VG_(pp_ExeContext)( xe->XE.Misc.auxctx ); } } else { emit( "Thread #%d: %s\n", (Int)xe->XE.Misc.thr->errmsg_index, xe->XE.Misc.errstr ); VG_(pp_ExeContext)( 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 ); } } break; } case XE_LockOrder: { tl_assert( HG_(is_sane_Thread)( xe->XE.LockOrder.thr ) ); if (xml) { emit( " <xwhat>\n" ); emit( " <text>Thread #%d: lock order \"%p before %p\" " "violated</text>\n", (Int)xe->XE.LockOrder.thr->errmsg_index, (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr, (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.LockOrder.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.LockOrder.shouldbe_earlier_ec && xe->XE.LockOrder.shouldbe_later_ec) { emit( " <auxwhat>Required order was established by " "acquisition of lock at %p</auxwhat>\n", (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr ); VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_earlier_ec ); emit( " <auxwhat>followed by a later acquisition " "of lock at %p</auxwhat>\n", (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr ); VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_later_ec ); } announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk ); announce_LockP ( xe->XE.LockOrder.shouldbe_later_lk ); } else { emit( "Thread #%d: lock order \"%p before %p\" violated\n", (Int)xe->XE.LockOrder.thr->errmsg_index, (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr, (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr ); emit( "\n" ); emit( "Observed (incorrect) order is: " "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); } else { emit(" (stack unavailable)\n"); } emit( "\n" ); emit(" followed by a later acquisition of lock at %p\n", (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.LockOrder.shouldbe_earlier_ec && xe->XE.LockOrder.shouldbe_later_ec) { emit("\n"); emit( "Required order was established by " "acquisition of lock at %p\n", (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr ); VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_earlier_ec ); 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 ); } emit("\n"); announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk ); announce_LockP ( xe->XE.LockOrder.shouldbe_later_lk ); } break; } case XE_PthAPIerror: { tl_assert( HG_(is_sane_Thread)( xe->XE.PthAPIerror.thr ) ); if (xml) { emit( " <xwhat>\n" ); emit( " <text>Thread #%d's call to %pS failed</text>\n", (Int)xe->XE.PthAPIerror.thr->errmsg_index, xe->XE.PthAPIerror.fnname ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.PthAPIerror.thr->errmsg_index ); emit( " </xwhat>\n" ); emit( " <what>with error code %ld (%s)</what>\n", xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } else { emit( "Thread #%d's call to %pS failed\n", (Int)xe->XE.PthAPIerror.thr->errmsg_index, 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) ); } break; } case XE_UnlockBogus: { tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockBogus.thr ) ); if (xml) { emit( " <xwhat>\n" ); emit( " <text>Thread #%d unlocked an invalid " "lock at %p</text>\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index, (void*)xe->XE.UnlockBogus.lock_ga ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } else { emit( "Thread #%d unlocked an invalid lock at %p\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index, (void*)xe->XE.UnlockBogus.lock_ga ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); } break; } case XE_UnlockForeign: { tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockForeign.lock ) ); tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.owner ) ); tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.thr ) ); if (xml) { emit( " <xwhat>\n" ); emit( " <text>Thread #%d unlocked lock at %p " "currently held by thread #%d</text>\n", (Int)xe->XE.UnlockForeign.thr->errmsg_index, (void*)xe->XE.UnlockForeign.lock->guestaddr, (Int)xe->XE.UnlockForeign.owner->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockForeign.thr->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockForeign.owner->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); announce_LockP ( xe->XE.UnlockForeign.lock ); } else { emit( "Thread #%d unlocked lock at %p " "currently held by thread #%d\n", (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) ); announce_LockP ( xe->XE.UnlockForeign.lock ); } break; } case XE_UnlockUnlocked: { tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockUnlocked.lock ) ); tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockUnlocked.thr ) ); if (xml) { emit( " <xwhat>\n" ); emit( " <text>Thread #%d unlocked a " "not-locked lock at %p</text>\n", (Int)xe->XE.UnlockUnlocked.thr->errmsg_index, (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.UnlockUnlocked.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); announce_LockP ( xe->XE.UnlockUnlocked.lock); } else { 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) ); announce_LockP ( xe->XE.UnlockUnlocked.lock); } break; } case XE_Race: { Addr err_ga; const HChar* what; Int szB; what = xe->XE.Race.isWrite ? "write" : "read"; szB = xe->XE.Race.szB; err_ga = VG_(get_error_address)(err); tl_assert( HG_(is_sane_Thread)( xe->XE.Race.thr )); if (xe->XE.Race.h2_ct) tl_assert( HG_(is_sane_Thread)( xe->XE.Race.h2_ct )); if (xml) { /* ------ XML ------ */ emit( " <xwhat>\n" ); emit( " <text>Possible data race during %s of size %d " "at %p by thread #%d</text>\n", what, szB, (void*)err_ga, (Int)xe->XE.Race.thr->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", (Int)xe->XE.Race.thr->errmsg_index ); emit( " </xwhat>\n" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.Race.h2_ct) { tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra emit( " <xauxwhat>\n"); emit( " <text>This conflicts with a previous %s of size %d " "by thread #%d</text>\n", xe->XE.Race.h2_ct_accIsW ? "write" : "read", xe->XE.Race.h2_ct_accSzB, xe->XE.Race.h2_ct->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", xe->XE.Race.h2_ct->errmsg_index); emit(" </xauxwhat>\n"); VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC ); } if (xe->XE.Race.h1_ct) { emit( " <xauxwhat>\n"); emit( " <text>This conflicts with a previous access " "by thread #%d, after</text>\n", xe->XE.Race.h1_ct->errmsg_index ); emit( " <hthreadid>%d</hthreadid>\n", xe->XE.Race.h1_ct->errmsg_index ); emit(" </xauxwhat>\n"); if (xe->XE.Race.h1_ct_mbsegstartEC) { VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC ); } else { emit( " <auxwhat>(the start of the thread)</auxwhat>\n" ); } emit( " <auxwhat>but before</auxwhat>\n" ); if (xe->XE.Race.h1_ct_mbsegendEC) { VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC ); } else { emit( " <auxwhat>(the end of the thread)</auxwhat>\n" ); } } } else { /* ------ Text ------ */ announce_combined_LockP_vecs( xe->XE.Race.locksHeldW, xe->XE.Race.h2_ct_locksHeldW ); emit( "Possible data race during %s of size %d " "at %p by thread #%d\n", what, szB, (void*)err_ga, (Int)xe->XE.Race.thr->errmsg_index ); tl_assert(xe->XE.Race.locksHeldW); show_LockP_summary_textmode( xe->XE.Race.locksHeldW, "" ); VG_(pp_ExeContext)( VG_(get_error_where)(err) ); if (xe->XE.Race.h2_ct) { tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra tl_assert(xe->XE.Race.h2_ct_locksHeldW); emit( "\n" ); emit( "This conflicts with a previous %s of size %d " "by thread #%d\n", xe->XE.Race.h2_ct_accIsW ? "write" : "read", 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 ); } if (xe->XE.Race.h1_ct) { emit( " This conflicts with a previous access by thread #%d, " "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 ); } 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 ); } else { emit( " (the end of the thread)\n" ); } } } VG_(pp_addrinfo) (err_ga, &xe->XE.Race.data_addrinfo); break; /* case XE_Race */ } /* case XE_Race */ default: tl_assert(0); } /* switch (VG_(get_error_kind)(err)) */ }
WordSet HG_(singletonWS) ( WordSetU* wsu, Word w ) { return HG_(doubletonWS)( wsu, w, w ); }
/* Updates the copy with address info if necessary. */ UInt HG_(update_extra) ( Error* err ) { XError* xe = (XError*)VG_(get_error_extra)(err); tl_assert(xe); //if (extra != NULL && Undescribed == extra->addrinfo.akind) { // describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) ); //} if (xe->tag == XE_Race) { /* See if we can come up with a source level description of the raced-upon address. This is potentially expensive, which is why it's only done at the update_extra point, not when the error is initially created. */ static Int xxx = 0; xxx++; if (0) VG_(printf)("HG_(update_extra): " "%d conflicting-event queries\n", xxx); tl_assert(!xe->XE.Race.descr1); tl_assert(!xe->XE.Race.descr2); xe->XE.Race.descr1 = VG_(newXA)( HG_(zalloc), "hg.update_extra.Race.descr1", HG_(free), sizeof(HChar) ); xe->XE.Race.descr2 = VG_(newXA)( HG_(zalloc), "hg.update_extra.Race.descr2", HG_(free), sizeof(HChar) ); (void) VG_(get_data_description)( xe->XE.Race.descr1, xe->XE.Race.descr2, xe->XE.Race.data_addr ); /* If there's nothing in descr1/2, free it. Why is it safe to to VG_(indexXA) at zero here? Because VG_(get_data_description) guarantees to zero terminate descr1/2 regardless of the outcome of the call. So there's always at least one element in each XA after the call. */ if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Race.descr1, 0 ))) { VG_(deleteXA)( xe->XE.Race.descr1 ); xe->XE.Race.descr1 = NULL; } if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Race.descr2, 0 ))) { VG_(deleteXA)( xe->XE.Race.descr2 ); xe->XE.Race.descr2 = NULL; } /* And poke around in the conflicting-event map, to see if we can rustle up a plausible-looking conflicting memory access to show. */ if (HG_(clo_history_level) >= 2) { Thr* thrp = NULL; ExeContext* wherep = NULL; Addr acc_addr = xe->XE.Race.data_addr; Int acc_szB = xe->XE.Race.szB; Thr* acc_thr = xe->XE.Race.thr->hbthr; Bool acc_isW = xe->XE.Race.isWrite; SizeT conf_szB = 0; Bool conf_isW = False; tl_assert(!xe->XE.Race.h2_ct_accEC); tl_assert(!xe->XE.Race.h2_ct); if (libhb_event_map_lookup( &wherep, &thrp, &conf_szB, &conf_isW, acc_thr, acc_addr, acc_szB, acc_isW )) { Thread* threadp; tl_assert(wherep); tl_assert(thrp); threadp = libhb_get_Thr_opaque( thrp ); tl_assert(threadp); xe->XE.Race.h2_ct_accEC = wherep; xe->XE.Race.h2_ct = threadp; xe->XE.Race.h2_ct_accSzB = (Int)conf_szB; xe->XE.Race.h2_ct_accIsW = conf_isW; } } // both NULL or both non-NULL tl_assert( (!!xe->XE.Race.h2_ct) == (!!xe->XE.Race.h2_ct_accEC) ); } return sizeof(XError); }