Merge patch from JeremyF:

31-hg-shadow-execontext

HELGRIND: Add option to record ExeContext for every word access. This
is probably very slow and memory hungry, but it helps make the error
reports more useful. Defaults to off.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1304 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_execontext.c b/coregrind/vg_execontext.c
index 5ce393d..605b798 100644
--- a/coregrind/vg_execontext.c
+++ b/coregrind/vg_execontext.c
@@ -278,10 +278,34 @@
 
 ExeContext* VG_(get_ExeContext) ( ThreadState *tst )
 {
-   return VG_(get_ExeContext2)( tst->m_eip, tst->m_ebp, tst->m_esp, 
-                                tst->stack_highest_word );
+   ExeContext *ec;
+
+   if (tst == NULL) {
+      /* thread currently in baseblock */
+      ThreadId tid = VG_(get_current_tid)();
+
+      ec = VG_(get_ExeContext2)( VG_(baseBlock)[VGOFF_(m_eip)], 
+				 VG_(baseBlock)[VGOFF_(m_ebp)],
+				 VG_(baseBlock)[VGOFF_(m_esp)],
+				 VG_(threads)[tid].stack_highest_word);
+   } else {
+      ec = VG_(get_ExeContext2)( tst->m_eip, tst->m_ebp, tst->m_esp, 
+				 tst->stack_highest_word );
+   }
+   return ec;
 }
 
+Addr VG_(get_EIP) ( ThreadState *tst )
+{
+   Addr ret;
+
+   if (tst == NULL)
+      ret = VG_(baseBlock)[VGOFF_(m_eip)];
+   else
+      ret = tst->m_eip;
+
+   return ret;
+}
 
 /*--------------------------------------------------------------------*/
 /*--- end                                          vg_execontext.c ---*/
diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c
index ea5efb9..a0e7283 100644
--- a/helgrind/hg_main.c
+++ b/helgrind/hg_main.c
@@ -168,6 +168,53 @@
    } while(0)
 
 
+/* Parallel map which contains execution contexts when words were last
+   accessed (if required) */
+
+static enum {
+   EC_None,
+   EC_Some,
+   EC_All
+} clo_execontext = EC_None;
+
+typedef union EC_EIP {
+   ExeContext *ec;
+   Addr eip;
+} EC_EIP;
+
+#define NULL_EC_EIP	((EC_EIP) { .ec = NULL })
+
+typedef struct {
+   EC_EIP execontext[ESEC_MAP_WORDS];
+} ExeContextMap;
+
+static ExeContextMap** execontext_map;
+
+static inline void setExeContext(Addr a, EC_EIP ec)
+{
+   UInt idx = (a >> 16) & 0xffff;
+   UInt off = (a >>  2) & 0x3fff;
+
+   if (execontext_map[idx] == NULL) {
+      execontext_map[idx] = VG_(malloc)(sizeof(ExeContextMap));
+      VG_(memset)(execontext_map[idx], 0, sizeof(ExeContextMap));
+   }
+
+   execontext_map[idx]->execontext[off] = ec;
+}
+
+static inline EC_EIP getExeContext(Addr a)
+{
+   UInt idx = (a >> 16) & 0xffff;
+   UInt off = (a >>  2) & 0x3fff;
+   EC_EIP ec = { .ec = NULL };
+
+   if (execontext_map[idx] != NULL)
+      ec = execontext_map[idx]->execontext[off];
+
+   return ec;
+}
+
 /*------------------------------------------------------------*/
 /*--- Low-level support for memory tracking.               ---*/
 /*------------------------------------------------------------*/
@@ -267,6 +314,8 @@
 static __inline__ 
 void init_virgin_sword(Addr a)
 {
+   if (clo_execontext != EC_None)
+      setExeContext(a, NULL_EC_EIP);
    set_sword(a, virgin_sword);
 }
 
@@ -287,6 +336,8 @@
    sk_assert(IS_ALIGNED4_ADDR(a));
 
    for ( ; a < a_past_end; a += 4) {
+      if (clo_execontext != EC_None)
+	 setExeContext(a, NULL_EC_EIP);
       set_sword(a, virgin_sword);
    }
 }
@@ -1419,7 +1470,7 @@
 static void make_readable ( Addr a, UInt len )
 {
    //PROF_EVENT(37);  PPP
-   set_address_range_state( a, len, Vge_NonVirginInit );
+   set_address_range_state( a, len, Vge_VirginInit );
 }
 
 
