]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Implement stack registration client requests. See the documentation
authorRobert Walsh <rjwalsh@valgrind.org>
Sat, 4 Jun 2005 20:42:33 +0000 (20:42 +0000)
committerRobert Walsh <rjwalsh@valgrind.org>
Sat, 4 Jun 2005 20:42:33 +0000 (20:42 +0000)
in the user manual for usage information.  The stack_changes.c file in
corecheck/tests contains a short example.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3846

12 files changed:
corecheck/tests/Makefile.am
corecheck/tests/stack_changes.c [new file with mode: 0644]
corecheck/tests/stack_changes.stderr.exp [new file with mode: 0644]
corecheck/tests/stack_changes.stdout.exp [new file with mode: 0644]
corecheck/tests/stack_changes.vgtest [new file with mode: 0644]
coregrind/m_aspacemgr/aspacemgr.c
coregrind/m_main.c
coregrind/m_scheduler/scheduler.c
coregrind/m_signals.c
coregrind/pub_core_aspacemgr.h
docs/xml/manual-core.xml
include/valgrind.h

index ccf9629041c40a99a1b972603becd758df5181b2..d877249c66e10c49045798063462efdaf1131e7a 100644 (file)
@@ -36,6 +36,7 @@ EXTRA_DIST = $(noinst_SCRIPTS) \
        pth_once.stderr.exp pth_once.stdout.exp pth_once.vgtest \
        pth_rwlock.stderr.exp pth_rwlock.vgtest \
        sigkill.stderr.exp sigkill.stderr.exp2 sigkill.vgtest \
+       stack_changes.vgtest \
        res_search.stderr.exp res_search.stdout.exp res_search.vgtest \
        threadederrno.stderr.exp threadederrno.stdout.exp \
        threadederrno.vgtest \
@@ -48,7 +49,7 @@ check_PROGRAMS = \
        pth_atfork1 pth_cancel1 pth_cancel2 pth_cvsimple pth_empty \
        pth_exit pth_exit2 pth_mutexspeed pth_once pth_rwlock \
        as_mmap as_shm threadederrno \
-       vgprintf
+       stack_changes vgprintf
 
 AM_CFLAGS   = $(WERROR) -Winline -Wall -Wshadow -g -O0
 AM_CPPFLAGS = -I$(top_builddir)/include
@@ -97,3 +98,6 @@ res_search_SOURCES      = res_search.c
 res_search_LDADD        = -lresolv -lpthread
 threadederrno_SOURCES  = threadederrno.c
 threadederrno_LDADD    = -lpthread
+
+# Stack tests
+stack_changes_SOURCES   = stack_changes.c
diff --git a/corecheck/tests/stack_changes.c b/corecheck/tests/stack_changes.c
new file mode 100644 (file)
index 0000000..d18353e
--- /dev/null
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ucontext.h>
+
+#include "valgrind.h"
+
+#define STACK_SIZE 4096
+
+struct ucontext ctx1, ctx2, oldc;
+int count;
+
+void hello(struct ucontext *newc)
+{
+    printf("hello, world: %d\n", count);
+    if (count++ == 2)
+        newc = &oldc;
+    setcontext(newc);
+}
+
+int init_context(struct ucontext *uc)
+{
+    void *stack;
+    int ret;
+
+    if (getcontext(uc) == -1) {
+        perror("getcontext");
+        exit(1);
+    }
+
+    if ((stack = malloc(STACK_SIZE)) == NULL) {
+        perror("malloc");
+        exit(1);
+    }
+
+    ret = VALGRIND_STACK_REGISTER(stack, stack + STACK_SIZE);
+
+    uc->uc_link = NULL;
+    uc->uc_stack.ss_sp = stack;
+    uc->uc_stack.ss_size = STACK_SIZE;
+    uc->uc_stack.ss_flags = 0;
+
+    return ret;
+}
+
+int main(int argc, char **argv)
+{
+    int c1 = init_context(&ctx1);
+    int c2 = init_context(&ctx2);
+
+    makecontext(&ctx1, (void (*)()) hello, 2, &ctx2);
+    makecontext(&ctx2, (void (*)()) hello, 2, &ctx1);
+
+    swapcontext(&oldc, &ctx1);
+
+    VALGRIND_STACK_DEREGISTER(c1);
+    free(ctx1.uc_stack.ss_sp);
+    VALGRIND_STACK_DEREGISTER(c2);
+    free(ctx2.uc_stack.ss_sp);
+
+    return 0;
+}
diff --git a/corecheck/tests/stack_changes.stderr.exp b/corecheck/tests/stack_changes.stderr.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/corecheck/tests/stack_changes.stdout.exp b/corecheck/tests/stack_changes.stdout.exp
new file mode 100644 (file)
index 0000000..c3f04c7
--- /dev/null
@@ -0,0 +1,3 @@
+hello, world: 0
+hello, world: 1
+hello, world: 2
diff --git a/corecheck/tests/stack_changes.vgtest b/corecheck/tests/stack_changes.vgtest
new file mode 100644 (file)
index 0000000..fa3c737
--- /dev/null
@@ -0,0 +1,2 @@
+prog: stack_changes
+vgopts: -q
index a33032e30b05b9591ca9d9fd893a03fde430a6b3..4eb20e242b8f538950d996bb5c78699624214a93 100644 (file)
@@ -59,6 +59,7 @@ Addr VG_(client_mapbase);
 Addr VG_(client_trampoline_code);
 Addr VG_(clstk_base);
 Addr VG_(clstk_end);
