mirror of
https://github.com/intel/llvm.git
synced 2026-01-27 14:50:42 +08:00
[ASan] Switch ASan to generic ThreadRegistry from sanitizer_common. Delete ASan-specific AsanThreadRegistry.
llvm-svn: 177634
This commit is contained in:
@@ -20,7 +20,6 @@ set(ASAN_SOURCES
|
||||
asan_stack.cc
|
||||
asan_stats.cc
|
||||
asan_thread.cc
|
||||
asan_thread_registry.cc
|
||||
asan_win.cc
|
||||
)
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "asan_stats.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_list.h"
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
@@ -91,7 +90,7 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
|
||||
void SetThreadName(const char *name) {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (t)
|
||||
t->summary()->set_name(name);
|
||||
asanThreadRegistry().SetThreadName(t->tid(), name);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
@@ -116,16 +115,23 @@ using namespace __asan; // NOLINT
|
||||
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
AsanThread *t = (AsanThread*)arg;
|
||||
SetCurrentThread(t);
|
||||
return t->ThreadStart();
|
||||
return t->ThreadStart(GetTid());
|
||||
}
|
||||
|
||||
#if ASAN_INTERCEPT_PTHREAD_CREATE
|
||||
extern "C" int pthread_attr_getdetachstate(void *attr, int *v);
|
||||
|
||||
INTERCEPTOR(int, pthread_create, void *thread,
|
||||
void *attr, void *(*start_routine)(void*), void *arg) {
|
||||
GET_STACK_TRACE_THREAD;
|
||||
int detached = 0;
|
||||
if (attr != 0)
|
||||
pthread_attr_getdetachstate(attr, &detached);
|
||||
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
|
||||
asanThreadRegistry().RegisterThread(t);
|
||||
AsanThread *t = AsanThread::Create(start_routine, arg);
|
||||
CreateThreadContextArgs args = { t, &stack };
|
||||
asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args);
|
||||
return REAL(pthread_create)(thread, attr, asan_thread_start, t);
|
||||
}
|
||||
#endif // ASAN_INTERCEPT_PTHREAD_CREATE
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#include <crt_externs.h> // for _NSGetArgv
|
||||
@@ -293,9 +292,11 @@ static ALWAYS_INLINE
|
||||
void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (!t) {
|
||||
t = AsanThread::Create(parent_tid, 0, 0, stack);
|
||||
asanThreadRegistry().RegisterThread(t);
|
||||
t = AsanThread::Create(0, 0);
|
||||
CreateThreadContextArgs args = { t, stack };
|
||||
asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args);
|
||||
t->Init();
|
||||
asanThreadRegistry().StartThread(t->tid(), 0, 0);
|
||||
SetCurrentThread(t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread_registry.h"
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread_registry.h"
|
||||
|
||||
// Similar code is used in Google Perftools,
|
||||
// http://code.google.com/p/google-perftools.
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_report_decorator.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
@@ -237,7 +236,7 @@ bool DescribeAddressIfShadow(uptr addr) {
|
||||
}
|
||||
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||
AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
|
||||
AsanThread *t = FindThreadByStackAddress(addr);
|
||||
if (!t) return false;
|
||||
const sptr kBufSize = 4095;
|
||||
char buf[kBufSize];
|
||||
@@ -286,7 +285,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||
Printf("HINT: this may be a false positive if your program uses "
|
||||
"some custom stack unwind mechanism or swapcontext\n"
|
||||
" (longjmp and C++ exceptions *are* supported)\n");
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(t->context());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -315,10 +314,10 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
|
||||
}
|
||||
|
||||
// Return " (thread_name) " or an empty string if the name is empty.
|
||||
const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[],
|
||||
const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
|
||||
uptr buff_len) {
|
||||
const char *name = t->name();
|
||||
if (*name == 0) return "";
|
||||
const char *name = t->name;
|
||||
if (name[0] == '\0') return "";
|
||||
buff[0] = 0;
|
||||
internal_strncat(buff, " (", 3);
|
||||
internal_strncat(buff, name, buff_len - 4);
|
||||
@@ -329,7 +328,8 @@ const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[],
|
||||
const char *ThreadNameWithParenthesis(u32 tid, char buff[],
|
||||
uptr buff_len) {
|
||||
if (tid == kInvalidTid) return "";
|
||||
AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid);
|
||||
asanThreadRegistry().CheckLocked();
|
||||
AsanThreadContext *t = GetThreadContextByTidLocked(tid);
|
||||
return ThreadNameWithParenthesis(t, buff, buff_len);
|
||||
}
|
||||
|
||||
@@ -338,8 +338,9 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
if (!chunk.IsValid()) return;
|
||||
DescribeAccessToHeapChunk(chunk, addr, access_size);
|
||||
CHECK(chunk.AllocTid() != kInvalidTid);
|
||||
AsanThreadSummary *alloc_thread =
|
||||
asanThreadRegistry().FindByTid(chunk.AllocTid());
|
||||
asanThreadRegistry().CheckLocked();
|
||||
AsanThreadContext *alloc_thread =
|
||||
GetThreadContextByTidLocked(chunk.AllocTid());
|
||||
StackTrace alloc_stack;
|
||||
chunk.GetAllocStack(&alloc_stack);
|
||||
AsanThread *t = GetCurrentThread();
|
||||
@@ -347,30 +348,30 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
char tname[128];
|
||||
Decorator d;
|
||||
if (chunk.FreeTid() != kInvalidTid) {
|
||||
AsanThreadSummary *free_thread =
|
||||
asanThreadRegistry().FindByTid(chunk.FreeTid());
|
||||
AsanThreadContext *free_thread =
|
||||
GetThreadContextByTidLocked(chunk.FreeTid());
|
||||
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
|
||||
free_thread->tid(),
|
||||
free_thread->tid,
|
||||
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
StackTrace free_stack;
|
||||
chunk.GetFreeStack(&free_stack);
|
||||
PrintStack(&free_stack);
|
||||
Printf("%spreviously allocated by thread T%d%s here:%s\n",
|
||||
d.Allocation(), alloc_thread->tid(),
|
||||
d.Allocation(), alloc_thread->tid,
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(t->context());
|
||||
DescribeThread(free_thread);
|
||||
DescribeThread(alloc_thread);
|
||||
} else {
|
||||
Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
|
||||
alloc_thread->tid(),
|
||||
alloc_thread->tid,
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(t->summary());
|
||||
DescribeThread(t->context());
|
||||
DescribeThread(alloc_thread);
|
||||
}
|
||||
}
|
||||
@@ -390,26 +391,27 @@ void DescribeAddress(uptr addr, uptr access_size) {
|
||||
|
||||
// ------------------- Thread description -------------------- {{{1
|
||||
|
||||
void DescribeThread(AsanThreadSummary *summary) {
|
||||
CHECK(summary);
|
||||
void DescribeThread(AsanThreadContext *context) {
|
||||
CHECK(context);
|
||||
asanThreadRegistry().CheckLocked();
|
||||
// No need to announce the main thread.
|
||||
if (summary->tid() == 0 || summary->announced()) {
|
||||
if (context->tid == 0 || context->announced) {
|
||||
return;
|
||||
}
|
||||
summary->set_announced(true);
|
||||
context->announced = true;
|
||||
char tname[128];
|
||||
Printf("Thread T%d%s", summary->tid(),
|
||||
ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname)));
|
||||
Printf("Thread T%d%s", context->tid,
|
||||
ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
|
||||
Printf(" created by T%d%s here:\n",
|
||||
summary->parent_tid(),
|
||||
ThreadNameWithParenthesis(summary->parent_tid(),
|
||||
context->parent_tid,
|
||||
ThreadNameWithParenthesis(context->parent_tid,
|
||||
tname, sizeof(tname)));
|
||||
PrintStack(summary->stack());
|
||||
PrintStack(&context->stack);
|
||||
// Recursively described parent thread if needed.
|
||||
if (flags()->print_full_thread_history) {
|
||||
AsanThreadSummary *parent_summary =
|
||||
asanThreadRegistry().FindByTid(summary->parent_tid());
|
||||
DescribeThread(parent_summary);
|
||||
AsanThreadContext *parent_context =
|
||||
GetThreadContextByTidLocked(context->parent_tid);
|
||||
DescribeThread(parent_context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +442,10 @@ class ScopedInErrorReport {
|
||||
internal__exit(flags()->exitcode);
|
||||
}
|
||||
ASAN_ON_ERROR();
|
||||
// Make sure the registry is locked while we're printing an error report.
|
||||
// We can lock the registry only here to avoid self-deadlock in case of
|
||||
// recursive reports.
|
||||
asanThreadRegistry().Lock();
|
||||
reporting_thread_tid = GetCurrentTidOrInvalid();
|
||||
Printf("===================================================="
|
||||
"=============\n");
|
||||
@@ -456,7 +462,7 @@ class ScopedInErrorReport {
|
||||
// Make sure the current thread is announced.
|
||||
AsanThread *curr_thread = GetCurrentThread();
|
||||
if (curr_thread) {
|
||||
DescribeThread(curr_thread->summary());
|
||||
DescribeThread(curr_thread->context());
|
||||
}
|
||||
// Print memory stats.
|
||||
if (flags()->print_stats)
|
||||
|
||||
@@ -29,7 +29,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||
// Determines memory type on its own.
|
||||
void DescribeAddress(uptr addr, uptr access_size);
|
||||
|
||||
void DescribeThread(AsanThreadSummary *summary);
|
||||
void DescribeThread(AsanThreadContext *context);
|
||||
|
||||
// Different kinds of error reports.
|
||||
void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
@@ -517,8 +516,15 @@ void __asan_init() {
|
||||
asan_inited = 1;
|
||||
asan_init_is_running = false;
|
||||
|
||||
asanThreadRegistry().Init();
|
||||
asanThreadRegistry().GetMain()->ThreadStart();
|
||||
// Create main thread.
|
||||
AsanTSDInit(AsanThread::TSDDtor);
|
||||
AsanThread *main_thread = AsanThread::Create(0, 0);
|
||||
CreateThreadContextArgs create_main_args = { main_thread, 0 };
|
||||
u32 main_tid = asanThreadRegistry().CreateThread(
|
||||
0, true, 0, &create_main_args);
|
||||
CHECK_EQ(0, main_tid);
|
||||
SetCurrentThread(main_thread);
|
||||
main_thread->ThreadStart(GetPid());
|
||||
force_interface_symbols(); // no-op.
|
||||
|
||||
InitializeAllocator();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
|
||||
@@ -75,7 +75,7 @@ static AsanStats accumulated_stats(LINKER_INITIALIZED);
|
||||
static uptr max_malloced_memory;
|
||||
static BlockingMutex acc_stats_lock(LINKER_INITIALIZED);
|
||||
|
||||
void FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
|
||||
static void FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
|
||||
acc_stats_lock.CheckLocked();
|
||||
uptr *dst = (uptr*)&accumulated_stats;
|
||||
uptr *src = (uptr*)stats;
|
||||
@@ -86,9 +86,18 @@ void FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
|
||||
}
|
||||
}
|
||||
|
||||
static void FlushThreadStats(ThreadContextBase *tctx_base, void *arg) {
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
|
||||
if (AsanThread *t = tctx->thread)
|
||||
FlushToAccumulatedStatsUnlocked(&t->stats());
|
||||
}
|
||||
|
||||
static void UpdateAccumulatedStatsUnlocked() {
|
||||
acc_stats_lock.CheckLocked();
|
||||
asanThreadRegistry().FlushAllStats();
|
||||
{
|
||||
ThreadRegistryLock l(&asanThreadRegistry());
|
||||
asanThreadRegistry().RunCallbackForEachThreadLocked(FlushThreadStats, 0);
|
||||
}
|
||||
FlushToAccumulatedStatsUnlocked(&unknown_thread_stats);
|
||||
// This is not very accurate: we may miss allocation peaks that happen
|
||||
// between two updates of accumulated_stats_. For more accurate bookkeeping
|
||||
|
||||
@@ -64,8 +64,6 @@ AsanStats &GetCurrentThreadStats();
|
||||
void GetAccumulatedStats(AsanStats *stats);
|
||||
// Flushes a given stats into accumulated stats.
|
||||
void FlushToAccumulatedStats(AsanStats *stats);
|
||||
// FIXME: Hide this method when AsanThreadRegistry is removed.
|
||||
void FlushToAccumulatedStatsUnlocked(AsanStats *stats);
|
||||
|
||||
// A cross-platform equivalent of malloc_statistics_t on Mac OS.
|
||||
struct AsanMallocStats {
|
||||
|
||||
@@ -15,44 +15,77 @@
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
AsanThread::AsanThread(LinkerInitialized x)
|
||||
: fake_stack_(x),
|
||||
malloc_storage_(x),
|
||||
stats_(x) { }
|
||||
// AsanThreadContext implementation.
|
||||
|
||||
AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine,
|
||||
void *arg, StackTrace *stack) {
|
||||
void AsanThreadContext::OnCreated(void *arg) {
|
||||
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
|
||||
if (args->stack) {
|
||||
internal_memcpy(&stack, args->stack, sizeof(stack));
|
||||
}
|
||||
thread = args->thread;
|
||||
thread->set_context(this);
|
||||
}
|
||||
|
||||
void AsanThreadContext::OnFinished() {
|
||||
// Drop the link to the AsanThread object.
|
||||
thread = 0;
|
||||
}
|
||||
|
||||
static char thread_registry_placeholder[sizeof(ThreadRegistry)];
|
||||
static ThreadRegistry *asan_thread_registry;
|
||||
|
||||
static ThreadContextBase *GetAsanThreadContext(u32 tid) {
|
||||
void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext");
|
||||
return new(mem) AsanThreadContext(tid);
|
||||
}
|
||||
|
||||
ThreadRegistry &asanThreadRegistry() {
|
||||
static bool initialized;
|
||||
// Don't worry about thread_safety - this should be called when there is
|
||||
// a single thread.
|
||||
if (!initialized) {
|
||||
// Never reuse ASan threads: we store pointer to AsanThreadContext
|
||||
// in TSD and can't reliably tell when no more TSD destructors will
|
||||
// be called. It would be wrong to reuse AsanThreadContext for another
|
||||
// thread before all TSD destructors will be called for it.
|
||||
asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry(
|
||||
GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads);
|
||||
initialized = true;
|
||||
}
|
||||
return *asan_thread_registry;
|
||||
}
|
||||
|
||||
AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
|
||||
return static_cast<AsanThreadContext *>(
|
||||
asanThreadRegistry().GetThreadLocked(tid));
|
||||
}
|
||||
|
||||
// AsanThread implementation.
|
||||
|
||||
AsanThread *AsanThread::Create(thread_callback_t start_routine,
|
||||
void *arg) {
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
|
||||
AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__);
|
||||
thread->start_routine_ = start_routine;
|
||||
thread->arg_ = arg;
|
||||
|
||||
const uptr kSummaryAllocSize = PageSize;
|
||||
CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize);
|
||||
AsanThreadSummary *summary =
|
||||
(AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary");
|
||||
summary->Init(parent_tid, stack);
|
||||
summary->set_thread(thread);
|
||||
thread->set_summary(summary);
|
||||
thread->context_ = 0;
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
void AsanThreadSummary::TSDDtor(void *tsd) {
|
||||
AsanThreadSummary *summary = (AsanThreadSummary*)tsd;
|
||||
if (flags()->verbosity >= 1) {
|
||||
Report("T%d TSDDtor\n", summary->tid());
|
||||
}
|
||||
if (summary->thread()) {
|
||||
summary->thread()->Destroy();
|
||||
}
|
||||
void AsanThread::TSDDtor(void *tsd) {
|
||||
AsanThreadContext *context = (AsanThreadContext*)tsd;
|
||||
if (flags()->verbosity >= 1)
|
||||
Report("T%d TSDDtor\n", context->tid);
|
||||
if (context->thread)
|
||||
context->thread->Destroy();
|
||||
}
|
||||
|
||||
void AsanThread::Destroy() {
|
||||
@@ -60,8 +93,8 @@ void AsanThread::Destroy() {
|
||||
Report("T%d exited\n", tid());
|
||||
}
|
||||
|
||||
asanThreadRegistry().UnregisterThread(this);
|
||||
CHECK(summary()->thread() == 0);
|
||||
asanThreadRegistry().FinishThread(tid());
|
||||
FlushToAccumulatedStats(&stats_);
|
||||
// We also clear the shadow on thread destruction because
|
||||
// some code may still be executing in later TSD destructors
|
||||
// and we don't want it to have any poisoned stack.
|
||||
@@ -86,8 +119,9 @@ void AsanThread::Init() {
|
||||
AsanPlatformThreadInit();
|
||||
}
|
||||
|
||||
thread_return_t AsanThread::ThreadStart() {
|
||||
thread_return_t AsanThread::ThreadStart(uptr os_id) {
|
||||
Init();
|
||||
asanThreadRegistry().StartThread(tid(), os_id, 0);
|
||||
if (flags()->use_sigaltstack) SetAlternateSignalStack();
|
||||
|
||||
if (!start_routine_) {
|
||||
@@ -152,37 +186,45 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
|
||||
return (const char*)ptr[1];
|
||||
}
|
||||
|
||||
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
|
||||
void *addr) {
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
|
||||
AsanThread *t = tctx->thread;
|
||||
return (t && t->fake_stack().StackSize() &&
|
||||
(t->fake_stack().AddrIsInFakeStack((uptr)addr) ||
|
||||
t->AddrIsInStack((uptr)addr)));
|
||||
}
|
||||
|
||||
AsanThread *GetCurrentThread() {
|
||||
AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet();
|
||||
if (!summary) {
|
||||
#if SANITIZER_ANDROID
|
||||
// On Android, libc constructor is called _after_ asan_init, and cleans up
|
||||
// TSD. Try to figure out if this is still the main thread by the stack
|
||||
// address. We are not entirely sure that we have correct main thread
|
||||
// limits, so only do this magic on Android, and only if the found thread is
|
||||
// the main thread.
|
||||
AsanThread *thread =
|
||||
asanThreadRegistry().FindThreadByStackAddress((uptr)&summary);
|
||||
if (thread && thread->tid() == 0) {
|
||||
SetCurrentThread(thread);
|
||||
return thread;
|
||||
AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet();
|
||||
if (!context) {
|
||||
if (SANITIZER_ANDROID) {
|
||||
// On Android, libc constructor is called _after_ asan_init, and cleans up
|
||||
// TSD. Try to figure out if this is still the main thread by the stack
|
||||
// address. We are not entirely sure that we have correct main thread
|
||||
// limits, so only do this magic on Android, and only if the found thread is
|
||||
// the main thread.
|
||||
AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
|
||||
if (ThreadStackContainsAddress(tctx, &context)) {
|
||||
SetCurrentThread(tctx->thread);
|
||||
return tctx->thread;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return summary->thread();
|
||||
return context->thread;
|
||||
}
|
||||
|
||||
void SetCurrentThread(AsanThread *t) {
|
||||
CHECK(t->summary());
|
||||
CHECK(t->context());
|
||||
if (flags()->verbosity >= 2) {
|
||||
Report("SetCurrentThread: %p for thread %p\n",
|
||||
t->summary(), (void*)GetThreadSelf());
|
||||
t->context(), (void*)GetThreadSelf());
|
||||
}
|
||||
// Make sure we do not reset the current AsanThread.
|
||||
CHECK(AsanTSDGet() == 0);
|
||||
AsanTSDSet(t->summary());
|
||||
CHECK(AsanTSDGet() == t->summary());
|
||||
CHECK_EQ(0, AsanTSDGet());
|
||||
AsanTSDSet(t->context());
|
||||
CHECK_EQ(t->context(), AsanTSDGet());
|
||||
}
|
||||
|
||||
u32 GetCurrentTidOrInvalid() {
|
||||
@@ -190,4 +232,12 @@ u32 GetCurrentTidOrInvalid() {
|
||||
return t ? t->tid() : kInvalidTid;
|
||||
}
|
||||
|
||||
AsanThread *FindThreadByStackAddress(uptr addr) {
|
||||
asanThreadRegistry().CheckLocked();
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
|
||||
asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
|
||||
(void *)addr));
|
||||
return tctx ? tctx->thread : 0;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
@@ -19,71 +19,52 @@
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_thread_registry.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits.
|
||||
const u32 kMaxNumberOfThreads = (1 << 22); // 4M
|
||||
|
||||
class AsanThread;
|
||||
|
||||
// These objects are created for every thread and are never deleted,
|
||||
// so we can find them by tid even if the thread is long dead.
|
||||
class AsanThreadSummary {
|
||||
class AsanThreadContext : public ThreadContextBase {
|
||||
public:
|
||||
explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
|
||||
void Init(u32 parent_tid, StackTrace *stack) {
|
||||
parent_tid_ = parent_tid;
|
||||
announced_ = false;
|
||||
tid_ = kInvalidTid;
|
||||
if (stack) {
|
||||
internal_memcpy(&stack_, stack, sizeof(*stack));
|
||||
}
|
||||
thread_ = 0;
|
||||
name_[0] = 0;
|
||||
explicit AsanThreadContext(int tid)
|
||||
: ThreadContextBase(tid),
|
||||
announced(false),
|
||||
thread(0) {
|
||||
internal_memset(&stack, 0, sizeof(stack));
|
||||
}
|
||||
u32 tid() { return tid_; }
|
||||
void set_tid(u32 tid) { tid_ = tid; }
|
||||
u32 parent_tid() { return parent_tid_; }
|
||||
bool announced() { return announced_; }
|
||||
void set_announced(bool announced) { announced_ = announced; }
|
||||
StackTrace *stack() { return &stack_; }
|
||||
AsanThread *thread() { return thread_; }
|
||||
void set_thread(AsanThread *thread) { thread_ = thread; }
|
||||
static void TSDDtor(void *tsd);
|
||||
void set_name(const char *name) {
|
||||
internal_strncpy(name_, name, sizeof(name_) - 1);
|
||||
}
|
||||
const char *name() { return name_; }
|
||||
bool announced;
|
||||
StackTrace stack;
|
||||
AsanThread *thread;
|
||||
|
||||
private:
|
||||
u32 tid_;
|
||||
u32 parent_tid_;
|
||||
bool announced_;
|
||||
StackTrace stack_;
|
||||
AsanThread *thread_;
|
||||
char name_[128];
|
||||
void OnCreated(void *arg);
|
||||
void OnFinished();
|
||||
};
|
||||
|
||||
// AsanThreadSummary objects are never freed, so we need many of them.
|
||||
COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094);
|
||||
// AsanThreadContext objects are never freed, so we need many of them.
|
||||
COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096);
|
||||
|
||||
// AsanThread are stored in TSD and destroyed when the thread dies.
|
||||
class AsanThread {
|
||||
public:
|
||||
explicit AsanThread(LinkerInitialized); // for T0.
|
||||
static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine,
|
||||
void *arg, StackTrace *stack);
|
||||
static AsanThread *Create(thread_callback_t start_routine, void *arg);
|
||||
static void TSDDtor(void *tsd);
|
||||
void Destroy();
|
||||
|
||||
void Init(); // Should be called from the thread itself.
|
||||
thread_return_t ThreadStart();
|
||||
thread_return_t ThreadStart(uptr os_id);
|
||||
|
||||
uptr stack_top() { return stack_top_; }
|
||||
uptr stack_bottom() { return stack_bottom_; }
|
||||
uptr stack_size() { return stack_top_ - stack_bottom_; }
|
||||
u32 tid() { return summary_->tid(); }
|
||||
AsanThreadSummary *summary() { return summary_; }
|
||||
void set_summary(AsanThreadSummary *summary) { summary_ = summary; }
|
||||
u32 tid() { return context_->tid; }
|
||||
AsanThreadContext *context() { return context_; }
|
||||
void set_context(AsanThreadContext *context) { context_ = context; }
|
||||
|
||||
const char *GetFrameNameByAddr(uptr addr, uptr *offset);
|
||||
|
||||
@@ -96,9 +77,10 @@ class AsanThread {
|
||||
AsanStats &stats() { return stats_; }
|
||||
|
||||
private:
|
||||
AsanThread() {}
|
||||
void SetThreadStackTopAndBottom();
|
||||
void ClearShadowForThreadStack();
|
||||
AsanThreadSummary *summary_;
|
||||
AsanThreadContext *context_;
|
||||
thread_callback_t start_routine_;
|
||||
void *arg_;
|
||||
uptr stack_top_;
|
||||
@@ -109,10 +91,22 @@ class AsanThread {
|
||||
AsanStats stats_;
|
||||
};
|
||||
|
||||
struct CreateThreadContextArgs {
|
||||
AsanThread *thread;
|
||||
StackTrace *stack;
|
||||
};
|
||||
|
||||
// Returns a single instance of registry.
|
||||
ThreadRegistry &asanThreadRegistry();
|
||||
|
||||
// Must be called under ThreadRegistryLock.
|
||||
AsanThreadContext *GetThreadContextByTidLocked(u32 tid);
|
||||
|
||||
// Get the current thread. May return 0.
|
||||
AsanThread *GetCurrentThread();
|
||||
void SetCurrentThread(AsanThread *t);
|
||||
u32 GetCurrentTidOrInvalid();
|
||||
AsanThread *FindThreadByStackAddress(uptr addr);
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
//===-- asan_thread_registry.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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// AsanThreadRegistry-related code. AsanThreadRegistry is a container
|
||||
// for summaries of all created threads.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED);
|
||||
|
||||
AsanThreadRegistry &asanThreadRegistry() {
|
||||
return asan_thread_registry;
|
||||
}
|
||||
|
||||
AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
|
||||
: main_thread_(x),
|
||||
main_thread_summary_(x),
|
||||
mu_(x) { }
|
||||
|
||||
void AsanThreadRegistry::Init() {
|
||||
AsanTSDInit(AsanThreadSummary::TSDDtor);
|
||||
main_thread_.set_summary(&main_thread_summary_);
|
||||
main_thread_summary_.set_thread(&main_thread_);
|
||||
RegisterThread(&main_thread_);
|
||||
SetCurrentThread(&main_thread_);
|
||||
// At this point only one thread exists.
|
||||
inited_ = true;
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
|
||||
BlockingMutexLock lock(&mu_);
|
||||
u32 tid = n_threads_;
|
||||
n_threads_++;
|
||||
CHECK(n_threads_ < kMaxNumberOfThreads);
|
||||
|
||||
AsanThreadSummary *summary = thread->summary();
|
||||
CHECK(summary != 0);
|
||||
summary->set_tid(tid);
|
||||
thread_summaries_[tid] = summary;
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
|
||||
BlockingMutexLock lock(&mu_);
|
||||
FlushToAccumulatedStats(&thread->stats());
|
||||
AsanThreadSummary *summary = thread->summary();
|
||||
CHECK(summary);
|
||||
summary->set_thread(0);
|
||||
}
|
||||
|
||||
AsanThread *AsanThreadRegistry::GetMain() {
|
||||
return &main_thread_;
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::FlushAllStats() {
|
||||
BlockingMutexLock lock(&mu_);
|
||||
for (u32 tid = 0; tid < n_threads_; tid++) {
|
||||
AsanThread *t = thread_summaries_[tid]->thread();
|
||||
if (t != 0) {
|
||||
FlushToAccumulatedStatsUnlocked(&t->stats());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
|
||||
CHECK(tid < n_threads_);
|
||||
CHECK(thread_summaries_[tid]);
|
||||
return thread_summaries_[tid];
|
||||
}
|
||||
|
||||
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
|
||||
BlockingMutexLock lock(&mu_);
|
||||
for (u32 tid = 0; tid < n_threads_; tid++) {
|
||||
AsanThread *t = thread_summaries_[tid]->thread();
|
||||
if (!t || !(t->fake_stack().StackSize())) continue;
|
||||
if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
@@ -1,57 +0,0 @@
|
||||
//===-- asan_thread_registry.h ----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan-private header for asan_thread_registry.cc
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_THREAD_REGISTRY_H
|
||||
#define ASAN_THREAD_REGISTRY_H
|
||||
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// Stores summaries of all created threads, returns current thread,
|
||||
// thread by tid, thread by stack address. There is a single instance
|
||||
// of AsanThreadRegistry for the whole program.
|
||||
// AsanThreadRegistry is thread-safe.
|
||||
class AsanThreadRegistry {
|
||||
public:
|
||||
explicit AsanThreadRegistry(LinkerInitialized);
|
||||
void Init();
|
||||
void RegisterThread(AsanThread *thread);
|
||||
void UnregisterThread(AsanThread *thread);
|
||||
|
||||
AsanThread *GetMain();
|
||||
void FlushAllStats();
|
||||
|
||||
AsanThreadSummary *FindByTid(u32 tid);
|
||||
AsanThread *FindThreadByStackAddress(uptr addr);
|
||||
|
||||
private:
|
||||
static const u32 kMaxNumberOfThreads = (1 << 22); // 4M
|
||||
AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads];
|
||||
AsanThread main_thread_;
|
||||
AsanThreadSummary main_thread_summary_;
|
||||
u32 n_threads_;
|
||||
BlockingMutex mu_;
|
||||
bool inited_;
|
||||
};
|
||||
|
||||
// Returns a single instance of registry.
|
||||
AsanThreadRegistry &asanThreadRegistry();
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_THREAD_REGISTRY_H
|
||||
Reference in New Issue
Block a user