Implement stack registration client requests. See the documentation
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 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/corecheck/tests/Makefile.am b/corecheck/tests/Makefile.am
index ccf9629..d877249 100644
--- a/corecheck/tests/Makefile.am
+++ b/corecheck/tests/Makefile.am
@@ -36,6 +36,7 @@
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 @@
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_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
index 0000000..d18353e
--- /dev/null
+++ b/corecheck/tests/stack_changes.c
@@ -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
index 0000000..e69de29
--- /dev/null
+++ b/corecheck/tests/stack_changes.stderr.exp
diff --git a/corecheck/tests/stack_changes.stdout.exp b/corecheck/tests/stack_changes.stdout.exp
new file mode 100644
index 0000000..c3f04c7
--- /dev/null
+++ b/corecheck/tests/stack_changes.stdout.exp
@@ -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
index 0000000..fa3c737
--- /dev/null
+++ b/corecheck/tests/stack_changes.vgtest
@@ -0,0 +1,2 @@
+prog: stack_changes
+vgopts: -q
diff --git a/coregrind/m_aspacemgr/aspacemgr.c b/coregrind/m_aspacemgr/aspacemgr.c
index a33032e..4eb20e2 100644
--- a/coregrind/m_aspacemgr/aspacemgr.c
+++ b/coregrind/m_aspacemgr/aspacemgr.c
@@ -59,6 +59,7 @@
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 @@
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 @@
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
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 7c1d0c7..86d250f 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -2786,6 +2786,11 @@
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!
//--------------------------------------------------------------
VGP_POPCC(VgpStartup);
diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c
index 43a5378..9be79d5 100644
--- a/coregrind/m_scheduler/scheduler.c
+++ b/coregrind/m_scheduler/scheduler.c
@@ -1038,6 +1038,21 @@
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];
diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c
index cfe0f23..e126ec9 100644
--- a/coregrind/m_signals.c
+++ b/coregrind/m_signals.c
@@ -1675,6 +1675,10 @@
-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);
diff --git a/coregrind/pub_core_aspacemgr.h b/coregrind/pub_core_aspacemgr.h
index a7bb27d..15a5400 100644
--- a/coregrind/pub_core_aspacemgr.h
+++ b/coregrind/pub_core_aspacemgr.h
@@ -49,6 +49,7 @@
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 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 );
diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml
index 894c6d9..d2be7cb 100644
--- a/docs/xml/manual-core.xml
+++ b/docs/xml/manual-core.xml
@@ -1378,6 +1378,41 @@
</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
diff --git a/include/valgrind.h b/include/valgrind.h
index 750a9d9..9e29bf2 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -232,7 +232,12 @@
/* 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 @@
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 */