| //=-- lsan_thread.cc ------------------------------------------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file is a part of LeakSanitizer. | |
| // See lsan_thread.h for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lsan_thread.h" | |
| #include "sanitizer_common/sanitizer_common.h" | |
| #include "sanitizer_common/sanitizer_placement_new.h" | |
| #include "sanitizer_common/sanitizer_thread_registry.h" | |
| #include "sanitizer_common/sanitizer_tls_get_addr.h" | |
| #include "lsan_allocator.h" | |
| namespace __lsan { | |
| const u32 kInvalidTid = (u32) -1; | |
| static ThreadRegistry *thread_registry; | |
| static THREADLOCAL u32 current_thread_tid = kInvalidTid; | |
| static ThreadContextBase *CreateThreadContext(u32 tid) { | |
| void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext"); | |
| return new(mem) ThreadContext(tid); | |
| } | |
| static const uptr kMaxThreads = 1 << 13; | |
| static const uptr kThreadQuarantineSize = 64; | |
| void InitializeThreadRegistry() { | |
| static char thread_registry_placeholder[sizeof(ThreadRegistry)] ALIGNED(64); | |
| thread_registry = new(thread_registry_placeholder) | |
| ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize); | |
| } | |
| u32 GetCurrentThread() { | |
| return current_thread_tid; | |
| } | |
| void SetCurrentThread(u32 tid) { | |
| current_thread_tid = tid; | |
| } | |
| ThreadContext::ThreadContext(int tid) | |
| : ThreadContextBase(tid), | |
| stack_begin_(0), | |
| stack_end_(0), | |
| cache_begin_(0), | |
| cache_end_(0), | |
| tls_begin_(0), | |
| tls_end_(0), | |
| dtls_(nullptr) {} | |
| struct OnStartedArgs { | |
| uptr stack_begin, stack_end, | |
| cache_begin, cache_end, | |
| tls_begin, tls_end; | |
| DTLS *dtls; | |
| }; | |
| void ThreadContext::OnStarted(void *arg) { | |
| OnStartedArgs *args = reinterpret_cast<OnStartedArgs *>(arg); | |
| stack_begin_ = args->stack_begin; | |
| stack_end_ = args->stack_end; | |
| tls_begin_ = args->tls_begin; | |
| tls_end_ = args->tls_end; | |
| cache_begin_ = args->cache_begin; | |
| cache_end_ = args->cache_end; | |
| dtls_ = args->dtls; | |
| } | |
| void ThreadContext::OnFinished() { | |
| AllocatorThreadFinish(); | |
| DTLS_Destroy(); | |
| } | |
| u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) { | |
| return thread_registry->CreateThread(user_id, detached, parent_tid, | |
| /* arg */ nullptr); | |
| } | |
| void ThreadStart(u32 tid, uptr os_id) { | |
| OnStartedArgs args; | |
| uptr stack_size = 0; | |
| uptr tls_size = 0; | |
| GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size, | |
| &args.tls_begin, &tls_size); | |
| args.stack_end = args.stack_begin + stack_size; | |
| args.tls_end = args.tls_begin + tls_size; | |
| GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); | |
| args.dtls = DTLS_Get(); | |
| thread_registry->StartThread(tid, os_id, &args); | |
| } | |
| void ThreadFinish() { | |
| thread_registry->FinishThread(GetCurrentThread()); | |
| } | |
| ThreadContext *CurrentThreadContext() { | |
| if (!thread_registry) return nullptr; | |
| if (GetCurrentThread() == kInvalidTid) | |
| return nullptr; | |
| // No lock needed when getting current thread. | |
| return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); | |
| } | |
| static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { | |
| uptr uid = (uptr)arg; | |
| if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| u32 ThreadTid(uptr uid) { | |
| return thread_registry->FindThread(FindThreadByUid, (void*)uid); | |
| } | |
| void ThreadJoin(u32 tid) { | |
| CHECK_NE(tid, kInvalidTid); | |
| thread_registry->JoinThread(tid, /* arg */nullptr); | |
| } | |
| void EnsureMainThreadIDIsCorrect() { | |
| if (GetCurrentThread() == 0) | |
| CurrentThreadContext()->os_id = GetTid(); | |
| } | |
| ///// Interface to the common LSan module. ///// | |
| bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, | |
| uptr *tls_begin, uptr *tls_end, uptr *cache_begin, | |
| uptr *cache_end, DTLS **dtls) { | |
| ThreadContext *context = static_cast<ThreadContext *>( | |
| thread_registry->FindThreadContextByOsIDLocked(os_id)); | |
| if (!context) return false; | |
| *stack_begin = context->stack_begin(); | |
| *stack_end = context->stack_end(); | |
| *tls_begin = context->tls_begin(); | |
| *tls_end = context->tls_end(); | |
| *cache_begin = context->cache_begin(); | |
| *cache_end = context->cache_end(); | |
| *dtls = context->dtls(); | |
| return true; | |
| } | |
| void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, | |
| void *arg) { | |
| } | |
| void LockThreadRegistry() { | |
| thread_registry->Lock(); | |
| } | |
| void UnlockThreadRegistry() { | |
| thread_registry->Unlock(); | |
| } | |
| } // namespace __lsan |