void cds_volatile_store ## size (void * obj, uint ## size ## _t val, const char * position) { \
ensureModel(); \
model->switch_to_master(new ModelAction(ATOMIC_WRITE, position, memory_order_relaxed, obj, (uint64_t) val)); \
- *((volatile uint ## size ## _t *)obj) = val; \
+ *((volatile uint ## size ## _t *)obj) = val; \
}
VOLATILESTORE(8)
/** Page size configuration */
#define PAGESIZE 4096
+#define TLS 1
+
/** Thread parameters */
/* Size of stack to allocate for a thread. */
SNAPSHOTALLOC
};
+
+#ifdef TLS
+#include <dlfcn.h>
+
+//Code taken from LLVM and licensed under the University of Illinois Open Source
+//License.
+static uintptr_t thread_descriptor_size;
+#if __LP64__ || defined(_WIN64)
+# define SANITIZER_WORDSIZE 64
+#else
+# define SANITIZER_WORDSIZE 32
+#endif
+
+#if SANITIZER_WORDSIZE == 64
+# define FIRST_32_SECOND_64(a, b) (b)
+#else
+# define FIRST_32_SECOND_64(a, b) (a)
+#endif
+
+#if defined(__x86_64__) && !defined(_LP64)
+# define SANITIZER_X32 1
+#else
+# define SANITIZER_X32 0
+#endif
+
+#if defined(__arm__)
+# define SANITIZER_ARM 1
+#else
+# define SANITIZER_ARM 0
+#endif
+
+uintptr_t ThreadDescriptorSize() {
+ uintptr_t val = thread_descriptor_size;
+ if (val)
+ return val;
+#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
+#ifdef _CS_GNU_LIBC_VERSION
+ char buf[64];
+ uintptr_t len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
+ if (len < sizeof(buf) && strncmp(buf, "glibc 2.", 8) == 0) {
+ char *end;
+ int minor = strtoll(buf + 8, &end, 10);
+ if (end != buf + 8 && (*end == '\0' || *end == '.' || *end == '-')) {
+ int patch = 0;
+ if (*end == '.')
+ // strtoll will return 0 if no valid conversion could be performed
+ patch = strtoll(end + 1, nullptr, 10);
+
+ /* sizeof(struct pthread) values from various glibc versions. */
+ if (SANITIZER_X32)
+ val = 1728;// Assume only one particular version for x32.
+ // For ARM sizeof(struct pthread) changed in Glibc 2.23.
+ else if (SANITIZER_ARM)
+ val = minor <= 22 ? 1120 : 1216;
+ else if (minor <= 3)
+ val = FIRST_32_SECOND_64(1104, 1696);
+ else if (minor == 4)
+ val = FIRST_32_SECOND_64(1120, 1728);
+ else if (minor == 5)
+ val = FIRST_32_SECOND_64(1136, 1728);
+ else if (minor <= 9)
+ val = FIRST_32_SECOND_64(1136, 1712);
+ else if (minor == 10)
+ val = FIRST_32_SECOND_64(1168, 1776);
+ else if (minor == 11 || (minor == 12 && patch == 1))
+ val = FIRST_32_SECOND_64(1168, 2288);
+ else if (minor <= 13)
+ val = FIRST_32_SECOND_64(1168, 2304);
+ else
+ val = FIRST_32_SECOND_64(1216, 2304);
+ }
+ }
+#endif
+#elif defined(__mips__)
+ // TODO(sagarthakur): add more values as per different glibc versions.
+ val = FIRST_32_SECOND_64(1152, 1776);
+#elif defined(__aarch64__)
+ // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
+ val = 1776;
+#elif defined(__powerpc64__)
+ val = 1776; // from glibc.ppc64le 2.20-8.fc21
+#elif defined(__s390__)
+ val = FIRST_32_SECOND_64(1152, 1776); // valid for glibc 2.22
+#endif
+ if (val)
+ thread_descriptor_size = val;
+ return val;
+}
+
+#ifdef __i386__
+# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
+#else
+# define DL_INTERNAL_FUNCTION
+#endif
+
+intptr_t RoundUpTo(uintptr_t size, uintptr_t boundary) {
+ return (size + boundary - 1) & ~(boundary - 1);
+}
+
+uintptr_t getTlsSize() {
+ // all current supported platforms have 16 bytes stack alignment
+ const size_t kStackAlign = 16;
+ typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
+ get_tls_func get_tls;
+ void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+ memcpy(&get_tls, &get_tls_static_info_ptr, sizeof(get_tls_static_info_ptr));
+ ASSERT(get_tls != 0);
+ size_t tls_size = 0;
+ size_t tls_align = 0;
+ get_tls(&tls_size, &tls_align);
+ if (tls_align < kStackAlign)
+ tls_align = kStackAlign;
+ return RoundUpTo(tls_size, tls_align);
+}
+
+#endif
+
/** @brief Constructor */
ModelExecution::ModelExecution(ModelChecker *m, Scheduler *scheduler) :
model(m),
fuzzer(new Fuzzer()),
thrd_func_list(),
thrd_func_inst_lists()
+#ifdef TLS
+ ,tls_base(NULL),
+ tls_addr(0),
+ tls_size(0),
+ thd_desc_size(0)
+#endif
{
/* Initialize a model-checker thread, for special ModelActions */
model_thread = new Thread(get_next_id());
scheduler->register_engine(this);
}
+#ifdef TLS
+void ModelExecution::initTLS() {
+ tls_addr = get_tls_addr();
+ tls_size = getTlsSize();
+ tls_addr -= tls_size;
+ thd_desc_size = ThreadDescriptorSize();
+ tls_addr += thd_desc_size;
+ tls_base = (char *) snapshot_calloc(tls_size,1);
+ memcpy(tls_base, reinterpret_cast<const char *>(tls_addr), tls_size);
+}
+#endif
+
/** @brief Destructor */
ModelExecution::~ModelExecution()
{
* This data structure is handled by ModelHistory
*/
SnapVector< SnapList< func_inst_list_t *> *> thrd_func_inst_lists;
+
+#ifdef TLS
+public:
+ char * getTLSBase() {return tls_base;}
+ uintptr_t getTLSAddr() {return tls_addr;}
+ uintptr_t getTLSSize() {if (!tls_base) initTLS();return tls_size;}
+ uintptr_t getThdDescSize() {return thd_desc_size;}
+private:
+ void initTLS();
+ char * tls_base;
+ uintptr_t tls_addr;
+ uintptr_t tls_size;
+ uintptr_t thd_desc_size;
+#endif
};
#endif /* __EXECUTION_H__ */
void ModelHistory::enter_function(const uint32_t func_id, thread_id_t tid)
{
- //model_print("thread %d entering func %d\n", tid, func_id);
- uint32_t id = id_to_int(tid);
- SnapVector<func_id_list_t *> * thrd_func_list = model->get_execution()->get_thrd_func_list();
- SnapVector< SnapList<func_inst_list_t *> *> *
- thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists();
+ /* //model_print("thread %d entering func %d\n", tid, func_id);
+ uint32_t id = id_to_int(tid);
+ SnapVector<func_id_list_t *> * thrd_func_list = model->get_execution()->get_thrd_func_list();
+ SnapVector< SnapList<func_inst_list_t *> *> *
+ thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists();
- if ( thrd_func_list->size() <= id ) {
- thrd_func_list->resize( id + 1 );
- thrd_func_inst_lists->resize( id + 1 );
- }
+ if ( thrd_func_list->size() <= id ) {
+ thrd_func_list->resize( id + 1 );
+ thrd_func_inst_lists->resize( id + 1 );
+ }
- func_id_list_t * func_list = thrd_func_list->at(id);
- SnapList<func_inst_list_t *> * func_inst_lists = thrd_func_inst_lists->at(id);
+ func_id_list_t * func_list = thrd_func_list->at(id);
+ SnapList<func_inst_list_t *> * func_inst_lists = thrd_func_inst_lists->at(id);
- if (func_list == NULL) {
- func_list = new func_id_list_t();
- thrd_func_list->at(id) = func_list;
- }
+ if (func_list == NULL) {
+ func_list = new func_id_list_t();
+ thrd_func_list->at(id) = func_list;
+ }
- if (func_inst_lists == NULL) {
- func_inst_lists = new SnapList< func_inst_list_t *>();
- thrd_func_inst_lists->at(id) = func_inst_lists;
- }
+ if (func_inst_lists == NULL) {
+ func_inst_lists = new SnapList< func_inst_list_t *>();
+ thrd_func_inst_lists->at(id) = func_inst_lists;
+ }
- func_list->push_back(func_id);
- func_inst_lists->push_back( new func_inst_list_t() );
+ func_list->push_back(func_id);
+ func_inst_lists->push_back( new func_inst_list_t() );
- if ( func_nodes.size() <= func_id )
- resize_func_nodes( func_id + 1 );
-}
+ if ( func_nodes.size() <= func_id )
+ resize_func_nodes( func_id + 1 );
+ */}
/* @param func_id a non-zero value */
void ModelHistory::exit_function(const uint32_t func_id, thread_id_t tid)
{
- uint32_t id = id_to_int(tid);
- SnapVector<func_id_list_t *> * thrd_func_list = model->get_execution()->get_thrd_func_list();
- SnapVector< SnapList<func_inst_list_t *> *> *
- thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists();
-
- func_id_list_t * func_list = thrd_func_list->at(id);
- SnapList<func_inst_list_t *> * func_inst_lists = thrd_func_inst_lists->at(id);
-
- uint32_t last_func_id = func_list->back();
-
- if (last_func_id == func_id) {
- /* clear read map upon exiting functions */
- FuncNode * func_node = func_nodes[func_id];
- func_node->clear_read_map(tid);
-
- func_inst_list_t * curr_inst_list = func_inst_lists->back();
- func_node->link_insts(curr_inst_list);
-
- func_list->pop_back();
- func_inst_lists->pop_back();
- } else {
- model_print("trying to exit with a wrong function id\n");
- model_print("--- last_func: %d, func_id: %d\n", last_func_id, func_id);
- }
- //model_print("thread %d exiting func %d\n", tid, func_id);
+ /* uint32_t id = id_to_int(tid);
+ SnapVector<func_id_list_t *> * thrd_func_list = model->get_execution()->get_thrd_func_list();
+ SnapVector< SnapList<func_inst_list_t *> *> *
+ thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists();
+
+ func_id_list_t * func_list = thrd_func_list->at(id);
+ SnapList<func_inst_list_t *> * func_inst_lists = thrd_func_inst_lists->at(id);
+
+ uint32_t last_func_id = func_list->back();
+
+ if (last_func_id == func_id) {
+ FuncNode * func_node = func_nodes[func_id];
+ func_node->clear_read_map(tid);
+
+ func_inst_list_t * curr_inst_list = func_inst_lists->back();
+ func_node->link_insts(curr_inst_list);
+
+ func_list->pop_back();
+ func_inst_lists->pop_back();
+ } else {
+ model_print("trying to exit with a wrong function id\n");
+ model_print("--- last_func: %d, func_id: %d\n", last_func_id, func_id);
+ }
+ //model_print("thread %d exiting func %d\n", tid, func_id);*/
}
void ModelHistory::resize_func_nodes(uint32_t new_size)
if ( old_size < new_size )
func_nodes.resize(new_size);
- for (uint32_t id = old_size; id < new_size; id++) {
+ for (uint32_t id = old_size;id < new_size;id++) {
const char * func_name = func_map_rev[id];
FuncNode * func_node = new FuncNode();
func_node->set_func_id(id);
/** Wrapper to run the user's main function, with appropriate arguments */
void user_main_wrapper(void *)
{
+#ifdef TLS
+ model->get_execution()->getTLSSize();
+#endif
user_main(model->params.argc, model->params.argv);
}
{
memset(&stats,0,sizeof(struct execution_stats));
init_thread = new Thread(execution->get_next_id(), (thrd_t *) model_malloc(sizeof(thrd_t)), &user_main_wrapper, NULL, NULL); // L: user_main_wrapper passes the user program
+#ifdef TLS
+ init_thread->setTLS((char *)get_tls_addr());
+#endif
execution->add_thread(init_thread);
scheduler->set_current_thread(init_thread);
execution->setParams(¶ms);
void set_inspect_plugin(TraceAnalysis *a) { inspect_plugin=a; }
void startMainThread();
void startChecker();
+ Thread * getInitThread() {return init_thread;}
MEMALLOC
private:
/** Flag indicates whether to restart the model checker. */
model = new ModelChecker();
model->startChecker();
}
-
+
ModelExecution *execution = model->get_execution();
cdsc::snapmutex *m = execution->getMutexMap()->get(p_mutex);
return m->try_lock();
void operator delete[](void *p, size_t size) {
Thread_free(p);
}
+#ifdef TLS
+ void setTLS(char *_tls) { tls = _tls;}
+#endif
private:
int create_context();
void *arg;
ucontext_t context;
void *stack;
+#ifdef TLS
+ char *tls;
+#endif
thrd_t *user_thread;
thread_id_t id;
thread_state state;
const bool model_thread;
};
+#ifdef TLS
+uintptr_t get_tls_addr();
+#endif
+
Thread * thread_current();
void thread_startup();
/* global "model" object */
#include "model.h"
+#include "execution.h"
+
+#ifdef TLS
+uintptr_t get_tls_addr() {
+ uintptr_t addr;
+ asm ("mov %%fs:0, %0" : "=r" (addr));
+ return addr;
+}
+
+#include <asm/prctl.h>
+#include <sys/prctl.h>
+extern "C" {
+int arch_prctl(int code, unsigned long addr);
+}
+static void set_tls_addr(uintptr_t addr) {
+ arch_prctl(ARCH_SET_FS, addr);
+ asm ("mov %0, %%fs:0" : : "r" (addr) : "memory");
+}
+#endif
/** Allocate a stack for a new thread. */
static void * stack_allocate(size_t size)
/* Add dummy "start" action, just to create a first clock vector */
model->switch_to_master(new ModelAction(THREAD_START, std::memory_order_seq_cst, curr_thread));
+#ifdef TLS
+ if (curr_thread->tls == NULL) {
+ uintptr_t tlssize = model->get_execution()->getTLSSize();
+ uintptr_t thddesc = model->get_execution()->getThdDescSize();
+ curr_thread->tls = (char*) Thread_malloc(tlssize);
+ memcpy(curr_thread->tls, model->get_execution()->getTLSBase(), tlssize);
+ curr_thread->tls += tlssize - thddesc;
+ set_tls_addr((uintptr_t)curr_thread->tls);
+ }
+#endif
+
/* Call the actual thread function */
if (curr_thread->start_routine != NULL) {
curr_thread->start_routine(curr_thread->arg);
int Thread::swap(Thread *t, ucontext_t *ctxt)
{
t->set_state(THREAD_READY);
+#ifdef TLS
+ set_tls_addr((uintptr_t)model->getInitThread()->tls);
+#endif
return model_swapcontext(&t->context, ctxt);
}
int Thread::swap(ucontext_t *ctxt, Thread *t)
{
t->set_state(THREAD_RUNNING);
+#ifdef TLS
+ if (t->tls != NULL)
+ set_tls_addr((uintptr_t)t->tls);
+#endif
return model_swapcontext(ctxt, &t->context);
}
state = THREAD_COMPLETED;
if (stack)
stack_free(stack);
+#ifdef TLS
+ if (tls && get_id() != 1)
+ tls += model->get_execution()->getTLSSize() - model->get_execution()->getThdDescSize();
+ Thread_free(tls);
+#endif
}
/**
start_routine(NULL),
arg(NULL),
stack(NULL),
+#ifdef TLS
+ tls(NULL),
+#endif
user_thread(NULL),
id(tid),
state(THREAD_READY), /* Thread is always ready? */
start_routine(func),
pstart_routine(NULL),
arg(a),
+#ifdef TLS
+ tls(NULL),
+#endif
user_thread(t),
id(tid),
state(THREAD_CREATED),
start_routine(NULL),
pstart_routine(func),
arg(a),
+#ifdef TLS
+ tls(NULL),
+#endif
user_thread(t),
id(tid),
state(THREAD_CREATED),