@@ -1729,7 +1780,7 @@
       shadow_word prevstate;
       /* MutexErr, LockGraphErr */
       Mutex      *mutex;
-      ExeContext *lasttouched;
+      EC_EIP      lasttouched;
       ThreadId    lasttid;
       /* LockGraphErr */
       const LockSet    *held_lockset;
@@ -1757,7 +1808,7 @@
    err_extra->axskind    = ReadAxs;
    err_extra->size       = 0;
    err_extra->mutex      = NULL;
-   err_extra->lasttouched= NULL;
+   err_extra->lasttouched= NULL_EC_EIP;
    err_extra->lasttid    = VG_INVALID_THREADID;
    err_extra->prev_lockset = 0;
    err_extra->held_lockset = 0;
@@ -1882,7 +1933,8 @@
    err_extra.isWrite = is_write;
    err_extra.addrinfo.akind = Undescribed;
    err_extra.prevstate = prevstate;
-
+   if (clo_execontext)
+      err_extra.lasttouched = getExeContext(a);
    VG_(maybe_record_error)( tst, EraserErr, a, 
                             (is_write ? "writing" : "reading"),
                             &err_extra);
@@ -1898,7 +1950,7 @@
    clear_HelgrindError(&err_extra);
    err_extra.addrinfo.akind = Undescribed;
    err_extra.mutex = mutex;
-   err_extra.lasttouched = ec;
+   err_extra.lasttouched = (EC_EIP){ .ec = ec };
    err_extra.lasttid = tid;
 
    VG_(maybe_record_error)(VG_(get_ThreadState)(tid), MutexErr, 
@@ -1917,7 +1969,7 @@
    err_extra.addrinfo.akind = Undescribed;
    err_extra.mutex = mutex;
    
-   err_extra.lasttouched = mutex->location;
+   err_extra.lasttouched = (EC_EIP) { .ec = mutex->location };
    err_extra.held_lockset = lockset_holding;
    err_extra.prev_lockset = lockset_prev;
    
@@ -1983,7 +2035,7 @@
             relative = "inside";
          }
 	 VG_(message)(Vg_UserMsg, 
-		      "  Address %p is %d bytes %s a block of size %d %s by thread %d at",
+		      "  Address %p is %d bytes %s a block of size %d %s by thread %d",
 		      a, delta, relative, 
 		      ai->blksize,
 		      ai->akind == Mallocd ? "alloc'd" : "freed",
@@ -2065,19 +2117,41 @@
 	 break;
       }
 
-      if (*msg) {
+      if (*msg)
 	 VG_(message)(Vg_UserMsg, "  Previous state: %s", msg);
-      }
+
       pp_AddrInfo(err->addr, &extra->addrinfo);
+
+      if (clo_execontext == EC_Some && extra->lasttouched.eip != 0) {
+	 Char file[100];
+	 UInt line;
+	 Addr eip = extra->lasttouched.eip;
+	 
+	 VG_(message)(Vg_UserMsg, "  Word at %p last accessed", err->addr);
+	 
+	 if (VG_(get_filename_linenum)(eip, file, sizeof(file), &line)) {
+	    VG_(message)(Vg_UserMsg, "   at %p: %y (%s:%u)",
+			 eip, eip, file, line);
+	 } else if (VG_(get_objname)(eip, file, sizeof(file))) {
+	    VG_(message)(Vg_UserMsg, "   at %p: %y (in %s)",
+			 eip, eip, file);
+	 } else {
+	    VG_(message)(Vg_UserMsg, "   at %p: %y", eip, eip);
+	 }
+      } else if (clo_execontext == EC_All && extra->lasttouched.ec != NULL) {
+	 VG_(message)(Vg_UserMsg, "  Word at %p last accessed", err->addr);
+	 VG_(pp_ExeContext)(extra->lasttouched.ec);
+      }
+
       break;
 
    case MutexErr:
