mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
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
This commit is contained in:
parent
6ca61278bf
commit
5d35d711ba
@ -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
|
||||
|
||||
61
corecheck/tests/stack_changes.c
Normal file
61
corecheck/tests/stack_changes.c
Normal file
@ -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;
|
||||
}
|
||||
0
corecheck/tests/stack_changes.stderr.exp
Normal file
0
corecheck/tests/stack_changes.stderr.exp
Normal file
3
corecheck/tests/stack_changes.stdout.exp
Normal file
3
corecheck/tests/stack_changes.stdout.exp
Normal file
@ -0,0 +1,3 @@
|
||||
hello, world: 0
|
||||
hello, world: 1
|
||||
hello, world: 2
|
||||
2
corecheck/tests/stack_changes.vgtest
Normal file
2
corecheck/tests/stack_changes.vgtest
Normal file
@ -0,0 +1,2 @@
|
||||
prog: stack_changes
|
||||
vgopts: -q
|
||||
@ -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
|
||||
|
||||
@ -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!
|
||||
//--------------------------------------------------------------
|
||||
|
||||
@ -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];
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 );
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user