mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 02:18:37 +00:00
At various places, there were either some assumption that the 'end'
boundary (highest address) was either not included, included,
or was the highest addressable word, or the highest addressable byte.
This e.g. was very visible when doing:
./vg-in-place -d -d ./helgrind/tests/tc01_simple_race|&grep regi
giving
--24040:2:stacks register 0xBEDB4000-0xBEDB4FFF as stack 0
--24040:2:stacks register 0x402C000-0x4A2C000 as stack 1
showing that the main stack end was (on x86) not the highest word
but the highest byte, while for the thread 1, the registered end
was a byte not part of the stack.
The attached patch ensures that stack bounds semantic are documented and
consistent. Also, some of the stack handling code is factorised.
The convention that the patch ensures and documents is:
start is the lowest addressable byte, end is the highest addressable byte.
(the words 'min' and 'max' have been kept when already used, as this wording is
consistent with the new semantic of start/end).
In various debug log, used brackets [ and ] to make clear that
both bounds are included.
The code to guess and register the client stack was duplicated
in all the platform specific syswrap-<plat>-<os>.c files.
Code has been factorised in syswrap-generic.c
The patch has been regression tested on
x86, amd64, ppc32/64, s390x.
It has been compiled and one test run on arm64.
Not compiled/not tested on darwin, android, mips32/64, arm
More in details, the patch does the following:
coregrind/pub_core_aspacemgr.h
include/valgrind.h
include/pub_tool_machine.h
coregrind/pub_core_scheduler.h
coregrind/pub_core_stacks.h
- document start/end semantic in various functions
also in pub_tool_machine.h:
- replaces unclear 'bottommost address' by 'lowest address'
(unclear as stack bottom is or at least can be interpreted as
the 'functional' bottom of the stack, which is the highest
address for 'stack growing downwards').
coregrind/pub_core_initimg.h
replace unclear clstack_top by clstack_end
coregrind/m_main.c
updated to clstack_end
coregrind/pub_core_threadstate.h
renamed client_stack_highest_word to client_stack_highest_byte
coregrind/m_scheduler/scheduler.c
computes client_stack_highest_byte as the highest addressable byte
Update comments in call to VG_(show_sched_status)
coregrind/m_machine.c
coregrind/m_stacktrace.c
updated to client_stack_highest_byte, and switched
stack_lowest/highest_word to stack_lowest/highest_byte accordingly
coregrind/m_stacks.c
clarify semantic of start/end,
added a comment to indicate why we invert start/end in register call
(note that the code find_stack_by_addr was already assuming that
end was included as the checks were doing e.g.
sp >= i->start && sp <= i->end
coregrind/pub_core_clientstate.h
coregrind/m_clientstate.c
renames Addr VG_(clstk_base) to Addr VG_(clstk_start_base)
(start to indicate it is the lowest address, base suffix kept
to indicate it is the initial lowest address).
coregrind/m_initimg/initimg-darwin.c
updated to VG_(clstk_start_base)
replace unclear iicii.clstack_top by iicii.clstack_end
updated clstack_max_size computation according to both bounds included.
coregrind/m_initimg/initimg-linux.c
updated to VG_(clstk_start_base)
updated VG_(clstk_end) computation according to both bounds included.
replace unclear iicii.clstack_top by iicii.clstack_end
coregrind/pub_core_aspacemgr.h
extern Addr VG_(am_startup) : clarify semantic of the returned value
coregrind/m_aspacemgr/aspacemgr-linux.c
removed a copy of a comment that was already in pub_core_aspacemgr.h
(avoid double maintenance)
renamed unclear suggested_clstack_top to suggested_clstack_end
(note that here, it looks like suggested_clstack_top was already
the last addressable byte)
* factorisation of the stack guessing and registration causes
mechanical changes in the following files:
coregrind/m_syswrap/syswrap-ppc64-linux.c
coregrind/m_syswrap/syswrap-x86-darwin.c
coregrind/m_syswrap/syswrap-amd64-linux.c
coregrind/m_syswrap/syswrap-arm-linux.c
coregrind/m_syswrap/syswrap-generic.c
coregrind/m_syswrap/syswrap-mips64-linux.c
coregrind/m_syswrap/syswrap-ppc32-linux.c
coregrind/m_syswrap/syswrap-amd64-darwin.c
coregrind/m_syswrap/syswrap-mips32-linux.c
coregrind/m_syswrap/priv_syswrap-generic.h
coregrind/m_syswrap/syswrap-x86-linux.c
coregrind/m_syswrap/syswrap-s390x-linux.c
coregrind/m_syswrap/syswrap-darwin.c
coregrind/m_syswrap/syswrap-arm64-linux.c
Some files to look at more in details:
syswrap-darwin.c : the handling of sysctl(kern.usrstack) looked
buggy to me, and has probably be made correct by the fact that
VG_(clstk_end) is now the last addressable byte. However,unsure
about this, as I could not find any documentation about
sysctl(kern.usrstack). I only find several occurences on the web,
showing that the result of this is page aligned, which I guess
means it must be 1+ the last addressable byte.
syswrap-x86-darwin.c and syswrap-amd64-darwin.c
I suspect the code that was computing client_stack_highest_word
was wrong, and the patch makes it correct.
syswrap-mips64-linux.c
not sure what to do for this code. This is the only code
that was guessing the stack differently from others.
Kept (almost) untouched. To be discussed with mips maintainers.
coregrind/pub_core_libcassert.h
coregrind/m_libcassert.c
* void VG_(show_sched_status):
renamed Bool valgrind_stack_usage to Bool stack_usage
if stack_usage, shows both the valgrind stack usage and
the client stack boundaries
coregrind/m_scheduler/scheduler.c
coregrind/m_gdbserver/server.c
coregrind/m_gdbserver/remote-utils.c
Updated comments in callers to VG_(show_sched_status)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14392
428 lines
14 KiB
C
428 lines
14 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- The thread state. pub_core_threadstate.h ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2013 Julian Seward
|
|
jseward@acm.org
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#ifndef __PUB_CORE_THREADSTATE_H
|
|
#define __PUB_CORE_THREADSTATE_H
|
|
|
|
//--------------------------------------------------------------------
|
|
// PURPOSE: This module defines the ThreadState type and the
|
|
// VG_(threads)[] data structure which holds all the important thread
|
|
// state. It also defines some simple operations on the data structure
|
|
// that don't require any external help. (m_scheduler does the complex
|
|
// stuff).
|
|
//--------------------------------------------------------------------
|
|
|
|
#include "pub_tool_threadstate.h"
|
|
#include "pub_core_libcsetjmp.h" // VG_MINIMAL_JMP_BUF
|
|
#include "pub_core_vki.h" // vki_sigset_t
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Types ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/*
|
|
Thread state machine:
|
|
|
|
Empty -> Init -> Runnable <=> WaitSys/Yielding
|
|
^ |
|
|
\---- Zombie -----/
|
|
*/
|
|
typedef
|
|
enum ThreadStatus {
|
|
VgTs_Empty, /* this slot is not in use */
|
|
VgTs_Init, /* just allocated */
|
|
VgTs_Runnable, /* ready to run */
|
|
VgTs_WaitSys, /* waiting for a syscall to complete */
|
|
VgTs_Yielding, /* temporarily yielding the CPU */
|
|
VgTs_Zombie, /* transient state just before exiting */
|
|
}
|
|
ThreadStatus;
|
|
|
|
/* Return codes from the scheduler. */
|
|
typedef
|
|
enum {
|
|
VgSrc_None, /* not exiting yet */
|
|
VgSrc_ExitThread, /* just this thread is exiting */
|
|
VgSrc_ExitProcess, /* this thread is exiting due to another thread
|
|
calling exit() */
|
|
VgSrc_FatalSig /* Killed by the default action of a fatal
|
|
signal */
|
|
}
|
|
VgSchedReturnCode;
|
|
|
|
|
|
#if defined(VGA_x86)
|
|
typedef VexGuestX86State VexGuestArchState;
|
|
#elif defined(VGA_amd64)
|
|
typedef VexGuestAMD64State VexGuestArchState;
|
|
#elif defined(VGA_ppc32)
|
|
typedef VexGuestPPC32State VexGuestArchState;
|
|
#elif defined(VGA_ppc64be) || defined(VGA_ppc64le)
|
|
typedef VexGuestPPC64State VexGuestArchState;
|
|
#elif defined(VGA_arm)
|
|
typedef VexGuestARMState VexGuestArchState;
|
|
#elif defined(VGA_arm64)
|
|
typedef VexGuestARM64State VexGuestArchState;
|
|
#elif defined(VGA_s390x)
|
|
typedef VexGuestS390XState VexGuestArchState;
|
|
#elif defined(VGA_mips32)
|
|
typedef VexGuestMIPS32State VexGuestArchState;
|
|
#elif defined(VGA_mips64)
|
|
typedef VexGuestMIPS64State VexGuestArchState;
|
|
#else
|
|
# error Unknown architecture
|
|
#endif
|
|
|
|
/* Forward declarations */
|
|
struct SyscallStatus;
|
|
struct SyscallArgs;
|
|
|
|
/* Architecture-specific thread state */
|
|
typedef
|
|
struct {
|
|
/* --- BEGIN vex-mandated guest state --- */
|
|
|
|
/* Note that for code generation reasons, we require that the
|
|
guest state area, its two shadows, and the spill area, are
|
|
16-aligned and have 16-aligned sizes, and there are no holes
|
|
in between. This is checked by do_pre_run_checks() in
|
|
scheduler.c. */
|
|
|
|
/* Saved machine context. */
|
|
VexGuestArchState vex __attribute__((aligned(16)));
|
|
|
|
/* Saved shadow context (2 copies). */
|
|
VexGuestArchState vex_shadow1 __attribute__((aligned(16)));
|
|
VexGuestArchState vex_shadow2 __attribute__((aligned(16)));
|
|
|
|
/* Spill area. */
|
|
UChar vex_spill[LibVEX_N_SPILL_BYTES] __attribute__((aligned(16)));
|
|
|
|
/* --- END vex-mandated guest state --- */
|
|
}
|
|
ThreadArchState;
|
|
|
|
|
|
/* OS-specific thread state. IMPORTANT: if you add fields to this,
|
|
you _must_ add code to os_state_clear() to initialise those
|
|
fields. */
|
|
typedef
|
|
struct {
|
|
/* who we are */
|
|
Int lwpid; // PID of kernel task (Darwin: Mach thread)
|
|
Int threadgroup; // thread group id
|
|
|
|
ThreadId parent; // parent tid (if any)
|
|
|
|
/* runtime details */
|
|
Addr valgrind_stack_base; // Valgrind's stack (VgStack*)
|
|
Addr valgrind_stack_init_SP; // starting value for SP
|
|
|
|
/* exit details */
|
|
Word exitcode; // in the case of exitgroup, set by someone else
|
|
Int fatalsig; // fatal signal
|
|
|
|
# if defined(VGO_darwin)
|
|
// Mach trap POST handler as chosen by PRE
|
|
void (*post_mach_trap_fn)(ThreadId tid,
|
|
struct SyscallArgs *, struct SyscallStatus *);
|
|
|
|
// This thread's pthread
|
|
Addr pthread;
|
|
|
|
// Argument passed when thread started
|
|
Addr func_arg;
|
|
|
|
// Synchronization between child thread and parent thread's POST wrapper
|
|
semaphore_t child_go;
|
|
semaphore_t child_done;
|
|
|
|
// Workqueue re-entry
|
|
// (setjmp in PRE(workq_ops), longjmp in wqthread_hijack)
|
|
// DDD: JRS fixme: this comment is no longer correct; wq_jmpbuf is
|
|
// never used, and there is no such setjmp or longjmp pair.
|
|
// I guess we could leave wq_jmpbuf_valid in place though, since
|
|
// it does allow for an assertion in ML_(wqthread_continue_NORETURN).
|
|
Bool wq_jmpbuf_valid;
|
|
//jmp_buf wq_jmpbuf;
|
|
|
|
// Values saved from transient Mach RPC messages
|
|
Addr remote_port; // destination for original message
|
|
Int msgh_id; // outgoing message id
|
|
union {
|
|
struct {
|
|
Addr port;
|
|
} mach_port;
|
|
struct {
|
|
Int right;
|
|
} mach_port_allocate;
|
|
struct {
|
|
Addr port;
|
|
Int right;
|
|
Int delta;
|
|
} mach_port_mod_refs;
|
|
struct {
|
|
Addr task;
|
|
Addr name;
|
|
Int disposition;
|
|
} mach_port_insert_right;
|
|
struct {
|
|
Addr size;
|
|
int flags;
|
|
} vm_allocate;
|
|
struct {
|
|
Addr address;
|
|
Addr size;
|
|
} vm_deallocate;
|
|
struct {
|
|
Addr src;
|
|
Addr dst;
|
|
Addr size;
|
|
} vm_copy;
|
|
struct {
|
|
Addr address;
|
|
Addr size;
|
|
int set_maximum;
|
|
UWord new_protection;
|
|
} vm_protect;
|
|
struct {
|
|
Addr addr;
|
|
SizeT size;
|
|
} vm_read;
|
|
struct {
|
|
ULong addr;
|
|
ULong size;
|
|
} mach_vm_read;
|
|
struct {
|
|
Addr addr;
|
|
SizeT size;
|
|
Addr data;
|
|
} vm_read_overwrite;
|
|
struct {
|
|
Addr size;
|
|
int copy;
|
|
UWord protection;
|
|
} vm_map;
|
|
struct {
|
|
Addr size;
|
|
} vm_remap;
|
|
struct {
|
|
ULong size;
|
|
int flags;
|
|
} mach_vm_allocate;
|
|
struct {
|
|
ULong address;
|
|
ULong size;
|
|
} mach_vm_deallocate;
|
|
struct {
|
|
ULong address;
|
|
ULong size;
|
|
int set_maximum;
|
|
unsigned int new_protection;
|
|
} mach_vm_protect;
|
|
struct {
|
|
ULong size;
|
|
int copy;
|
|
UWord protection;
|
|
} mach_vm_map;
|
|
struct {
|
|
ULong size;
|
|
int copy;
|
|
} mach_vm_remap;
|
|
struct {
|
|
Addr thread;
|
|
UWord flavor;
|
|
} thread_get_state;
|
|
struct {
|
|
Addr address;
|
|
} io_connect_unmap_memory;
|
|
struct {
|
|
int which_port;
|
|
} task_get_special_port;
|
|
struct {
|
|
char *service_name;
|
|
} bootstrap_look_up;
|
|
struct {
|
|
vki_size_t size;
|
|
} WindowServer_29828;
|
|
struct {
|
|
Int access_rights;
|
|
} WindowServer_29831;
|
|
struct {
|
|
char *path;
|
|
} io_registry_entry_from_path;
|
|
} mach_args;
|
|
# endif
|
|
|
|
}
|
|
ThreadOSstate;
|
|
|
|
|
|
/* Overall thread state */
|
|
typedef struct {
|
|
/* ThreadId == 0 (and hence vg_threads[0]) is NEVER USED.
|
|
The thread identity is simply the index in vg_threads[].
|
|
ThreadId == 1 is the root thread and has the special property
|
|
that we don't try and allocate or deallocate its stack. For
|
|
convenience of generating error message, we also put the
|
|
ThreadId in this tid field, but be aware that it should
|
|
ALWAYS == the index in vg_threads[]. */
|
|
ThreadId tid;
|
|
|
|
/* Current scheduling status. */
|
|
ThreadStatus status;
|
|
|
|
/* This is set if the thread is in the process of exiting for any
|
|
reason. The precise details of the exit are in the OS-specific
|
|
state. */
|
|
VgSchedReturnCode exitreason;
|
|
|
|
/* Architecture-specific thread state. */
|
|
ThreadArchState arch;
|
|
|
|
/* This thread's blocked-signals mask. Semantics is that for a
|
|
signal to be delivered to this thread, the signal must not be
|
|
blocked by this signal mask. If more than one thread accepts a
|
|
signal, then it will be delivered to one at random. If all
|
|
threads block the signal, it will remain pending until either a
|
|
thread unblocks it or someone uses sigwaitsig/sigtimedwait. */
|
|
vki_sigset_t sig_mask;
|
|
|
|
/* tmp_sig_mask is usually the same as sig_mask, and is kept in
|
|
sync whenever sig_mask is changed. The only time they have
|
|
different values is during the execution of a sigsuspend, where
|
|
tmp_sig_mask is the temporary mask which sigsuspend installs.
|
|
It is only consulted to compute the signal mask applied to a
|
|
signal handler. */
|
|
vki_sigset_t tmp_sig_mask;
|
|
|
|
/* A little signal queue for signals we can't get the kernel to
|
|
queue for us. This is only allocated as needed, since it should
|
|
be rare. */
|
|
struct SigQueue *sig_queue;
|
|
|
|
/* Client stacks. When a thread slot is freed, we don't deallocate its
|
|
stack; we just leave it lying around for the next use of the
|
|
slot. If the next use of the slot requires a larger stack,
|
|
only then is the old one deallocated and a new one
|
|
allocated.
|
|
|
|
For the main thread (threadid == 1), this mechanism doesn't
|
|
apply. We don't know the size of the stack since we didn't
|
|
allocate it, and furthermore we never reallocate it. */
|
|
|
|
/* The allocated size of this thread's stack */
|
|
SizeT client_stack_szB;
|
|
|
|
/* Address of the highest legitimate byte in this stack. This is
|
|
used for error messages only -- not critical for execution
|
|
correctness. Is is set for all stacks, specifically including
|
|
ThreadId == 1 (the main thread). */
|
|
Addr client_stack_highest_byte;
|
|
|
|
/* Alternate signal stack */
|
|
vki_stack_t altstack;
|
|
|
|
/* OS-specific thread state */
|
|
ThreadOSstate os_state;
|
|
|
|
/* Error disablement level. A counter which allows selectively
|
|
disabling error reporting in threads. When zero, reporting is
|
|
enabled. When nonzero, it is disabled. This is controlled by
|
|
the client request 'VG_USERREQ__CHANGE_ERR_DISABLEMENT'. New
|
|
threads are always created with this as zero (errors
|
|
enabled). */
|
|
UInt err_disablement_level;
|
|
|
|
/* Per-thread jmp_buf to resume scheduler after a signal */
|
|
Bool sched_jmpbuf_valid;
|
|
VG_MINIMAL_JMP_BUF(sched_jmpbuf);
|
|
|
|
/* This thread's name. NULL, if no name. */
|
|
HChar *thread_name;
|
|
}
|
|
ThreadState;
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- The thread table. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* A statically allocated array of threads. NOTE: [0] is
|
|
never used, to simplify the simulation of initialisers for
|
|
LinuxThreads. */
|
|
extern ThreadState VG_(threads)[VG_N_THREADS];
|
|
|
|
// The running thread. m_scheduler should be the only other module
|
|
// to write to this.
|
|
extern ThreadId VG_(running_tid);
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Basic operations on the thread table. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Initialize the m_threadstate module. */
|
|
void VG_(init_Threads)(void);
|
|
|
|
// Convert a ThreadStatus to a string.
|
|
const HChar* VG_(name_of_ThreadStatus) ( ThreadStatus status );
|
|
|
|
// Convert a VgSchedReturnCode to a string.
|
|
const HChar* VG_(name_of_VgSchedReturnCode) ( VgSchedReturnCode retcode );
|
|
|
|
/* Get the ThreadState for a particular thread */
|
|
extern ThreadState *VG_(get_ThreadState) ( ThreadId tid );
|
|
|
|
/* Check that tid is in range and denotes a non-Empty thread. */
|
|
extern Bool VG_(is_valid_tid) ( ThreadId tid );
|
|
|
|
/* Returns true if a thread is currently running (ie, has the CPU lock) */
|
|
extern Bool VG_(is_running_thread)(ThreadId tid);
|
|
|
|
/* Returns true if the thread is in the process of exiting */
|
|
extern Bool VG_(is_exiting)(ThreadId tid);
|
|
|
|
/* Return the number of non-dead Threads */
|
|
extern Int VG_(count_living_threads)(void);
|
|
|
|
/* Return the number of threads in VgTs_Runnable state */
|
|
extern Int VG_(count_runnable_threads)(void);
|
|
|
|
/* Given an LWP id (ie, real kernel thread id), find the corresponding
|
|
ThreadId */
|
|
extern ThreadId VG_(lwpid_to_vgtid)(Int lwpid);
|
|
|
|
#endif // __PUB_CORE_THREADSTATE_H
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|