From: Robert Walsh Date: Sat, 4 Jun 2005 20:42:33 +0000 (+0000) Subject: Implement stack registration client requests. See the documentation X-Git-Tag: svn/VALGRIND_3_0_0~472 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5d35d711ba36ea9b5b9ede0100266b3aa55cf67e;p=thirdparty%2Fvalgrind.git 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 --- diff --git a/corecheck/tests/Makefile.am b/corecheck/tests/Makefile.am index ccf9629041..d877249c66 100644 --- a/corecheck/tests/Makefile.am +++ b/corecheck/tests/Makefile.am @@ -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 index 0000000000..d18353e966 --- /dev/null +++ b/corecheck/tests/stack_changes.c @@ -0,0 +1,61 @@ +#include +#include +#include + +#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 0000000000..e69de29bb2 diff --git a/corecheck/tests/stack_changes.stdout.exp b/corecheck/tests/stack_changes.stdout.exp new file mode 100644 index 0000000000..c3f04c799f --- /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 0000000000..fa3c737066 --- /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 a33032e30b..4eb20e242b 100644 --- a/coregrind/m_aspacemgr/aspacemgr.c +++ b/coregrind/m_aspacemgr/aspacemgr.c @@ -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 diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 7c1d0c7ece..86d250fdf3 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -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! //-------------------------------------------------------------- diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 43a537818a..9be79d5aa2 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -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]; diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c index cfe0f23c97..e126ec920d 100644 --- a/coregrind/m_signals.c +++ b/coregrind/m_signals.c @@ -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); diff --git a/coregrind/pub_core_aspacemgr.h b/coregrind/pub_core_aspacemgr.h index a7bb27df35..15a5400dc7 100644 --- a/coregrind/pub_core_aspacemgr.h +++ b/coregrind/pub_core_aspacemgr.h @@ -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 ); diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 894c6d9e2b..d2be7cbd40 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -1378,6 +1378,41 @@ tool-specific macros). + + VALGRIND_STACK_REGISTER(start, end): + + 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 + VALGRIND_STACK_* calls. + 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. + + + + + VALGRIND_STACK_DEREGISTER(id): + + Deregister a previously registered stack. Informs + Valgrind that previously registered memory range with stack id + id is no longer a stack. + + + + + VALGRIND_STACK_CHANGE(id, start, end): + + Change a previously registered stack. Informs + Valgrind that the previously registerer stack with stack id + id has changed it's start and end + values. Use this if your user-level thread package implements + stack growth. + + + Note that valgrind.h is included by diff --git a/include/valgrind.h b/include/valgrind.h index 750a9d9457..9e29bf242b 100644 --- a/include/valgrind.h +++ b/include/valgrind.h @@ -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 */