+UWord VG_(clstk_id);
 
 Addr VG_(brk_base);             /* start of brk */
 Addr VG_(brk_limit);            /* current brk */
@@ -1103,6 +1104,175 @@ Segment *VG_(find_segment_above_mapped)(Addr a)
    bias; that is handled by new_mem_stack/die_mem_stack.
 */
 
+/*
+ * This structure holds information about the start and end addresses of
+ * registered stacks.  There's always at least one stack registered:
+ * the main process stack.  It will be the first stack registered and
+ * so will have a stack id of 0.  The user does not need to register
+ * this stack: Valgrind does it automatically right before it starts
+ * running the client.  No other stacks are automatically registered by
+ * Valgrind, however.
+ */
+
+typedef struct _Stack {
+   UWord id;
+   Addr start;
+   Addr end;
+   struct _Stack *next;
+} Stack;
+
+static Stack *stacks;
+static UWord next_id;  /* Next id we hand out to a newly registered stack */
+
+/*
+ * These are the id, start and end values of the current stack.  If the
+ * stack pointer falls outside the range of the current stack, we search
+ * the stacks list above for a matching stack.
+ */
+
+static Addr current_stack_start;
+static Addr current_stack_end;
+static UWord current_stack_id;
+
+/* Search for a particular stack by id number. */
+static Bool find_stack_by_id(UWord id, Addr *start, Addr *end)
+{
+   Stack *i = stacks;
+   while(i) {
+      if(i->id == id) {
+         *start = i->start;
+         *end = i->end;
+         return True;
+      }
+      i = i->next;
+   }
+   return False;
+}
+
+/* Find what stack an address falls into. */
+static Bool find_stack_by_addr(Addr sp, Addr *start, Addr *end, UWord *id)
+{
+   Stack *i = stacks;
+   while(i) {
+      if(sp >= i->start && sp <= i->end) {
+         *start = i->start;
+         *end = i->end;
+         *id = i->id;
+         return True;
+      }
+      i = i->next;
+   }
+   return False;
+}
+
+/* Change over to a new stack. */
+static Bool set_current_stack(UWord id)
+{
+   Addr start, end;
+   if (find_stack_by_id(id, &start, &end)) {
+      current_stack_id = id;
+      current_stack_start = start;
+      current_stack_end = end;
+      return True;
+   }
+   return False;
+}
+
+/*
+ * Register a new stack from start - end.  This is invoked from the
+ * VALGRIND_STACK_REGISTER client request, and is also called just before
+ * we start the client running, to register the main process stack.
+ *
+ * Note: this requires allocating a piece of memory to store the Stack
+ * structure, which places a dependency between this module and the
+ * mallocfree module.  However, there is no real chance of a circular
+ * dependency here, since the mallocfree module would never call back to
+ * this function.
+ */
+
+UWord VG_(handle_stack_register)(Addr start, Addr end)
+{
+   Stack *i;
+   if (start > end) {
+      Addr t = end;
+      end = start;
+      start = t;
+   }
+
+   i = (Stack *)VG_(arena_malloc)(VG_AR_CORE, sizeof(Stack));
+   i->start = start;
+   i->end = end;
+   i->id = next_id++;
+   i->next = stacks;
+   stacks = i;
+
+   if(i->id == 0) {
+      set_current_stack(i->id);
+   }
+
+   return i->id;
+}
+
+/*
+ * Deregister a stack.  This is invoked from the VALGRIND_STACK_DEREGISTER
+ * client request.
+ *
+ * Note: this requires freeing the piece of memory that was used to store
+ * the Stack structure, which places a dependency between this module
+ * and the mallocfree module.  However, there is no real chance of
+ * a circular dependency here, since the mallocfree module would never
+ * call back to this function.
+ */
+
+void VG_(handle_stack_deregister)(UWord id)
+{
+   Stack *i = stacks;
+   Stack *prev = NULL;
+
+   if(current_stack_id == id) {
+      return;
+   }
+
+   while(i) {
+      if (i->id == id) {
+         if(prev == NULL) {
+            stacks = i->next;
+         } else {
+            prev->next = i->next;
+         }
+         VG_(arena_free)(VG_AR_CORE, i);
+         return;
+      }
+      prev = i;
+      i = i->next;
+   }
+}
+
+/*
+ * Change a stack.  This is invoked from the VALGRIND_STACK_CHANGE client
+ * request and from the stack growth stuff the signals module when
+ * extending the main process stack.
+ */
+
+void VG_(handle_stack_change)(UWord id, Addr start, Addr end)
+{
+   Stack *i = stacks;
+
+   if (id == current_stack_id) {
+      current_stack_start = start;
+      current_stack_end = end;
+   }
+
+   while(i) {
+      if (i->id == id) {
+         i->start = start;
+         i->end = end;
+         return;
+      }
+      i = i->next;
+   }
+}
+
 /* This function gets called if new_mem_stack and/or die_mem_stack are
    tracked by the tool, and one of the specialised cases
    (eg. new_mem_stack_4) isn't used in preference.  
@@ -1113,6 +1283,19 @@ void VG_(unknown_SP_update)( Addr old_SP, Addr new_SP )
    static Int moans = 3;
    Word delta  = (Word)new_SP - (Word)old_SP;
 
+   /* Check if the stack pointer is still in the same stack as before. */
+   if (new_SP < current_stack_start || new_SP > current_stack_end) {
+      Addr start, end;
+      UWord new_id;
+      Bool found = find_stack_by_addr(new_SP, &start, &end, &new_id);
+      if (found && new_id != current_stack_id) {
+         /* The stack pointer is now in another stack.  Update the current
+            stack information and return without doing anything else. */
+         set_current_stack(new_id);
+         return;
+      }
+   }
+
    if (delta < -VG_(clo_max_stackframe) || VG_(clo_max_stackframe) < delta) {
       /* SP has changed by more than some threshold amount (by
          default, 2MB).  We take this to mean that the application is
index 7c1d0c7eced6a8d84657aa9aba73d303edb7a0cf..86d250fdf33bf0c2e44c98ef4b3262418ba088d6 100644 (file)
@@ -2785,6 +2785,11 @@ int main(int argc, char **argv, char **envp)
       VG_(clo_pointercheck) =
          VGA_(setup_pointercheck)( VG_(client_base), VG_(client_end));
 
+   //--------------------------------------------------------------
+   // register client stack
+   //--------------------------------------------------------------
+   VG_(clstk_id) = VG_(handle_stack_register)(VG_(clstk_base), VG_(clstk_end));
+
    //--------------------------------------------------------------
    // Run!
    //--------------------------------------------------------------
index 43a537818a34c72a1899110b5d7068851bbe8341..9be79d5aa27161870abc424e029642411ed11522 100644 (file)
@@ -1038,6 +1038,21 @@ void do_client_request ( ThreadId tid )
             SET_CLREQ_RETVAL( tid, count );
          break; }
 
+      case VG_USERREQ__STACK_REGISTER: {
+         UWord sid = VG_(handle_stack_register)((Addr)arg[1], (Addr)arg[2]);
+         SET_CLREQ_RETVAL( tid, sid );
+         break; }
+
+      case VG_USERREQ__STACK_DEREGISTER: {
+         VG_(handle_stack_deregister)(arg[1]);
+         SET_CLREQ_RETVAL( tid, 0 );     /* return value is meaningless */
+         break; }
+
+      case VG_USERREQ__STACK_CHANGE: {
+         VG_(handle_stack_change)(arg[1], (Addr)arg[2], (Addr)arg[3]);
+         SET_CLREQ_RETVAL( tid, 0 );     /* return value is meaningless */
+         break; }
+
       case VG_USERREQ__GET_MALLOCFUNCS: {
         struct vg_mallocfunc_info *info = (struct vg_mallocfunc_info *)arg[1];
 
index cfe0f23c97f1dafe8a1401a184586a659c0bd2c0..e126ec920d8be422ff5f899bbe06e734122a6f95 100644 (file)
@@ -1675,6 +1675,10 @@ Bool VG_(extend_stack)(Addr addr, UInt maxsize)
                 -1, 0) == (void *)-1)
       return False;
 