-      VG_(message)(Vg_UserMsg, "Mutex problem at %p%(y trying to %s at",
+      VG_(message)(Vg_UserMsg, "Mutex problem at %p%(y trying to %s",
 		   err->addr, err->addr, err->string );
       pp_ExeContext();
-      if (extra->lasttouched) {
-	 VG_(message)(Vg_UserMsg, "  last touched by thread %d at", extra->lasttid);
-	 VG_(pp_ExeContext)(extra->lasttouched);
+      if (extra->lasttouched.ec != NULL) {
+	 VG_(message)(Vg_UserMsg, "  last touched by thread %d", extra->lasttid);
+	 VG_(pp_ExeContext)(extra->lasttouched.ec);
       }
       pp_AddrInfo(err->addr, &extra->addrinfo);
       break;
@@ -2276,7 +2350,10 @@
    shadow_word  prevstate;
    const LockSet *ls;
 
-   tid = (tst == NULL) ? VG_(get_current_tid)() : VG_(get_tid_from_ThreadState)(tst);
+   if (tst == NULL)
+      tid = VG_(get_current_tid)();
+   else
+      tid = VG_(get_tid_from_ThreadState)(tst);
 
    for ( ; a < end; a += 4) {
 
@@ -2340,6 +2417,16 @@
       default:
          VG_(skin_panic)("Unknown eraser state");
       }
+
+      if (clo_execontext != EC_None) {
+	 EC_EIP eceip;
+
+	 if (clo_execontext == EC_Some)
+	    eceip.eip = VG_(get_EIP)(tst);
+	 else
+	    eceip.ec = VG_(get_ExeContext)(tst);
+	 setExeContext(a, eceip);
+      }
    }
 }
 
@@ -2350,8 +2437,11 @@
    Addr     end = a + 4*compute_num_words_accessed(a, size);
    shadow_word  prevstate;
 
-   tid = (tst == NULL) ? VG_(get_current_tid)() : VG_(get_tid_from_ThreadState)(tst);
-
+   if (tst == NULL)
+      tid = VG_(get_current_tid)();
+   else
+      tid = VG_(get_tid_from_ThreadState)(tst);
+   
    for ( ; a < end; a += 4) {
 
       sword = get_sword_addr(a);
@@ -2408,6 +2498,16 @@
       default:
          VG_(skin_panic)("Unknown eraser state");
       }
+
+      if (clo_execontext != EC_None) {
+	 EC_EIP eceip;
+
+	 if (clo_execontext == EC_Some)
+	    eceip.eip = VG_(get_EIP)(tst);
+	 else
+	    eceip.ec = VG_(get_ExeContext)(tst);
+	 setExeContext(a, eceip);
+      }
    }
 }
 
@@ -2558,7 +2658,6 @@
    init_shadow_memory();
 }
 
-#if 0
 static Bool match_Bool(Char *arg, Char *argstr, Bool *ret)
 {
    Int len = VG_(strlen)(argstr);
@@ -2598,22 +2697,46 @@
 
    return False;
 }
-#endif
 
 Bool SK_(process_cmd_line_option)(Char* arg)
 {
+   Char *str;
+
+   if (match_str(arg, "--show-last-access=", &str)) {
+      Bool ok = True;
+      if (VG_(strcmp)(str, "no") == 0)
+	 clo_execontext = EC_None;
+      else if (VG_(strcmp)(str, "some") == 0)
+	 clo_execontext = EC_Some;
+      else if (VG_(strcmp)(str, "all") == 0)
+	 clo_execontext = EC_All;
+      else {
+	 ok = False;
+	 VG_(bad_option)(arg);
+      }
+
+      VG_(free)(str);
+      if (ok)
+	 return True;
+   }
+
    return False;
 }
 
 Char *SK_(usage)(void)
 {
    return ""
+"    --show-last-access=no|some|all  show location of last word access on error [no]\n"
       ;
 }
 
 
 void SK_(post_clo_init)(void)
 {
+   if (clo_execontext) {
+      execontext_map = VG_(malloc)(sizeof(ExeContextMap *) * 65536);
+      VG_(memset)(execontext_map, 0, sizeof(ExeContextMap *) * 65536);
+   }
 }
 
 
diff --git a/include/vg_skin.h b/include/vg_skin.h
index 755a371..87fb1e3 100644
--- a/include/vg_skin.h
+++ b/include/vg_skin.h
@@ -974,6 +974,9 @@
    new one.  Either way, return a pointer to the context. */
 extern ExeContext* VG_(get_ExeContext) ( ThreadState *tst );
 
+/* Just grab the client's EIP, as a much smaller and cheaper
+   indication of where they are. */
+extern Addr VG_(get_EIP)( ThreadState *tst );
 
 /*====================================================================*/
 /*=== Error reporting                                              ===*/