Experimental TLS support.
authorroot <root@dw-6.eecs.uci.edu>
Thu, 25 Jul 2019 19:55:24 +0000 (12:55 -0700)
committerroot <root@dw-6.eecs.uci.edu>
Thu, 25 Jul 2019 20:18:41 +0000 (13:18 -0700)
cmodelint.cc
config.h
execution.cc
execution.h
history.cc
model.cc
model.h
pthread.cc
threads-model.h
threads.cc

index 436de34..b39e15b 100644 (file)
@@ -110,7 +110,7 @@ VOLATILELOAD(64)
        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)
index ed0b6ba..c276bd0 100644 (file)
--- a/config.h
+++ b/config.h
@@ -43,6 +43,8 @@
 /** Page size configuration */
 #define PAGESIZE 4096
 
+#define TLS 1
+
 /** Thread parameters */
 
 /* Size of stack to allocate for a thread. */
index 33a608d..0710dd6 100644 (file)
@@ -47,6 +47,123 @@ struct model_snapshot_members {
        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),
@@ -67,6 +184,12 @@ ModelExecution::ModelExecution(ModelChecker *m, Scheduler *scheduler) :
        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());
@@ -74,6 +197,18 @@ ModelExecution::ModelExecution(ModelChecker *m, Scheduler *scheduler) :
        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()
 {
index f1d3dc5..513a92a 100644 (file)
@@ -212,6 +212,20 @@ private:
         * 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__ */
index a3398f9..4ee34bf 100644 (file)
@@ -18,65 +18,64 @@ ModelHistory::ModelHistory() :
 
 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)
@@ -86,7 +85,7 @@ 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);
index 45fa1b2..e4d6b75 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -24,6 +24,9 @@ ModelChecker *model = NULL;
 /** 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);
 }
 
@@ -41,6 +44,9 @@ ModelChecker::ModelChecker() :
 {
        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(&params);
diff --git a/model.h b/model.h
index 9fb9422..2f137a3 100644 (file)
--- a/model.h
+++ b/model.h
@@ -67,6 +67,7 @@ public:
        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. */
index d23c490..120d365 100644 (file)
@@ -108,7 +108,7 @@ int pthread_mutex_trylock(pthread_mutex_t *p_mutex) {
                model = new ModelChecker();
                model->startChecker();
        }
-       
+
        ModelExecution *execution = model->get_execution();
        cdsc::snapmutex *m = execution->getMutexMap()->get(p_mutex);
        return m->try_lock();
index 02f20b0..eebfcc7 100644 (file)
@@ -118,6 +118,9 @@ public:
        void operator delete[](void *p, size_t size) {
                Thread_free(p);
        }
+#ifdef TLS
+       void setTLS(char *_tls) { tls = _tls;}
+#endif
 private:
        int create_context();
 
@@ -142,6 +145,9 @@ private:
        void *arg;
        ucontext_t context;
        void *stack;
+#ifdef TLS
+       char *tls;
+#endif
        thrd_t *user_thread;
        thread_id_t id;
        thread_state state;
@@ -160,6 +166,10 @@ private:
        const bool model_thread;
 };
 
+#ifdef TLS
+uintptr_t get_tls_addr();
+#endif
+
 Thread * thread_current();
 void thread_startup();
 
index 67f681c..6c7b890 100644 (file)
 
 /* 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)
@@ -50,6 +69,17 @@ void thread_startup()
        /* 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);
@@ -98,6 +128,9 @@ int Thread::create_context()
 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);
 }
 
@@ -112,6 +145,10 @@ int Thread::swap(Thread *t, ucontext_t *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);
 }
 
@@ -124,6 +161,11 @@ void Thread::complete()
        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
 }
 
 /**
@@ -141,6 +183,9 @@ Thread::Thread(thread_id_t tid) :
        start_routine(NULL),
        arg(NULL),
        stack(NULL),
+#ifdef TLS
+       tls(NULL),
+#endif
        user_thread(NULL),
        id(tid),
        state(THREAD_READY),    /* Thread is always ready? */
@@ -163,6 +208,9 @@ Thread::Thread(thread_id_t tid, thrd_t *t, void (*func)(void *), void *a, Thread
        start_routine(func),
        pstart_routine(NULL),
        arg(a),
+#ifdef TLS
+       tls(NULL),
+#endif
        user_thread(t),
        id(tid),
        state(THREAD_CREATED),
@@ -192,6 +240,9 @@ Thread::Thread(thread_id_t tid, thrd_t *t, void *(*func)(void *), void *a, Threa
        start_routine(NULL),
        pstart_routine(func),
        arg(a),
+#ifdef TLS
+       tls(NULL),
+#endif
        user_thread(t),
        id(tid),
        state(THREAD_CREATED),