+   /* When we change the main stack, we have to let the stack handling
+      code know about it. */
+   VG_(handle_stack_change)(VG_(clstk_id), base, VG_(clstk_end));
+
    if (0)
       VG_(printf)("extended stack: %p %d\n",
                  base, newsize);
index a7bb27df35616062f0f81804830bb4cea147a896..15a5400dc7ae3a11036d57d505b17e0e1619aed3 100644 (file)
@@ -49,6 +49,7 @@ extern Addr VG_(client_end);
 extern Addr VG_(client_mapbase); // base of mappings
 extern Addr VG_(clstk_base);    // client stack range
 extern Addr VG_(clstk_end);
+extern UWord VG_(clstk_id);      // client stack id
 extern Addr VG_(client_trampoline_code);
 
 extern Addr VG_(brk_base);      // start of brk
@@ -131,6 +132,10 @@ extern Segment *VG_(split_segment)(Addr a);
 extern void VG_(pad_address_space)  (Addr start);
 extern void VG_(unpad_address_space)(Addr start);
 
+extern UWord VG_(handle_stack_register)(Addr start, Addr end);
+extern void VG_(handle_stack_deregister)(UWord id);
+extern void VG_(handle_stack_change)(UWord id, Addr start, Addr end);
+
 extern VGA_REGPARM(2)
        void VG_(unknown_SP_update) ( Addr old_SP, Addr new_SP );
 
index 894c6d9e2b78b06475b6642edbec230456617e32..d2be7cbd402909e32d377aec80462c9bce22d550 100644 (file)
@@ -1378,6 +1378,41 @@ tool-specific macros).</para>
    </listitem>
   </varlistentry>
 
+  <varlistentry>
+   <term><computeroutput>VALGRIND_STACK_REGISTER(start, end)</computeroutput>:</term>
+   <listitem>
+    <para>Register a new stack.  Informs Valgrind that the memory range
+    between start and end is a unique stack.  Returns a stack identifier
+    that can be used with other
+    <computeroutput>VALGRIND_STACK_*</computeroutput> calls.</para>
+    <para>Valgrind will use this information to determine if a change to
+    the stack pointer is an item pushed onto the stack or a change over
+    to a new stack.  Use this if you're using a user-level thread package
+    and are noticing spurious errors from Valgrind about uninitialized
+    memory reads.</para>
+   </listitem>
+  </varlistentry>
+
+  <varlistentry>
+   <term><computeroutput>VALGRIND_STACK_DEREGISTER(id)</computeroutput>:</term>
+   <listitem>
+    <para>Deregister a previously registered stack.  Informs
+    Valgrind that previously registered memory range with stack id
+    <computeroutput>id</computeroutput> is no longer a stack.</para>
+   </listitem>
+  </varlistentry>
+
+  <varlistentry>
+   <term><computeroutput>VALGRIND_STACK_CHANGE(id, start, end)</computeroutput>:</term>
+   <listitem>
+    <para>Change a previously registered stack.  Informs
+    Valgrind that the previously registerer stack with stack id
+    <computeroutput>id</computeroutput> has changed it's start and end
+    values.  Use this if your user-level thread package implements
+    stack growth.</para>
+   </listitem>
+  </varlistentry>
+
  </variablelist>
 
 <para>Note that <filename>valgrind.h</filename> is included by
index 750a9d94571931ea149f5beeeb686fd8285addf9..9e29bf242b9a04f4eb00840ba9aa22db5d82a6fd 100644 (file)
@@ -232,7 +232,12 @@ typedef
 
           /* Allow printfs to valgrind log. */
           VG_USERREQ__PRINTF = 0x1401,
-          VG_USERREQ__PRINTF_BACKTRACE = 0x1402
+          VG_USERREQ__PRINTF_BACKTRACE = 0x1402,
+
+          /* Stack support. */
+          VG_USERREQ__STACK_REGISTER   = 0x1501,
+          VG_USERREQ__STACK_DEREGISTER = 0x1502,
+          VG_USERREQ__STACK_CHANGE     = 0x1503,
    } Vg_ClientRequest;
 
 #ifndef __GNUC__
@@ -421,4 +426,30 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
                             pool, addr, 0, 0);                     \
    }
 
+/* Mark a piece of memory as being a stack. Returns a stack id. */
+#define VALGRIND_STACK_REGISTER(start, end)                        \
+   ({unsigned int _qzz_res;                                        \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__STACK_REGISTER,            \
+                            start, end, 0, 0);                     \
+    _qzz_res;                                                      \
+   })
+
+/* Unmark the piece of memory associated with a stack id as being a
+   stack. */
+#define VALGRIND_STACK_DEREGISTER(id)                              \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__STACK_DEREGISTER,          \
+                            id, 0, 0, 0);                          \
+   }
+
+/* Change the start and end address of the stack id. */
+#define VALGRIND_STACK_CHANGE(id, start, end)                      \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__STACK_CHANGE,              \
+                            id, start, end, 0);                    \
+   }
+
 #endif   /* __VALGRIND_H */