<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Feb 28, 2014 at 7:57 PM, Alexey Samsonov <span dir="ltr"><<a href="mailto:samsonov@google.com" target="_blank">samsonov@google.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">FYI, I'm going to revert this on Monday. Shell scripts and huge auto-generated .cc files into upstream compiler-rt repository is not nice.</div>
</blockquote><div>+1</div><div>Too much copy paste, please don't do this. </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><br></div><div>
The tool is not usable - there are no instructions on how to use it, or how to build it. You don't even specify its purpose or your goals in the commit message.</div>
<div><br></div><div>If you commit the code that is expected to become the part of compiler-rt, please do it incrementally, with a review process and build system integration.</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">
On Fri, Feb 28, 2014 at 6:52 PM, Dmitry Vyukov <span dir="ltr"><<a href="mailto:dvyukov@google.com" target="_blank">dvyukov@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Author: dvyukov<br>
Date: Fri Feb 28 08:52:20 2014<br>
New Revision: 202505<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=202505&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=202505&view=rev</a><br>
Log:<br>
tsan: add standalone deadlock detector<br>
<br>
<br>
Added:<br>
compiler-rt/trunk/lib/tsan/dd/<br>
compiler-rt/trunk/lib/tsan/dd/build.sh (with props)<br>
compiler-rt/trunk/lib/tsan/dd/dd.cc<br>
compiler-rt/trunk/lib/tsan/dd/dd_interceptors.cc<br>
compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc<br>
compiler-rt/trunk/lib/tsan/dd/dd_rtl.h<br>
<br>
Added: compiler-rt/trunk/lib/tsan/dd/build.sh<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/build.sh?rev=202505&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/build.sh?rev=202505&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/build.sh (added)<br>
+++ compiler-rt/trunk/lib/tsan/dd/build.sh Fri Feb 28 08:52:20 2014<br>
@@ -0,0 +1,45 @@<br>
+#!/bin/bash<br>
+set -e<br>
+<br>
+SRCS="<br>
+ dd_rtl.cc<br>
+ dd_interceptors.cc<br>
+ ../../sanitizer_common/sanitizer_allocator.cc<br>
+ ../../sanitizer_common/sanitizer_common.cc<br>
+ ../../sanitizer_common/sanitizer_deadlock_detector1.cc<br>
+ ../../sanitizer_common/sanitizer_flags.cc<br>
+ ../../sanitizer_common/sanitizer_libc.cc<br>
+ ../../sanitizer_common/sanitizer_printf.cc<br>
+ ../../sanitizer_common/sanitizer_suppressions.cc<br>
+ ../../sanitizer_common/sanitizer_thread_registry.cc<br>
+ ../../sanitizer_common/sanitizer_posix.cc<br>
+ ../../sanitizer_common/sanitizer_posix_libcdep.cc<br>
+ ../../sanitizer_common/sanitizer_procmaps_linux.cc<br>
+ ../../sanitizer_common/sanitizer_linux.cc<br>
+ ../../sanitizer_common/sanitizer_linux_libcdep.cc<br>
+ ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc<br>
+ ../../sanitizer_common/sanitizer_stackdepot.cc<br>
+ ../../sanitizer_common/sanitizer_stacktrace.cc<br>
+ ../../sanitizer_common/sanitizer_stacktrace_libcdep.cc<br>
+ ../../sanitizer_common/sanitizer_symbolizer.cc<br>
+ ../../sanitizer_common/sanitizer_symbolizer_libcdep.cc<br>
+ ../../sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc<br>
+ ../../sanitizer_common/sanitizer_symbolizer_libbacktrace.cc<br>
+ ../../interception/interception_linux.cc<br>
+"<br>
+<br>
+FLAGS=" -I../.. -I../../sanitizer_common -I../../interception -Wall -fno-exceptions -fno-rtti -DSANITIZER_USE_MALLOC"<br>
+if [ "$DEBUG" == "" ]; then<br>
+ FLAGS+=" -DDEBUG=0 -O3 -fomit-frame-pointer"<br>
+else<br>
+ FLAGS+=" -DDEBUG=1 -g"<br>
+fi<br>
+<br>
+rm -f dd.cc<br>
+for F in $SRCS; do<br>
+ g++ $F -c -o dd.o $FLAGS<br>
+ cat $F >> dd.cc<br>
+done<br>
+<br>
+g++ dd.cc -c -o dd.o $FLAGS<br>
+<br>
<br>
Propchange: compiler-rt/trunk/lib/tsan/dd/build.sh<br>
------------------------------------------------------------------------------<br>
svn:executable = *<br>
<br>
Added: compiler-rt/trunk/lib/tsan/dd/dd.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd.cc?rev=202505&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd.cc?rev=202505&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/dd.cc (added)<br>
+++ compiler-rt/trunk/lib/tsan/dd/dd.cc Fri Feb 28 08:52:20 2014<br>
@@ -0,0 +1,5877 @@<br>
+//===-- dd_rtl.cc ---------------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "dd_rtl.h"<br>
+#include "sanitizer_common/sanitizer_common.h"<br>
+#include "sanitizer_common/sanitizer_flags.h"<br>
+#include "sanitizer_common/sanitizer_stacktrace.h"<br>
+#include "sanitizer_common/sanitizer_stackdepot.h"<br>
+<br>
+namespace __dsan {<br>
+<br>
+static Context ctx0;<br>
+static Context * const ctx = &ctx0;<br>
+<br>
+void Initialize() {<br>
+ InitializeInterceptors();<br>
+ //common_flags()->allow_addr2line = true;<br>
+ common_flags()->symbolize = true;<br>
+ ctx->dd = DDetector::Create();<br>
+}<br>
+<br>
+void ThreadInit(Thread *thr) {<br>
+ thr->dd_pt = ctx->dd->CreatePhysicalThread();<br>
+ thr->dd_lt = ctx->dd->CreateLogicalThread(0);<br>
+}<br>
+<br>
+void ThreadDestroy(Thread *thr) {<br>
+ ctx->dd->DestroyPhysicalThread(thr->dd_pt);<br>
+ ctx->dd->DestroyLogicalThread(thr->dd_lt);<br>
+}<br>
+<br>
+static u32 CurrentStackTrace(Thread *thr) {<br>
+ StackTrace trace;<br>
+ thr->in_symbolizer = true;<br>
+ trace.Unwind(1000, 0, 0, 0, 0, 0, false);<br>
+ thr->in_symbolizer = false;<br>
+ const uptr skip = 4;<br>
+ if (trace.size <= skip)<br>
+ return 0;<br>
+ return StackDepotPut(trace.trace + skip, trace.size - skip);<br>
+}<br>
+<br>
+static void PrintStackTrace(Thread *thr, u32 stk) {<br>
+ uptr size = 0;<br>
+ const uptr *trace = StackDepotGet(stk, &size);<br>
+ thr->in_symbolizer = true;<br>
+ StackTrace::PrintStack(trace, size);<br>
+ thr->in_symbolizer = false;<br>
+}<br>
+<br>
+static Mutex *FindMutex(Thread *thr, uptr m) {<br>
+ SpinMutexLock l(&ctx->mutex_mtx);<br>
+ for (Mutex *mtx = ctx->mutex_list; mtx; mtx = mtx->link) {<br>
+ if (mtx->addr == m)<br>
+ return mtx;<br>
+ }<br>
+ Mutex *mtx = (Mutex*)InternalAlloc(sizeof(*mtx));<br>
+ internal_memset(mtx, 0, sizeof(*mtx));<br>
+ mtx->addr = m;<br>
+ ctx->dd->MutexInit(&mtx->dd, CurrentStackTrace(thr), ctx->mutex_seq++);<br>
+ mtx->link = ctx->mutex_list;<br>
+ ctx->mutex_list = mtx;<br>
+ return mtx;<br>
+}<br>
+<br>
+static Mutex *FindMutexAndRemove(uptr m) {<br>
+ SpinMutexLock l(&ctx->mutex_mtx);<br>
+ Mutex **prev = &ctx->mutex_list;<br>
+ for (;;) {<br>
+ Mutex *mtx = *prev;<br>
+ if (mtx == 0)<br>
+ return 0;<br>
+ if (mtx->addr == m) {<br>
+ *prev = mtx->link;<br>
+ return mtx;<br>
+ }<br>
+ prev = &mtx->link;<br>
+ }<br>
+}<br>
+<br>
+static void ReportDeadlock(Thread *thr, DDReport *rep) {<br>
+ Printf("==============================\n");<br>
+ Printf("DEADLOCK\n");<br>
+ PrintStackTrace(thr, CurrentStackTrace(thr));<br>
+ for (int i = 0; i < rep->n; i++) {<br>
+ Printf("Mutex %llu created at:\n", rep->loop[i].mtx_ctx0);<br>
+ PrintStackTrace(thr, rep->loop[i].stk);<br>
+ }<br>
+ Printf("==============================\n");<br>
+}<br>
+<br>
+void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) {<br>
+ if (thr->in_symbolizer)<br>
+ return;<br>
+ Mutex *mtx = FindMutex(thr, m);<br>
+ DDReport *rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &mtx->dd,<br>
+ writelock, trylock);<br>
+ if (rep)<br>
+ ReportDeadlock(thr, rep);<br>
+}<br>
+<br>
+void MutexUnlock(Thread *thr, uptr m, bool writelock) {<br>
+ if (thr->in_symbolizer)<br>
+ return;<br>
+ Mutex *mtx = FindMutex(thr, m);<br>
+ ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &mtx->dd, writelock);<br>
+}<br>
+<br>
+void MutexDestroy(Thread *thr, uptr m) {<br>
+ if (thr->in_symbolizer)<br>
+ return;<br>
+ Mutex *mtx = FindMutexAndRemove(m);<br>
+ if (mtx == 0)<br>
+ return;<br>
+ ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &mtx->dd);<br>
+ InternalFree(mtx);<br>
+}<br>
+<br>
+} // namespace __dsan<br>
+<br>
+__attribute__((section(".preinit_array"), used))<br>
+void (*__local_dsan_preinit)(void) = __dsan::Initialize;<br>
+//===-- dd_interceptors.cc ------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "dd_rtl.h"<br>
+#include "interception/interception.h"<br>
+#include <pthread.h><br>
+<br>
+using namespace __dsan;<br>
+<br>
+extern "C" void *__libc_malloc(uptr size);<br>
+extern "C" void __libc_free(void *ptr);<br>
+<br>
+static __thread Thread *thr;<br>
+<br>
+static void InitThread() {<br>
+ if (thr != 0)<br>
+ return;<br>
+ thr = (Thread*)InternalAlloc(sizeof(*thr));<br>
+ internal_memset(thr, 0, sizeof(*thr));<br>
+ ThreadInit(thr);<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ int res = REAL(pthread_mutex_destroy)(m);<br>
+ MutexDestroy(thr, (uptr)m);<br>
+ return res;<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ int res = REAL(pthread_mutex_lock)(m);<br>
+ if (res == 0)<br>
+ MutexLock(thr, (uptr)m, true, false);<br>
+ return res;<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ int res = REAL(pthread_mutex_trylock)(m);<br>
+ if (res == 0)<br>
+ MutexLock(thr, (uptr)m, true, true);<br>
+ return res;<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ MutexUnlock(thr, (uptr)m, true);<br>
+ int res = REAL(pthread_mutex_unlock)(m);<br>
+ return res;<br>
+}<br>
+<br>
+namespace __dsan {<br>
+<br>
+void InitializeInterceptors() {<br>
+ INTERCEPT_FUNCTION(pthread_mutex_destroy);<br>
+ INTERCEPT_FUNCTION(pthread_mutex_lock);<br>
+ INTERCEPT_FUNCTION(pthread_mutex_trylock);<br>
+ INTERCEPT_FUNCTION(pthread_mutex_unlock);<br>
+}<br>
+<br>
+} // namespace __dsan<br>
+//===-- sanitizer_allocator.cc --------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+// This allocator is used inside run-times.<br>
+//===----------------------------------------------------------------------===//<br>
+#include "sanitizer_allocator.h"<br>
+#include "sanitizer_allocator_internal.h"<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+// ThreadSanitizer for Go uses libc malloc/free.<br>
+#if defined(SANITIZER_GO) || defined(SANITIZER_USE_MALLOC)<br>
+# if SANITIZER_LINUX && !SANITIZER_ANDROID<br>
+extern "C" void *__libc_malloc(uptr size);<br>
+extern "C" void __libc_free(void *ptr);<br>
+# define LIBC_MALLOC __libc_malloc<br>
+# define LIBC_FREE __libc_free<br>
+# else<br>
+# include <stdlib.h><br>
+# define LIBC_MALLOC malloc<br>
+# define LIBC_FREE free<br>
+# endif<br>
+<br>
+static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) {<br>
+ (void)cache;<br>
+ return LIBC_MALLOC(size);<br>
+}<br>
+<br>
+static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {<br>
+ (void)cache;<br>
+ LIBC_FREE(ptr);<br>
+}<br>
+<br>
+InternalAllocator *internal_allocator() {<br>
+ return 0;<br>
+}<br>
+<br>
+#else // SANITIZER_GO<br>
+<br>
+static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];<br>
+static atomic_uint8_t internal_allocator_initialized;<br>
+static StaticSpinMutex internal_alloc_init_mu;<br>
+<br>
+static InternalAllocatorCache internal_allocator_cache;<br>
+static StaticSpinMutex internal_allocator_cache_mu;<br>
+<br>
+InternalAllocator *internal_allocator() {<br>
+ InternalAllocator *internal_allocator_instance =<br>
+ reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder);<br>
+ if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) {<br>
+ SpinMutexLock l(&internal_alloc_init_mu);<br>
+ if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==<br>
+ 0) {<br>
+ internal_allocator_instance->Init();<br>
+ atomic_store(&internal_allocator_initialized, 1, memory_order_release);<br>
+ }<br>
+ }<br>
+ return internal_allocator_instance;<br>
+}<br>
+<br>
+static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) {<br>
+ if (cache == 0) {<br>
+ SpinMutexLock l(&internal_allocator_cache_mu);<br>
+ return internal_allocator()->Allocate(&internal_allocator_cache, size, 8,<br>
+ false);<br>
+ }<br>
+ return internal_allocator()->Allocate(cache, size, 8, false);<br>
+}<br>
+<br>
+static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {<br>
+ if (cache == 0) {<br>
+ SpinMutexLock l(&internal_allocator_cache_mu);<br>
+ return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);<br>
+ }<br>
+ internal_allocator()->Deallocate(cache, ptr);<br>
+}<br>
+<br>
+#endif // SANITIZER_GO<br>
+<br>
+const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;<br>
+<br>
+void *InternalAlloc(uptr size, InternalAllocatorCache *cache) {<br>
+ if (size + sizeof(u64) < size)<br>
+ return 0;<br>
+ void *p = RawInternalAlloc(size + sizeof(u64), cache);<br>
+ if (p == 0)<br>
+ return 0;<br>
+ ((u64*)p)[0] = kBlockMagic;<br>
+ return (char*)p + sizeof(u64);<br>
+}<br>
+<br>
+void InternalFree(void *addr, InternalAllocatorCache *cache) {<br>
+ if (addr == 0)<br>
+ return;<br>
+ addr = (char*)addr - sizeof(u64);<br>
+ CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);<br>
+ ((u64*)addr)[0] = 0;<br>
+ RawInternalFree(addr, cache);<br>
+}<br>
+<br>
+// LowLevelAllocator<br>
+static LowLevelAllocateCallback low_level_alloc_callback;<br>
+<br>
+void *LowLevelAllocator::Allocate(uptr size) {<br>
+ // Align allocation size.<br>
+ size = RoundUpTo(size, 8);<br>
+ if (allocated_end_ - allocated_current_ < (sptr)size) {<br>
+ uptr size_to_allocate = Max(size, GetPageSizeCached());<br>
+ allocated_current_ =<br>
+ (char*)MmapOrDie(size_to_allocate, __func__);<br>
+ allocated_end_ = allocated_current_ + size_to_allocate;<br>
+ if (low_level_alloc_callback) {<br>
+ low_level_alloc_callback((uptr)allocated_current_,<br>
+ size_to_allocate);<br>
+ }<br>
+ }<br>
+ CHECK(allocated_end_ - allocated_current_ >= (sptr)size);<br>
+ void *res = allocated_current_;<br>
+ allocated_current_ += size;<br>
+ return res;<br>
+}<br>
+<br>
+void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {<br>
+ low_level_alloc_callback = callback;<br>
+}<br>
+<br>
+bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) {<br>
+ if (!size) return false;<br>
+ uptr max = (uptr)-1L;<br>
+ return (max / size) < n;<br>
+}<br>
+<br>
+void *AllocatorReturnNull() {<br>
+ if (common_flags()->allocator_may_return_null)<br>
+ return 0;<br>
+ Report("%s's allocator is terminating the process instead of returning 0\n",<br>
+ SanitizerToolName);<br>
+ Report("If you don't like this behavior set allocator_may_return_null=1\n");<br>
+ CHECK(0);<br>
+ return 0;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_common.cc -----------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_libc.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+const char *SanitizerToolName = "SanitizerTool";<br>
+<br>
+uptr GetPageSizeCached() {<br>
+ static uptr PageSize;<br>
+ if (!PageSize)<br>
+ PageSize = GetPageSize();<br>
+ return PageSize;<br>
+}<br>
+<br>
+<br>
+// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid|<br>
+// isn't equal to the current PID, try to obtain file descriptor by opening<br>
+// file "report_path_prefix.<PID>".<br>
+fd_t report_fd = kStderrFd;<br>
+<br>
+// Set via __sanitizer_set_report_path.<br>
+bool log_to_file = false;<br>
+char report_path_prefix[sizeof(report_path_prefix)];<br>
+<br>
+// PID of process that opened |report_fd|. If a fork() occurs, the PID of the<br>
+// child thread will be different from |report_fd_pid|.<br>
+uptr report_fd_pid = 0;<br>
+<br>
+// PID of the tracer task in StopTheWorld. It shares the address space with the<br>
+// main process, but has a different PID and thus requires special handling.<br>
+uptr stoptheworld_tracer_pid = 0;<br>
+// Cached pid of parent process - if the parent process dies, we want to keep<br>
+// writing to the same log file.<br>
+uptr stoptheworld_tracer_ppid = 0;<br>
+<br>
+static DieCallbackType DieCallback;<br>
+void SetDieCallback(DieCallbackType callback) {<br>
+ DieCallback = callback;<br>
+}<br>
+<br>
+DieCallbackType GetDieCallback() {<br>
+ return DieCallback;<br>
+}<br>
+<br>
+void NORETURN Die() {<br>
+ if (DieCallback) {<br>
+ DieCallback();<br>
+ }<br>
+ internal__exit(1);<br>
+}<br>
+<br>
+static CheckFailedCallbackType CheckFailedCallback;<br>
+void SetCheckFailedCallback(CheckFailedCallbackType callback) {<br>
+ CheckFailedCallback = callback;<br>
+}<br>
+<br>
+void NORETURN CheckFailed(const char *file, int line, const char *cond,<br>
+ u64 v1, u64 v2) {<br>
+ if (CheckFailedCallback) {<br>
+ CheckFailedCallback(file, line, cond, v1, v2);<br>
+ }<br>
+ Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,<br>
+ v1, v2);<br>
+ Die();<br>
+}<br>
+<br>
+uptr ReadFileToBuffer(const char *file_name, char **buff,<br>
+ uptr *buff_size, uptr max_len) {<br>
+ uptr PageSize = GetPageSizeCached();<br>
+ uptr kMinFileLen = PageSize;<br>
+ uptr read_len = 0;<br>
+ *buff = 0;<br>
+ *buff_size = 0;<br>
+ // The files we usually open are not seekable, so try different buffer sizes.<br>
+ for (uptr size = kMinFileLen; size <= max_len; size *= 2) {<br>
+ uptr openrv = OpenFile(file_name, /*write*/ false);<br>
+ if (internal_iserror(openrv)) return 0;<br>
+ fd_t fd = openrv;<br>
+ UnmapOrDie(*buff, *buff_size);<br>
+ *buff = (char*)MmapOrDie(size, __func__);<br>
+ *buff_size = size;<br>
+ // Read up to one page at a time.<br>
+ read_len = 0;<br>
+ bool reached_eof = false;<br>
+ while (read_len + PageSize <= size) {<br>
+ uptr just_read = internal_read(fd, *buff + read_len, PageSize);<br>
+ if (just_read == 0) {<br>
+ reached_eof = true;<br>
+ break;<br>
+ }<br>
+ read_len += just_read;<br>
+ }<br>
+ internal_close(fd);<br>
+ if (reached_eof) // We've read the whole file.<br>
+ break;<br>
+ }<br>
+ return read_len;<br>
+}<br>
+<br>
+typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);<br>
+<br>
+template<class T><br>
+static inline bool CompareLess(const T &a, const T &b) {<br>
+ return a < b;<br>
+}<br>
+<br>
+void SortArray(uptr *array, uptr size) {<br>
+ InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);<br>
+}<br>
+<br>
+// We want to map a chunk of address space aligned to 'alignment'.<br>
+// We do it by maping a bit more and then unmaping redundant pieces.<br>
+// We probably can do it with fewer syscalls in some OS-dependent way.<br>
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {<br>
+// uptr PageSize = GetPageSizeCached();<br>
+ CHECK(IsPowerOfTwo(size));<br>
+ CHECK(IsPowerOfTwo(alignment));<br>
+ uptr map_size = size + alignment;<br>
+ uptr map_res = (uptr)MmapOrDie(map_size, mem_type);<br>
+ uptr map_end = map_res + map_size;<br>
+ uptr res = map_res;<br>
+ if (res & (alignment - 1)) // Not aligned.<br>
+ res = (map_res + alignment) & ~(alignment - 1);<br>
+ uptr end = res + size;<br>
+ if (res != map_res)<br>
+ UnmapOrDie((void*)map_res, res - map_res);<br>
+ if (end != map_end)<br>
+ UnmapOrDie((void*)end, map_end - end);<br>
+ return (void*)res;<br>
+}<br>
+<br>
+const char *StripPathPrefix(const char *filepath,<br>
+ const char *strip_path_prefix) {<br>
+ if (filepath == 0) return 0;<br>
+ if (strip_path_prefix == 0) return filepath;<br>
+ const char *pos = internal_strstr(filepath, strip_path_prefix);<br>
+ if (pos == 0) return filepath;<br>
+ pos += internal_strlen(strip_path_prefix);<br>
+ if (pos[0] == '.' && pos[1] == '/')<br>
+ pos += 2;<br>
+ return pos;<br>
+}<br>
+<br>
+void PrintSourceLocation(InternalScopedString *buffer, const char *file,<br>
+ int line, int column) {<br>
+ CHECK(file);<br>
+ buffer->append("%s",<br>
+ StripPathPrefix(file, common_flags()->strip_path_prefix));<br>
+ if (line > 0) {<br>
+ buffer->append(":%d", line);<br>
+ if (column > 0)<br>
+ buffer->append(":%d", column);<br>
+ }<br>
+}<br>
+<br>
+void PrintModuleAndOffset(InternalScopedString *buffer, const char *module,<br>
+ uptr offset) {<br>
+ buffer->append("(%s+0x%zx)",<br>
+ StripPathPrefix(module, common_flags()->strip_path_prefix),<br>
+ offset);<br>
+}<br>
+<br>
+void ReportErrorSummary(const char *error_message) {<br>
+ if (!common_flags()->print_summary)<br>
+ return;<br>
+ InternalScopedBuffer<char> buff(kMaxSummaryLength);<br>
+ internal_snprintf(buff.data(), buff.size(),<br>
+ "SUMMARY: %s: %s", SanitizerToolName, error_message);<br>
+ __sanitizer_report_error_summary(buff.data());<br>
+}<br>
+<br>
+void ReportErrorSummary(const char *error_type, const char *file,<br>
+ int line, const char *function) {<br>
+ if (!common_flags()->print_summary)<br>
+ return;<br>
+ InternalScopedBuffer<char> buff(kMaxSummaryLength);<br>
+ internal_snprintf(<br>
+ buff.data(), buff.size(), "%s %s:%d %s", error_type,<br>
+ file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??",<br>
+ line, function ? function : "??");<br>
+ ReportErrorSummary(buff.data());<br>
+}<br>
+<br>
+void ReportErrorSummary(const char *error_type, StackTrace *stack) {<br>
+ if (!common_flags()->print_summary)<br>
+ return;<br>
+ AddressInfo ai;<br>
+#if !SANITIZER_GO<br>
+ if (stack->size > 0 && Symbolizer::Get()->CanReturnFileLineInfo()) {<br>
+ // Currently, we include the first stack frame into the report summary.<br>
+ // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).<br>
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);<br>
+ Symbolizer::Get()->SymbolizePC(pc, &ai, 1);<br>
+ }<br>
+#endif<br>
+ ReportErrorSummary(error_type, ai.file, ai.line, ai.function);<br>
+}<br>
+<br>
+LoadedModule::LoadedModule(const char *module_name, uptr base_address) {<br>
+ full_name_ = internal_strdup(module_name);<br>
+ base_address_ = base_address;<br>
+ n_ranges_ = 0;<br>
+}<br>
+<br>
+void LoadedModule::addAddressRange(uptr beg, uptr end) {<br>
+ CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);<br>
+ ranges_[n_ranges_].beg = beg;<br>
+ ranges_[n_ranges_].end = end;<br>
+ n_ranges_++;<br>
+}<br>
+<br>
+bool LoadedModule::containsAddress(uptr address) const {<br>
+ for (uptr i = 0; i < n_ranges_; i++) {<br>
+ if (ranges_[i].beg <= address && address < ranges_[i].end)<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+char *StripModuleName(const char *module) {<br>
+ if (module == 0)<br>
+ return 0;<br>
+ const char *short_module_name = internal_strrchr(module, '/');<br>
+ if (short_module_name)<br>
+ short_module_name += 1;<br>
+ else<br>
+ short_module_name = module;<br>
+ return internal_strdup(short_module_name);<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+using namespace __sanitizer; // NOLINT<br>
+<br>
+extern "C" {<br>
+void __sanitizer_set_report_path(const char *path) {<br>
+ if (!path)<br>
+ return;<br>
+ uptr len = internal_strlen(path);<br>
+ if (len > sizeof(report_path_prefix) - 100) {<br>
+ Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",<br>
+ path[0], path[1], path[2], path[3],<br>
+ path[4], path[5], path[6], path[7]);<br>
+ Die();<br>
+ }<br>
+ if (report_fd != kStdoutFd &&<br>
+ report_fd != kStderrFd &&<br>
+ report_fd != kInvalidFd)<br>
+ internal_close(report_fd);<br>
+ report_fd = kInvalidFd;<br>
+ log_to_file = false;<br>
+ if (internal_strcmp(path, "stdout") == 0) {<br>
+ report_fd = kStdoutFd;<br>
+ } else if (internal_strcmp(path, "stderr") == 0) {<br>
+ report_fd = kStderrFd;<br>
+ } else {<br>
+ internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix));<br>
+ report_path_prefix[len] = '\0';<br>
+ log_to_file = true;<br>
+ }<br>
+}<br>
+<br>
+void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) {<br>
+ (void)reserved;<br>
+ PrepareForSandboxing();<br>
+}<br>
+<br>
+void __sanitizer_report_error_summary(const char *error_summary) {<br>
+ Printf("%s\n", error_summary);<br>
+}<br>
+} // extern "C"<br>
+//===-- sanitizer_deadlock_detector1.cc -----------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// Deadlock detector implementation based on NxN adjacency bit matrix.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_deadlock_detector_interface.h"<br>
+#include "sanitizer_deadlock_detector.h"<br>
+#include "sanitizer_allocator_internal.h"<br>
+#include "sanitizer_placement_new.h"<br>
+#include "sanitizer_mutex.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector.<br>
+<br>
+struct DDPhysicalThread {<br>
+};<br>
+<br>
+struct DDLogicalThread {<br>
+ u64 ctx;<br>
+ DeadlockDetectorTLS<DDBV> dd;<br>
+ DDReport rep;<br>
+};<br>
+<br>
+struct DDetectorImpl : public DDetector {<br>
+ SpinMutex mtx;<br>
+ DeadlockDetector<DDBV> dd;<br>
+<br>
+ DDetectorImpl();<br>
+<br>
+ virtual DDPhysicalThread* CreatePhysicalThread();<br>
+ virtual void DestroyPhysicalThread(DDPhysicalThread *pt);<br>
+<br>
+ virtual DDLogicalThread* CreateLogicalThread(u64 ctx);<br>
+ virtual void DestroyLogicalThread(DDLogicalThread *lt);<br>
+<br>
+ virtual void MutexInit(DDMutex *m, u32 stk, u64 ctx);<br>
+ virtual DDReport *MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt,<br>
+ DDMutex *m, bool writelock, bool trylock);<br>
+ virtual DDReport *MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt,<br>
+ DDMutex *m, bool writelock);<br>
+ virtual void MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt,<br>
+ DDMutex *m);<br>
+<br>
+ void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);<br>
+};<br>
+<br>
+DDetector *DDetector::Create() {<br>
+ void *mem = MmapOrDie(sizeof(DDetectorImpl), "deadlock detector");<br>
+ return new(mem) DDetectorImpl();<br>
+}<br>
+<br>
+DDetectorImpl::DDetectorImpl() {<br>
+ dd.clear();<br>
+}<br>
+<br>
+DDPhysicalThread* DDetectorImpl::CreatePhysicalThread() {<br>
+ return 0;<br>
+}<br>
+<br>
+void DDetectorImpl::DestroyPhysicalThread(DDPhysicalThread *pt) {<br>
+}<br>
+<br>
+DDLogicalThread* DDetectorImpl::CreateLogicalThread(u64 ctx) {<br>
+ DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));<br>
+ lt->ctx = ctx;<br>
+ lt->dd.clear();<br>
+ return lt;<br>
+}<br>
+<br>
+void DDetectorImpl::DestroyLogicalThread(DDLogicalThread *lt) {<br>
+ lt->~DDLogicalThread();<br>
+ InternalFree(lt);<br>
+}<br>
+<br>
+void DDetectorImpl::MutexInit(DDMutex *m, u32 stk, u64 ctx) {<br>
+ m->id = 0;<br>
+ m->stk = stk;<br>
+ m->ctx = ctx;<br>
+}<br>
+<br>
+void DDetectorImpl::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {<br>
+ if (!dd.nodeBelongsToCurrentEpoch(m->id))<br>
+ m->id = dd.newNode(reinterpret_cast<uptr>(m));<br>
+ dd.ensureCurrentEpoch(<->dd);<br>
+}<br>
+<br>
+DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt,<br>
+ DDMutex *m, bool writelock, bool trylock) {<br>
+ if (dd.onFirstLock(<->dd, m->id))<br>
+ return 0;<br>
+ SpinMutexLock lk(&mtx);<br>
+ MutexEnsureID(lt, m);<br>
+ CHECK(!dd.isHeld(<->dd, m->id));<br>
+ // Printf("T%d MutexLock: %zx\n", thr->tid, s->deadlock_detector_id);<br>
+ bool has_deadlock = trylock<br>
+ ? dd.onTryLock(<->dd, m->id)<br>
+ : dd.onLock(<->dd, m->id);<br>
+ DDReport *rep = 0;<br>
+ if (has_deadlock) {<br>
+ uptr path[10];<br>
+ uptr len = dd.findPathToHeldLock(<->dd, m->id,<br>
+ path, ARRAY_SIZE(path));<br>
+ CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that.<br>
+ rep = <->rep;<br>
+ rep->n = len;<br>
+ for (uptr i = 0; i < len; i++) {<br>
+ DDMutex *m0 = (DDMutex*)dd.getData(path[i]);<br>
+ DDMutex *m1 = (DDMutex*)dd.getData(path[i < len - 1 ? i + 1 : 0]);<br>
+ rep->loop[i].thr_ctx = 0; // don't know<br>
+ rep->loop[i].mtx_ctx0 = m0->ctx;<br>
+ rep->loop[i].mtx_ctx1 = m1->ctx;<br>
+ rep->loop[i].stk = m0->stk;<br>
+ }<br>
+ }<br>
+ return rep;<br>
+}<br>
+<br>
+DDReport *DDetectorImpl::MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt,<br>
+ DDMutex *m, bool writelock) {<br>
+ // Printf("T%d MutexUnlock: %zx; recursion %d\n", thr->tid,<br>
+ // s->deadlock_detector_id, s->recursion);<br>
+ dd.onUnlock(<->dd, m->id);<br>
+ return 0;<br>
+}<br>
+<br>
+void DDetectorImpl::MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt,<br>
+ DDMutex *m) {<br>
+ if (!m->id) return;<br>
+ SpinMutexLock lk(&mtx);<br>
+ if (dd.nodeBelongsToCurrentEpoch(m->id))<br>
+ dd.removeNode(m->id);<br>
+ m->id = 0;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_flags.cc ------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_flags.h"<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_libc.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+CommonFlags common_flags_dont_use;<br>
+<br>
+void SetCommonFlagsDefaults(CommonFlags *f) {<br>
+ f->symbolize = true;<br>
+ f->external_symbolizer_path = 0;<br>
+ f->allow_addr2line = false;<br>
+ f->strip_path_prefix = "";<br>
+ f->fast_unwind_on_fatal = false;<br>
+ f->fast_unwind_on_malloc = true;<br>
+ f->handle_ioctl = false;<br>
+ f->malloc_context_size = 1;<br>
+ f->log_path = "stderr";<br>
+ f->verbosity = 0;<br>
+ f->detect_leaks = false;<br>
+ f->leak_check_at_exit = true;<br>
+ f->allocator_may_return_null = false;<br>
+ f->print_summary = true;<br>
+ f->check_printf = false;<br>
+ // TODO(glider): tools may want to set different defaults for handle_segv.<br>
+ f->handle_segv = SANITIZER_NEEDS_SEGV;<br>
+ f->allow_user_segv_handler = false;<br>
+ f->use_sigaltstack = false;<br>
+ f->detect_deadlocks = false;<br>
+ f->clear_shadow_mmap_threshold = 64 * 1024;<br>
+ f->color = "auto";<br>
+}<br>
+<br>
+void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {<br>
+ ParseFlag(str, &f->symbolize, "symbolize");<br>
+ ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path");<br>
+ ParseFlag(str, &f->allow_addr2line, "allow_addr2line");<br>
+ ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");<br>
+ ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal");<br>
+ ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc");<br>
+ ParseFlag(str, &f->handle_ioctl, "handle_ioctl");<br>
+ ParseFlag(str, &f->malloc_context_size, "malloc_context_size");<br>
+ ParseFlag(str, &f->log_path, "log_path");<br>
+ ParseFlag(str, &f->verbosity, "verbosity");<br>
+ ParseFlag(str, &f->detect_leaks, "detect_leaks");<br>
+ ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit");<br>
+ ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null");<br>
+ ParseFlag(str, &f->print_summary, "print_summary");<br>
+ ParseFlag(str, &f->check_printf, "check_printf");<br>
+ ParseFlag(str, &f->handle_segv, "handle_segv");<br>
+ ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler");<br>
+ ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack");<br>
+ ParseFlag(str, &f->detect_deadlocks, "detect_deadlocks");<br>
+ ParseFlag(str, &f->clear_shadow_mmap_threshold,<br>
+ "clear_shadow_mmap_threshold");<br>
+ ParseFlag(str, &f->color, "color");<br>
+<br>
+ // Do a sanity check for certain flags.<br>
+ if (f->malloc_context_size < 1)<br>
+ f->malloc_context_size = 1;<br>
+}<br>
+<br>
+static bool GetFlagValue(const char *env, const char *name,<br>
+ const char **value, int *value_length) {<br>
+ if (env == 0)<br>
+ return false;<br>
+ const char *pos = 0;<br>
+ for (;;) {<br>
+ pos = internal_strstr(env, name);<br>
+ if (pos == 0)<br>
+ return false;<br>
+ if (pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) {<br>
+ // Seems to be middle of another flag name or value.<br>
+ env = pos + 1;<br>
+ continue;<br>
+ }<br>
+ break;<br>
+ }<br>
+ pos += internal_strlen(name);<br>
+ const char *end;<br>
+ if (pos[0] != '=') {<br>
+ end = pos;<br>
+ } else {<br>
+ pos += 1;<br>
+ if (pos[0] == '"') {<br>
+ pos += 1;<br>
+ end = internal_strchr(pos, '"');<br>
+ } else if (pos[0] == '\'') {<br>
+ pos += 1;<br>
+ end = internal_strchr(pos, '\'');<br>
+ } else {<br>
+ // Read until the next space or colon.<br>
+ end = pos + internal_strcspn(pos, " :");<br>
+ }<br>
+ if (end == 0)<br>
+ end = pos + internal_strlen(pos);<br>
+ }<br>
+ *value = pos;<br>
+ *value_length = end - pos;<br>
+ return true;<br>
+}<br>
+<br>
+static bool StartsWith(const char *flag, int flag_length, const char *value) {<br>
+ if (!flag || !value)<br>
+ return false;<br>
+ int value_length = internal_strlen(value);<br>
+ return (flag_length >= value_length) &&<br>
+ (0 == internal_strncmp(flag, value, value_length));<br>
+}<br>
+<br>
+void ParseFlag(const char *env, bool *flag, const char *name) {<br>
+ const char *value;<br>
+ int value_length;<br>
+ if (!GetFlagValue(env, name, &value, &value_length))<br>
+ return;<br>
+ if (StartsWith(value, value_length, "0") ||<br>
+ StartsWith(value, value_length, "no") ||<br>
+ StartsWith(value, value_length, "false"))<br>
+ *flag = false;<br>
+ if (StartsWith(value, value_length, "1") ||<br>
+ StartsWith(value, value_length, "yes") ||<br>
+ StartsWith(value, value_length, "true"))<br>
+ *flag = true;<br>
+}<br>
+<br>
+void ParseFlag(const char *env, int *flag, const char *name) {<br>
+ const char *value;<br>
+ int value_length;<br>
+ if (!GetFlagValue(env, name, &value, &value_length))<br>
+ return;<br>
+ *flag = static_cast<int>(internal_atoll(value));<br>
+}<br>
+<br>
+void ParseFlag(const char *env, uptr *flag, const char *name) {<br>
+ const char *value;<br>
+ int value_length;<br>
+ if (!GetFlagValue(env, name, &value, &value_length))<br>
+ return;<br>
+ *flag = static_cast<uptr>(internal_atoll(value));<br>
+}<br>
+<br>
+static LowLevelAllocator allocator_for_flags;<br>
+<br>
+void ParseFlag(const char *env, const char **flag, const char *name) {<br>
+ const char *value;<br>
+ int value_length;<br>
+ if (!GetFlagValue(env, name, &value, &value_length))<br>
+ return;<br>
+ // Copy the flag value. Don't use locks here, as flags are parsed at<br>
+ // tool startup.<br>
+ char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1));<br>
+ internal_memcpy(value_copy, value, value_length);<br>
+ value_copy[value_length] = '\0';<br>
+ *flag = value_copy;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_libc.cc -------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries. See sanitizer_libc.h for details.<br>
+//===----------------------------------------------------------------------===//<br>
+#include "sanitizer_allocator_internal.h"<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_libc.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+// Make the compiler think that something is going on there.<br>
+static inline void break_optimization(void *arg) {<br>
+#if _MSC_VER<br>
+ // FIXME: make sure this is actually enough.<br>
+ __asm;<br>
+#else<br>
+ __asm__ __volatile__("" : : "r" (arg) : "memory");<br>
+#endif<br>
+}<br>
+<br>
+s64 internal_atoll(const char *nptr) {<br>
+ return internal_simple_strtoll(nptr, (char**)0, 10);<br>
+}<br>
+<br>
+void *internal_memchr(const void *s, int c, uptr n) {<br>
+ const char* t = (char*)s;<br>
+ for (uptr i = 0; i < n; ++i, ++t)<br>
+ if (*t == c)<br>
+ return (void*)t;<br>
+ return 0;<br>
+}<br>
+<br>
+int internal_memcmp(const void* s1, const void* s2, uptr n) {<br>
+ const char* t1 = (char*)s1;<br>
+ const char* t2 = (char*)s2;<br>
+ for (uptr i = 0; i < n; ++i, ++t1, ++t2)<br>
+ if (*t1 != *t2)<br>
+ return *t1 < *t2 ? -1 : 1;<br>
+ return 0;<br>
+}<br>
+<br>
+void *internal_memcpy(void *dest, const void *src, uptr n) {<br>
+ char *d = (char*)dest;<br>
+ char *s = (char*)src;<br>
+ for (uptr i = 0; i < n; ++i)<br>
+ d[i] = s[i];<br>
+ return dest;<br>
+}<br>
+<br>
+void *internal_memmove(void *dest, const void *src, uptr n) {<br>
+ char *d = (char*)dest;<br>
+ char *s = (char*)src;<br>
+ sptr i, signed_n = (sptr)n;<br>
+ CHECK_GE(signed_n, 0);<br>
+ if (d < s) {<br>
+ for (i = 0; i < signed_n; ++i)<br>
+ d[i] = s[i];<br>
+ } else {<br>
+ if (d > s && signed_n > 0)<br>
+ for (i = signed_n - 1; i >= 0 ; --i) {<br>
+ d[i] = s[i];<br>
+ }<br>
+ }<br>
+ return dest;<br>
+}<br>
+<br>
+// Semi-fast bzero for 16-aligned data. Still far from peak performance.<br>
+void internal_bzero_aligned16(void *s, uptr n) {<br>
+ struct S16 { u64 a, b; } ALIGNED(16);<br>
+ CHECK_EQ((reinterpret_cast<uptr>(s) | n) & 15, 0);<br>
+ for (S16 *p = reinterpret_cast<S16*>(s), *end = p + n / 16; p < end; p++) {<br>
+ p->a = p->b = 0;<br>
+ break_optimization(0); // Make sure this does not become memset.<br>
+ }<br>
+}<br>
+<br>
+void *internal_memset(void* s, int c, uptr n) {<br>
+ // The next line prevents Clang from making a call to memset() instead of the<br>
+ // loop below.<br>
+ // FIXME: building the runtime with -ffreestanding is a better idea. However<br>
+ // there currently are linktime problems due to PR12396.<br>
+ char volatile *t = (char*)s;<br>
+ for (uptr i = 0; i < n; ++i, ++t) {<br>
+ *t = c;<br>
+ }<br>
+ return s;<br>
+}<br>
+<br>
+uptr internal_strcspn(const char *s, const char *reject) {<br>
+ uptr i;<br>
+ for (i = 0; s[i]; i++) {<br>
+ if (internal_strchr(reject, s[i]) != 0)<br>
+ return i;<br>
+ }<br>
+ return i;<br>
+}<br>
+<br>
+char* internal_strdup(const char *s) {<br>
+ uptr len = internal_strlen(s);<br>
+ char *s2 = (char*)InternalAlloc(len + 1);<br>
+ internal_memcpy(s2, s, len);<br>
+ s2[len] = 0;<br>
+ return s2;<br>
+}<br>
+<br>
+int internal_strcmp(const char *s1, const char *s2) {<br>
+ while (true) {<br>
+ unsigned c1 = *s1;<br>
+ unsigned c2 = *s2;<br>
+ if (c1 != c2) return (c1 < c2) ? -1 : 1;<br>
+ if (c1 == 0) break;<br>
+ s1++;<br>
+ s2++;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+int internal_strncmp(const char *s1, const char *s2, uptr n) {<br>
+ for (uptr i = 0; i < n; i++) {<br>
+ unsigned c1 = *s1;<br>
+ unsigned c2 = *s2;<br>
+ if (c1 != c2) return (c1 < c2) ? -1 : 1;<br>
+ if (c1 == 0) break;<br>
+ s1++;<br>
+ s2++;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+char* internal_strchr(const char *s, int c) {<br>
+ while (true) {<br>
+ if (*s == (char)c)<br>
+ return (char*)s;<br>
+ if (*s == 0)<br>
+ return 0;<br>
+ s++;<br>
+ }<br>
+}<br>
+<br>
+char *internal_strchrnul(const char *s, int c) {<br>
+ char *res = internal_strchr(s, c);<br>
+ if (!res)<br>
+ res = (char*)s + internal_strlen(s);<br>
+ return res;<br>
+}<br>
+<br>
+char *internal_strrchr(const char *s, int c) {<br>
+ const char *res = 0;<br>
+ for (uptr i = 0; s[i]; i++) {<br>
+ if (s[i] == c) res = s + i;<br>
+ }<br>
+ return (char*)res;<br>
+}<br>
+<br>
+uptr internal_strlen(const char *s) {<br>
+ uptr i = 0;<br>
+ while (s[i]) i++;<br>
+ return i;<br>
+}<br>
+<br>
+char *internal_strncat(char *dst, const char *src, uptr n) {<br>
+ uptr len = internal_strlen(dst);<br>
+ uptr i;<br>
+ for (i = 0; i < n && src[i]; i++)<br>
+ dst[len + i] = src[i];<br>
+ dst[len + i] = 0;<br>
+ return dst;<br>
+}<br>
+<br>
+char *internal_strncpy(char *dst, const char *src, uptr n) {<br>
+ uptr i;<br>
+ for (i = 0; i < n && src[i]; i++)<br>
+ dst[i] = src[i];<br>
+ internal_memset(dst + i, '\0', n - i);<br>
+ return dst;<br>
+}<br>
+<br>
+uptr internal_strnlen(const char *s, uptr maxlen) {<br>
+ uptr i = 0;<br>
+ while (i < maxlen && s[i]) i++;<br>
+ return i;<br>
+}<br>
+<br>
+char *internal_strstr(const char *haystack, const char *needle) {<br>
+ // This is O(N^2), but we are not using it in hot places.<br>
+ uptr len1 = internal_strlen(haystack);<br>
+ uptr len2 = internal_strlen(needle);<br>
+ if (len1 < len2) return 0;<br>
+ for (uptr pos = 0; pos <= len1 - len2; pos++) {<br>
+ if (internal_memcmp(haystack + pos, needle, len2) == 0)<br>
+ return (char*)haystack + pos;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {<br>
+ CHECK_EQ(base, 10);<br>
+ while (IsSpace(*nptr)) nptr++;<br>
+ int sgn = 1;<br>
+ u64 res = 0;<br>
+ bool have_digits = false;<br>
+ char *old_nptr = (char*)nptr;<br>
+ if (*nptr == '+') {<br>
+ sgn = 1;<br>
+ nptr++;<br>
+ } else if (*nptr == '-') {<br>
+ sgn = -1;<br>
+ nptr++;<br>
+ }<br>
+ while (IsDigit(*nptr)) {<br>
+ res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX;<br>
+ int digit = ((*nptr) - '0');<br>
+ res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX;<br>
+ have_digits = true;<br>
+ nptr++;<br>
+ }<br>
+ if (endptr != 0) {<br>
+ *endptr = (have_digits) ? (char*)nptr : old_nptr;<br>
+ }<br>
+ if (sgn > 0) {<br>
+ return (s64)(Min((u64)INT64_MAX, res));<br>
+ } else {<br>
+ return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1);<br>
+ }<br>
+}<br>
+<br>
+bool mem_is_zero(const char *beg, uptr size) {<br>
+ CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check.<br>
+ const char *end = beg + size;<br>
+ uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));<br>
+ uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));<br>
+ uptr all = 0;<br>
+ // Prologue.<br>
+ for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)<br>
+ all |= *mem;<br>
+ // Aligned loop.<br>
+ for (; aligned_beg < aligned_end; aligned_beg++)<br>
+ all |= *aligned_beg;<br>
+ // Epilogue.<br>
+ if ((char*)aligned_end >= beg)<br>
+ for (const char *mem = (char*)aligned_end; mem < end; mem++)<br>
+ all |= *mem;<br>
+ return all == 0;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_printf.cc -----------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer.<br>
+//<br>
+// Internal printf function, used inside run-time libraries.<br>
+// We can't use libc printf because we intercept some of the functions used<br>
+// inside it.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_libc.h"<br>
+<br>
+#include <stdio.h><br>
+#include <stdarg.h><br>
+<br>
+#if SANITIZER_WINDOWS && !defined(va_copy)<br>
+# define va_copy(dst, src) ((dst) = (src))<br>
+#endif<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+StaticSpinMutex CommonSanitizerReportMutex;<br>
+<br>
+static int AppendChar(char **buff, const char *buff_end, char c) {<br>
+ if (*buff < buff_end) {<br>
+ **buff = c;<br>
+ (*buff)++;<br>
+ }<br>
+ return 1;<br>
+}<br>
+<br>
+// Appends number in a given base to buffer. If its length is less than<br>
+// |minimal_num_length|, it is padded with leading zeroes or spaces, depending<br>
+// on the value of |pad_with_zero|.<br>
+static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,<br>
+ u8 base, u8 minimal_num_length, bool pad_with_zero,<br>
+ bool negative) {<br>
+ uptr const kMaxLen = 30;<br>
+ RAW_CHECK(base == 10 || base == 16);<br>
+ RAW_CHECK(base == 10 || !negative);<br>
+ RAW_CHECK(absolute_value || !negative);<br>
+ RAW_CHECK(minimal_num_length < kMaxLen);<br>
+ int result = 0;<br>
+ if (negative && minimal_num_length)<br>
+ --minimal_num_length;<br>
+ if (negative && pad_with_zero)<br>
+ result += AppendChar(buff, buff_end, '-');<br>
+ uptr num_buffer[kMaxLen];<br>
+ int pos = 0;<br>
+ do {<br>
+ RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");<br>
+ num_buffer[pos++] = absolute_value % base;<br>
+ absolute_value /= base;<br>
+ } while (absolute_value > 0);<br>
+ if (pos < minimal_num_length) {<br>
+ // Make sure compiler doesn't insert call to memset here.<br>
+ internal_memset(&num_buffer[pos], 0,<br>
+ sizeof(num_buffer[0]) * (minimal_num_length - pos));<br>
+ pos = minimal_num_length;<br>
+ }<br>
+ RAW_CHECK(pos > 0);<br>
+ pos--;<br>
+ for (; pos >= 0 && num_buffer[pos] == 0; pos--) {<br>
+ char c = (pad_with_zero || pos == 0) ? '0' : ' ';<br>
+ result += AppendChar(buff, buff_end, c);<br>
+ }<br>
+ if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');<br>
+ for (; pos >= 0; pos--) {<br>
+ char digit = static_cast<char>(num_buffer[pos]);<br>
+ result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit<br>
+ : 'a' + digit - 10);<br>
+ }<br>
+ return result;<br>
+}<br>
+<br>
+static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,<br>
+ u8 minimal_num_length, bool pad_with_zero) {<br>
+ return AppendNumber(buff, buff_end, num, base, minimal_num_length,<br>
+ pad_with_zero, false /* negative */);<br>
+}<br>
+<br>
+static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,<br>
+ u8 minimal_num_length, bool pad_with_zero) {<br>
+ bool negative = (num < 0);<br>
+ return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,<br>
+ minimal_num_length, pad_with_zero, negative);<br>
+}<br>
+<br>
+static int AppendString(char **buff, const char *buff_end, int precision,<br>
+ const char *s) {<br>
+ if (s == 0)<br>
+ s = "<null>";<br>
+ int result = 0;<br>
+ for (; *s; s++) {<br>
+ if (precision >= 0 && result >= precision)<br>
+ break;<br>
+ result += AppendChar(buff, buff_end, *s);<br>
+ }<br>
+ return result;<br>
+}<br>
+<br>
+static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {<br>
+ int result = 0;<br>
+ result += AppendString(buff, buff_end, -1, "0x");<br>
+ result += AppendUnsigned(buff, buff_end, ptr_value, 16,<br>
+ (SANITIZER_WORDSIZE == 64) ? 12 : 8, true);<br>
+ return result;<br>
+}<br>
+<br>
+int VSNPrintf(char *buff, int buff_length,<br>
+ const char *format, va_list args) {<br>
+ static const char *kPrintfFormatsHelp =<br>
+ "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n";<br>
+ RAW_CHECK(format);<br>
+ RAW_CHECK(buff_length > 0);<br>
+ const char *buff_end = &buff[buff_length - 1];<br>
+ const char *cur = format;<br>
+ int result = 0;<br>
+ for (; *cur; cur++) {<br>
+ if (*cur != '%') {<br>
+ result += AppendChar(&buff, buff_end, *cur);<br>
+ continue;<br>
+ }<br>
+ cur++;<br>
+ bool have_width = (*cur >= '0' && *cur <= '9');<br>
+ bool pad_with_zero = (*cur == '0');<br>
+ int width = 0;<br>
+ if (have_width) {<br>
+ while (*cur >= '0' && *cur <= '9') {<br>
+ width = width * 10 + *cur++ - '0';<br>
+ }<br>
+ }<br>
+ bool have_precision = (cur[0] == '.' && cur[1] == '*');<br>
+ int precision = -1;<br>
+ if (have_precision) {<br>
+ cur += 2;<br>
+ precision = va_arg(args, int);<br>
+ }<br>
+ bool have_z = (*cur == 'z');<br>
+ cur += have_z;<br>
+ bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');<br>
+ cur += have_ll * 2;<br>
+ s64 dval;<br>
+ u64 uval;<br>
+ bool have_flags = have_width | have_z | have_ll;<br>
+ // Only %s supports precision for now<br>
+ CHECK(!(precision >= 0 && *cur != 's'));<br>
+ switch (*cur) {<br>
+ case 'd': {<br>
+ dval = have_ll ? va_arg(args, s64)<br>
+ : have_z ? va_arg(args, sptr)<br>
+ : va_arg(args, int);<br>
+ result += AppendSignedDecimal(&buff, buff_end, dval, width,<br>
+ pad_with_zero);<br>
+ break;<br>
+ }<br>
+ case 'u':<br>
+ case 'x': {<br>
+ uval = have_ll ? va_arg(args, u64)<br>
+ : have_z ? va_arg(args, uptr)<br>
+ : va_arg(args, unsigned);<br>
+ result += AppendUnsigned(&buff, buff_end, uval,<br>
+ (*cur == 'u') ? 10 : 16, width, pad_with_zero);<br>
+ break;<br>
+ }<br>
+ case 'p': {<br>
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);<br>
+ result += AppendPointer(&buff, buff_end, va_arg(args, uptr));<br>
+ break;<br>
+ }<br>
+ case 's': {<br>
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);<br>
+ result += AppendString(&buff, buff_end, precision, va_arg(args, char*));<br>
+ break;<br>
+ }<br>
+ case 'c': {<br>
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);<br>
+ result += AppendChar(&buff, buff_end, va_arg(args, int));<br>
+ break;<br>
+ }<br>
+ case '%' : {<br>
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);<br>
+ result += AppendChar(&buff, buff_end, '%');<br>
+ break;<br>
+ }<br>
+ default: {<br>
+ RAW_CHECK_MSG(false, kPrintfFormatsHelp);<br>
+ }<br>
+ }<br>
+ }<br>
+ RAW_CHECK(buff <= buff_end);<br>
+ AppendChar(&buff, buff_end + 1, '\0');<br>
+ return result;<br>
+}<br>
+<br>
+static void (*PrintfAndReportCallback)(const char *);<br>
+void SetPrintfAndReportCallback(void (*callback)(const char *)) {<br>
+ PrintfAndReportCallback = callback;<br>
+}<br>
+<br>
+// Can be overriden in frontend.<br>
+#if SANITIZER_SUPPORTS_WEAK_HOOKS<br>
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE<br>
+void OnPrint(const char *str) {<br>
+ (void)str;<br>
+}<br>
+#elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS)<br>
+void OnPrint(const char *str);<br>
+#else<br>
+void OnPrint(const char *str) {<br>
+ (void)str;<br>
+}<br>
+#endif<br>
+<br>
+static void CallPrintfAndReportCallback(const char *str) {<br>
+ OnPrint(str);<br>
+ if (PrintfAndReportCallback)<br>
+ PrintfAndReportCallback(str);<br>
+}<br>
+<br>
+static void SharedPrintfCode(bool append_pid, const char *format,<br>
+ va_list args) {<br>
+ va_list args2;<br>
+ va_copy(args2, args);<br>
+ const int kLen = 16 * 1024;<br>
+ // |local_buffer| is small enough not to overflow the stack and/or violate<br>
+ // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other<br>
+ // hand, the bigger the buffer is, the more the chance the error report will<br>
+ // fit into it.<br>
+ char local_buffer[400];<br>
+ int needed_length;<br>
+ char *buffer = local_buffer;<br>
+ int buffer_size = ARRAY_SIZE(local_buffer);<br>
+ // First try to print a message using a local buffer, and then fall back to<br>
+ // mmaped buffer.<br>
+ for (int use_mmap = 0; use_mmap < 2; use_mmap++) {<br>
+ if (use_mmap) {<br>
+ va_end(args);<br>
+ va_copy(args, args2);<br>
+ buffer = (char*)MmapOrDie(kLen, "Report");<br>
+ buffer_size = kLen;<br>
+ }<br>
+ needed_length = 0;<br>
+ if (append_pid) {<br>
+ int pid = internal_getpid();<br>
+ needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid);<br>
+ if (needed_length >= buffer_size) {<br>
+ // The pid doesn't fit into the current buffer.<br>
+ if (!use_mmap)<br>
+ continue;<br>
+ RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");<br>
+ }<br>
+ }<br>
+ needed_length += VSNPrintf(buffer + needed_length,<br>
+ buffer_size - needed_length, format, args);<br>
+ if (needed_length >= buffer_size) {<br>
+ // The message doesn't fit into the current buffer.<br>
+ if (!use_mmap)<br>
+ continue;<br>
+ RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");<br>
+ }<br>
+ // If the message fit into the buffer, print it and exit.<br>
+ break;<br>
+ }<br>
+ RawWrite(buffer);<br>
+ AndroidLogWrite(buffer);<br>
+ CallPrintfAndReportCallback(buffer);<br>
+ // If we had mapped any memory, clean up.<br>
+ if (buffer != local_buffer)<br>
+ UnmapOrDie((void *)buffer, buffer_size);<br>
+ va_end(args2);<br>
+}<br>
+<br>
+FORMAT(1, 2)<br>
+void Printf(const char *format, ...) {<br>
+ va_list args;<br>
+ va_start(args, format);<br>
+ SharedPrintfCode(false, format, args);<br>
+ va_end(args);<br>
+}<br>
+<br>
+// Like Printf, but prints the current PID before the output string.<br>
+FORMAT(1, 2)<br>
+void Report(const char *format, ...) {<br>
+ va_list args;<br>
+ va_start(args, format);<br>
+ SharedPrintfCode(true, format, args);<br>
+ va_end(args);<br>
+}<br>
+<br>
+// Writes at most "length" symbols to "buffer" (including trailing '\0').<br>
+// Returns the number of symbols that should have been written to buffer<br>
+// (not including trailing '\0'). Thus, the string is truncated<br>
+// iff return value is not less than "length".<br>
+FORMAT(3, 4)<br>
+int internal_snprintf(char *buffer, uptr length, const char *format, ...) {<br>
+ va_list args;<br>
+ va_start(args, format);<br>
+ int needed_length = VSNPrintf(buffer, length, format, args);<br>
+ va_end(args);<br>
+ return needed_length;<br>
+}<br>
+<br>
+FORMAT(2, 3)<br>
+void InternalScopedString::append(const char *format, ...) {<br>
+ CHECK_LT(length_, size());<br>
+ va_list args;<br>
+ va_start(args, format);<br>
+ VSNPrintf(data() + length_, size() - length_, format, args);<br>
+ va_end(args);<br>
+ length_ += internal_strlen(data() + length_);<br>
+ CHECK_LT(length_, size());<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_suppressions.cc -----------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// Suppression parsing/matching code shared between TSan and LSan.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_suppressions.h"<br>
+<br>
+#include "sanitizer_allocator_internal.h"<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_libc.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+static const char *const kTypeStrings[SuppressionTypeCount] = {<br>
+ "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib"<br>
+};<br>
+<br>
+bool TemplateMatch(char *templ, const char *str) {<br>
+ if (str == 0 || str[0] == 0)<br>
+ return false;<br>
+ bool start = false;<br>
+ if (templ && templ[0] == '^') {<br>
+ start = true;<br>
+ templ++;<br>
+ }<br>
+ bool asterisk = false;<br>
+ while (templ && templ[0]) {<br>
+ if (templ[0] == '*') {<br>
+ templ++;<br>
+ start = false;<br>
+ asterisk = true;<br>
+ continue;<br>
+ }<br>
+ if (templ[0] == '$')<br>
+ return str[0] == 0 || asterisk;<br>
+ if (str[0] == 0)<br>
+ return false;<br>
+ char *tpos = (char*)internal_strchr(templ, '*');<br>
+ char *tpos1 = (char*)internal_strchr(templ, '$');<br>
+ if (tpos == 0 || (tpos1 && tpos1 < tpos))<br>
+ tpos = tpos1;<br>
+ if (tpos != 0)<br>
+ tpos[0] = 0;<br>
+ const char *str0 = str;<br>
+ const char *spos = internal_strstr(str, templ);<br>
+ str = spos + internal_strlen(templ);<br>
+ templ = tpos;<br>
+ if (tpos)<br>
+ tpos[0] = tpos == tpos1 ? '$' : '*';<br>
+ if (spos == 0)<br>
+ return false;<br>
+ if (start && spos != str0)<br>
+ return false;<br>
+ start = false;<br>
+ asterisk = false;<br>
+ }<br>
+ return true;<br>
+}<br>
+<br>
+bool SuppressionContext::Match(const char *str, SuppressionType type,<br>
+ Suppression **s) {<br>
+ can_parse_ = false;<br>
+ uptr i;<br>
+ for (i = 0; i < suppressions_.size(); i++)<br>
+ if (type == suppressions_[i].type &&<br>
+ TemplateMatch(suppressions_[i].templ, str))<br>
+ break;<br>
+ if (i == suppressions_.size()) return false;<br>
+ *s = &suppressions_[i];<br>
+ return true;<br>
+}<br>
+<br>
+static const char *StripPrefix(const char *str, const char *prefix) {<br>
+ while (str && *str == *prefix) {<br>
+ str++;<br>
+ prefix++;<br>
+ }<br>
+ if (!*prefix)<br>
+ return str;<br>
+ return 0;<br>
+}<br>
+<br>
+void SuppressionContext::Parse(const char *str) {<br>
+ // Context must not mutate once Match has been called.<br>
+ CHECK(can_parse_);<br>
+ const char *line = str;<br>
+ while (line) {<br>
+ while (line[0] == ' ' || line[0] == '\t')<br>
+ line++;<br>
+ const char *end = internal_strchr(line, '\n');<br>
+ if (end == 0)<br>
+ end = line + internal_strlen(line);<br>
+ if (line != end && line[0] != '#') {<br>
+ const char *end2 = end;<br>
+ while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))<br>
+ end2--;<br>
+ int type;<br>
+ for (type = 0; type < SuppressionTypeCount; type++) {<br>
+ const char *next_char = StripPrefix(line, kTypeStrings[type]);<br>
+ if (next_char && *next_char == ':') {<br>
+ line = ++next_char;<br>
+ break;<br>
+ }<br>
+ }<br>
+ if (type == SuppressionTypeCount) {<br>
+ Printf("%s: failed to parse suppressions\n", SanitizerToolName);<br>
+ Die();<br>
+ }<br>
+ Suppression s;<br>
+ s.type = static_cast<SuppressionType>(type);<br>
+ s.templ = (char*)InternalAlloc(end2 - line + 1);<br>
+ internal_memcpy(s.templ, line, end2 - line);<br>
+ s.templ[end2 - line] = 0;<br>
+ s.hit_count = 0;<br>
+ s.weight = 0;<br>
+ suppressions_.push_back(s);<br>
+ }<br>
+ if (end[0] == 0)<br>
+ break;<br>
+ line = end + 1;<br>
+ }<br>
+}<br>
+<br>
+uptr SuppressionContext::SuppressionCount() const {<br>
+ return suppressions_.size();<br>
+}<br>
+<br>
+const Suppression *SuppressionContext::SuppressionAt(uptr i) const {<br>
+ CHECK_LT(i, suppressions_.size());<br>
+ return &suppressions_[i];<br>
+}<br>
+<br>
+void SuppressionContext::GetMatched(<br>
+ InternalMmapVector<Suppression *> *matched) {<br>
+ for (uptr i = 0; i < suppressions_.size(); i++)<br>
+ if (suppressions_[i].hit_count)<br>
+ matched->push_back(&suppressions_[i]);<br>
+}<br>
+<br>
+const char *SuppressionTypeString(SuppressionType t) {<br>
+ CHECK(t < SuppressionTypeCount);<br>
+ return kTypeStrings[t];<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_thread_registry.cc --------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between sanitizer tools.<br>
+//<br>
+// General thread bookkeeping functionality.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_thread_registry.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+ThreadContextBase::ThreadContextBase(u32 tid)<br>
+ : tid(tid), unique_id(0), os_id(0), user_id(0), status(ThreadStatusInvalid),<br>
+ detached(false), reuse_count(0), parent_tid(0), next(0) {<br>
+ name[0] = '\0';<br>
+}<br>
+<br>
+ThreadContextBase::~ThreadContextBase() {<br>
+ // ThreadContextBase should never be deleted.<br>
+ CHECK(0);<br>
+}<br>
+<br>
+void ThreadContextBase::SetName(const char *new_name) {<br>
+ name[0] = '\0';<br>
+ if (new_name) {<br>
+ internal_strncpy(name, new_name, sizeof(name));<br>
+ name[sizeof(name) - 1] = '\0';<br>
+ }<br>
+}<br>
+<br>
+void ThreadContextBase::SetDead() {<br>
+ CHECK(status == ThreadStatusRunning ||<br>
+ status == ThreadStatusFinished);<br>
+ status = ThreadStatusDead;<br>
+ user_id = 0;<br>
+ OnDead();<br>
+}<br>
+<br>
+void ThreadContextBase::SetJoined(void *arg) {<br>
+ // FIXME(dvyukov): print message and continue (it's user error).<br>
+ CHECK_EQ(false, detached);<br>
+ CHECK_EQ(ThreadStatusFinished, status);<br>
+ status = ThreadStatusDead;<br>
+ user_id = 0;<br>
+ OnJoined(arg);<br>
+}<br>
+<br>
+void ThreadContextBase::SetFinished() {<br>
+ if (!detached)<br>
+ status = ThreadStatusFinished;<br>
+ OnFinished();<br>
+}<br>
+<br>
+void ThreadContextBase::SetStarted(uptr _os_id, void *arg) {<br>
+ status = ThreadStatusRunning;<br>
+ os_id = _os_id;<br>
+ OnStarted(arg);<br>
+}<br>
+<br>
+void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,<br>
+ bool _detached, u32 _parent_tid, void *arg) {<br>
+ status = ThreadStatusCreated;<br>
+ user_id = _user_id;<br>
+ unique_id = _unique_id;<br>
+ detached = _detached;<br>
+ // Parent tid makes no sense for the main thread.<br>
+ if (tid != 0)<br>
+ parent_tid = _parent_tid;<br>
+ OnCreated(arg);<br>
+}<br>
+<br>
+void ThreadContextBase::Reset() {<br>
+ status = ThreadStatusInvalid;<br>
+ reuse_count++;<br>
+ SetName(0);<br>
+ OnReset();<br>
+}<br>
+<br>
+// ThreadRegistry implementation.<br>
+<br>
+const u32 ThreadRegistry::kUnknownTid = ~0U;<br>
+<br>
+ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,<br>
+ u32 thread_quarantine_size)<br>
+ : context_factory_(factory),<br>
+ max_threads_(max_threads),<br>
+ thread_quarantine_size_(thread_quarantine_size),<br>
+ mtx_(),<br>
+ n_contexts_(0),<br>
+ total_threads_(0),<br>
+ alive_threads_(0),<br>
+ max_alive_threads_(0),<br>
+ running_threads_(0) {<br>
+ threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),<br>
+ "ThreadRegistry");<br>
+ dead_threads_.clear();<br>
+ invalid_threads_.clear();<br>
+}<br>
+<br>
+void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,<br>
+ uptr *alive) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ if (total) *total = n_contexts_;<br>
+ if (running) *running = running_threads_;<br>
+ if (alive) *alive = alive_threads_;<br>
+}<br>
+<br>
+uptr ThreadRegistry::GetMaxAliveThreads() {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ return max_alive_threads_;<br>
+}<br>
+<br>
+u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,<br>
+ void *arg) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ u32 tid = kUnknownTid;<br>
+ ThreadContextBase *tctx = QuarantinePop();<br>
+ if (tctx) {<br>
+ tid = tctx->tid;<br>
+ } else if (n_contexts_ < max_threads_) {<br>
+ // Allocate new thread context and tid.<br>
+ tid = n_contexts_++;<br>
+ tctx = context_factory_(tid);<br>
+ threads_[tid] = tctx;<br>
+ } else {<br>
+#ifndef SANITIZER_GO<br>
+ Report("%s: Thread limit (%u threads) exceeded. Dying.\n",<br>
+ SanitizerToolName, max_threads_);<br>
+#else<br>
+ Printf("race: limit on %u simultaneously alive goroutines is exceeded,"<br>
+ " dying\n", max_threads_);<br>
+#endif<br>
+ Die();<br>
+ }<br>
+ CHECK_NE(tctx, 0);<br>
+ CHECK_NE(tid, kUnknownTid);<br>
+ CHECK_LT(tid, max_threads_);<br>
+ CHECK_EQ(tctx->status, ThreadStatusInvalid);<br>
+ alive_threads_++;<br>
+ if (max_alive_threads_ < alive_threads_) {<br>
+ max_alive_threads_++;<br>
+ CHECK_EQ(alive_threads_, max_alive_threads_);<br>
+ }<br>
+ tctx->SetCreated(user_id, total_threads_++, detached,<br>
+ parent_tid, arg);<br>
+ return tid;<br>
+}<br>
+<br>
+void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,<br>
+ void *arg) {<br>
+ CheckLocked();<br>
+ for (u32 tid = 0; tid < n_contexts_; tid++) {<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ if (tctx == 0)<br>
+ continue;<br>
+ cb(tctx, arg);<br>
+ }<br>
+}<br>
+<br>
+u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ for (u32 tid = 0; tid < n_contexts_; tid++) {<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ if (tctx != 0 && cb(tctx, arg))<br>
+ return tctx->tid;<br>
+ }<br>
+ return kUnknownTid;<br>
+}<br>
+<br>
+ThreadContextBase *<br>
+ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {<br>
+ CheckLocked();<br>
+ for (u32 tid = 0; tid < n_contexts_; tid++) {<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ if (tctx != 0 && cb(tctx, arg))<br>
+ return tctx;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,<br>
+ void *arg) {<br>
+ return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&<br>
+ tctx->status != ThreadStatusDead);<br>
+}<br>
+<br>
+ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) {<br>
+ return FindThreadContextLocked(FindThreadContextByOsIdCallback,<br>
+ (void *)os_id);<br>
+}<br>
+<br>
+void ThreadRegistry::SetThreadName(u32 tid, const char *name) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ CHECK_LT(tid, n_contexts_);<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ CHECK_NE(tctx, 0);<br>
+ CHECK_EQ(ThreadStatusRunning, tctx->status);<br>
+ tctx->SetName(name);<br>
+}<br>
+<br>
+void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ for (u32 tid = 0; tid < n_contexts_; tid++) {<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ if (tctx != 0 && tctx->user_id == user_id &&<br>
+ tctx->status != ThreadStatusInvalid) {<br>
+ tctx->SetName(name);<br>
+ return;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+void ThreadRegistry::DetachThread(u32 tid) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ CHECK_LT(tid, n_contexts_);<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ CHECK_NE(tctx, 0);<br>
+ if (tctx->status == ThreadStatusInvalid) {<br>
+ Report("%s: Detach of non-existent thread\n", SanitizerToolName);<br>
+ return;<br>
+ }<br>
+ if (tctx->status == ThreadStatusFinished) {<br>
+ tctx->SetDead();<br>
+ QuarantinePush(tctx);<br>
+ } else {<br>
+ tctx->detached = true;<br>
+ }<br>
+}<br>
+<br>
+void ThreadRegistry::JoinThread(u32 tid, void *arg) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ CHECK_LT(tid, n_contexts_);<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ CHECK_NE(tctx, 0);<br>
+ if (tctx->status == ThreadStatusInvalid) {<br>
+ Report("%s: Join of non-existent thread\n", SanitizerToolName);<br>
+ return;<br>
+ }<br>
+ tctx->SetJoined(arg);<br>
+ QuarantinePush(tctx);<br>
+}<br>
+<br>
+void ThreadRegistry::FinishThread(u32 tid) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ CHECK_GT(alive_threads_, 0);<br>
+ alive_threads_--;<br>
+ CHECK_GT(running_threads_, 0);<br>
+ running_threads_--;<br>
+ CHECK_LT(tid, n_contexts_);<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ CHECK_NE(tctx, 0);<br>
+ CHECK_EQ(ThreadStatusRunning, tctx->status);<br>
+ tctx->SetFinished();<br>
+ if (tctx->detached) {<br>
+ tctx->SetDead();<br>
+ QuarantinePush(tctx);<br>
+ }<br>
+}<br>
+<br>
+void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) {<br>
+ BlockingMutexLock l(&mtx_);<br>
+ running_threads_++;<br>
+ CHECK_LT(tid, n_contexts_);<br>
+ ThreadContextBase *tctx = threads_[tid];<br>
+ CHECK_NE(tctx, 0);<br>
+ CHECK_EQ(ThreadStatusCreated, tctx->status);<br>
+ tctx->SetStarted(os_id, arg);<br>
+}<br>
+<br>
+void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {<br>
+ dead_threads_.push_back(tctx);<br>
+ if (dead_threads_.size() <= thread_quarantine_size_)<br>
+ return;<br>
+ tctx = dead_threads_.front();<br>
+ dead_threads_.pop_front();<br>
+ CHECK_EQ(tctx->status, ThreadStatusDead);<br>
+ tctx->Reset();<br>
+ invalid_threads_.push_back(tctx);<br>
+}<br>
+<br>
+ThreadContextBase *ThreadRegistry::QuarantinePop() {<br>
+ if (invalid_threads_.size() == 0)<br>
+ return 0;<br>
+ ThreadContextBase *tctx = invalid_threads_.front();<br>
+ invalid_threads_.pop_front();<br>
+ return tctx;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_posix.cc ------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries and implements POSIX-specific functions from<br>
+// sanitizer_libc.h.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#if SANITIZER_LINUX || SANITIZER_MAC<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_libc.h"<br>
+#include "sanitizer_procmaps.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+<br>
+#include <sys/mman.h><br>
+<br>
+namespace __sanitizer {<br>
+<br>
+// ------------- sanitizer_common.h<br>
+uptr GetMmapGranularity() {<br>
+ return GetPageSize();<br>
+}<br>
+<br>
+uptr GetMaxVirtualAddress() {<br>
+#if SANITIZER_WORDSIZE == 64<br>
+# if defined(__powerpc64__)<br>
+ // On PowerPC64 we have two different address space layouts: 44- and 46-bit.<br>
+ // We somehow need to figure our which one we are using now and choose<br>
+ // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.<br>
+ // Note that with 'ulimit -s unlimited' the stack is moved away from the top<br>
+ // of the address space, so simply checking the stack address is not enough.<br>
+ return (1ULL << 44) - 1; // 0x00000fffffffffffUL<br>
+# elif defined(__aarch64__)<br>
+ return (1ULL << 39) - 1;<br>
+# else<br>
+ return (1ULL << 47) - 1; // 0x00007fffffffffffUL;<br>
+# endif<br>
+#else // SANITIZER_WORDSIZE == 32<br>
+ // FIXME: We can probably lower this on Android?<br>
+ return (1ULL << 32) - 1; // 0xffffffff;<br>
+#endif // SANITIZER_WORDSIZE<br>
+}<br>
+<br>
+void *MmapOrDie(uptr size, const char *mem_type) {<br>
+ size = RoundUpTo(size, GetPageSizeCached());<br>
+ uptr res = internal_mmap(0, size,<br>
+ PROT_READ | PROT_WRITE,<br>
+ MAP_PRIVATE | MAP_ANON, -1, 0);<br>
+ int reserrno;<br>
+ if (internal_iserror(res, &reserrno)) {<br>
+ static int recursion_count;<br>
+ if (recursion_count) {<br>
+ // The Report() and CHECK calls below may call mmap recursively and fail.<br>
+ // If we went into recursion, just die.<br>
+ RawWrite("ERROR: Failed to mmap\n");<br>
+ Die();<br>
+ }<br>
+ recursion_count++;<br>
+ Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %d\n",<br>
+ SanitizerToolName, size, size, mem_type, reserrno);<br>
+ DumpProcessMap();<br>
+ CHECK("unable to mmap" && 0);<br>
+ }<br>
+ return (void *)res;<br>
+}<br>
+<br>
+void UnmapOrDie(void *addr, uptr size) {<br>
+ if (!addr || !size) return;<br>
+ uptr res = internal_munmap(addr, size);<br>
+ if (internal_iserror(res)) {<br>
+ Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",<br>
+ SanitizerToolName, size, size, addr);<br>
+ CHECK("unable to unmap" && 0);<br>
+ }<br>
+}<br>
+<br>
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {<br>
+ uptr PageSize = GetPageSizeCached();<br>
+ uptr p = internal_mmap(0,<br>
+ RoundUpTo(size, PageSize),<br>
+ PROT_READ | PROT_WRITE,<br>
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,<br>
+ -1, 0);<br>
+ int reserrno;<br>
+ if (internal_iserror(p, &reserrno)) {<br>
+ Report("ERROR: "<br>
+ "%s failed to allocate noreserve 0x%zx (%zd) bytes for '%s' (%d)\n",<br>
+ SanitizerToolName, size, size, mem_type, reserrno);<br>
+ CHECK("unable to mmap" && 0);<br>
+ }<br>
+ return (void *)p;<br>
+}<br>
+<br>
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {<br>
+ uptr PageSize = GetPageSizeCached();<br>
+ uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),<br>
+ RoundUpTo(size, PageSize),<br>
+ PROT_READ | PROT_WRITE,<br>
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,<br>
+ -1, 0);<br>
+ int reserrno;<br>
+ if (internal_iserror(p, &reserrno))<br>
+ Report("ERROR: "<br>
+ "%s failed to allocate 0x%zx (%zd) bytes at address %zu (%d)\n",<br>
+ SanitizerToolName, size, size, fixed_addr, reserrno);<br>
+ return (void *)p;<br>
+}<br>
+<br>
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {<br>
+ uptr PageSize = GetPageSizeCached();<br>
+ uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),<br>
+ RoundUpTo(size, PageSize),<br>
+ PROT_READ | PROT_WRITE,<br>
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,<br>
+ -1, 0);<br>
+ int reserrno;<br>
+ if (internal_iserror(p, &reserrno)) {<br>
+ Report("ERROR:"<br>
+ " %s failed to allocate 0x%zx (%zd) bytes at address %zu (%d)\n",<br>
+ SanitizerToolName, size, size, fixed_addr, reserrno);<br>
+ CHECK("unable to mmap" && 0);<br>
+ }<br>
+ return (void *)p;<br>
+}<br>
+<br>
+void *Mprotect(uptr fixed_addr, uptr size) {<br>
+ return (void *)internal_mmap((void*)fixed_addr, size,<br>
+ PROT_NONE,<br>
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED |<br>
+ MAP_NORESERVE, -1, 0);<br>
+}<br>
+<br>
+void *MapFileToMemory(const char *file_name, uptr *buff_size) {<br>
+ uptr openrv = OpenFile(file_name, false);<br>
+ CHECK(!internal_iserror(openrv));<br>
+ fd_t fd = openrv;<br>
+ uptr fsize = internal_filesize(fd);<br>
+ CHECK_NE(fsize, (uptr)-1);<br>
+ CHECK_GT(fsize, 0);<br>
+ *buff_size = RoundUpTo(fsize, GetPageSizeCached());<br>
+ uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);<br>
+ return internal_iserror(map) ? 0 : (void *)map;<br>
+}<br>
+<br>
+<br>
+static inline bool IntervalsAreSeparate(uptr start1, uptr end1,<br>
+ uptr start2, uptr end2) {<br>
+ CHECK(start1 <= end1);<br>
+ CHECK(start2 <= end2);<br>
+ return (end1 < start2) || (end2 < start1);<br>
+}<br>
+<br>
+// FIXME: this is thread-unsafe, but should not cause problems most of the time.<br>
+// When the shadow is mapped only a single thread usually exists (plus maybe<br>
+// several worker threads on Mac, which aren't expected to map big chunks of<br>
+// memory).<br>
+bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {<br>
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);<br>
+ uptr start, end;<br>
+ while (proc_maps.Next(&start, &end,<br>
+ /*offset*/0, /*filename*/0, /*filename_size*/0,<br>
+ /*protection*/0)) {<br>
+ if (!IntervalsAreSeparate(start, end, range_start, range_end))<br>
+ return false;<br>
+ }<br>
+ return true;<br>
+}<br>
+<br>
+void DumpProcessMap() {<br>
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);<br>
+ uptr start, end;<br>
+ const sptr kBufSize = 4095;<br>
+ char *filename = (char*)MmapOrDie(kBufSize, __func__);<br>
+ Report("Process memory map follows:\n");<br>
+ while (proc_maps.Next(&start, &end, /* file_offset */0,<br>
+ filename, kBufSize, /* protection */0)) {<br>
+ Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename);<br>
+ }<br>
+ Report("End of process memory map.\n");<br>
+ UnmapOrDie(filename, kBufSize);<br>
+}<br>
+<br>
+const char *GetPwd() {<br>
+ return GetEnv("PWD");<br>
+}<br>
+<br>
+char *FindPathToBinary(const char *name) {<br>
+ const char *path = GetEnv("PATH");<br>
+ if (!path)<br>
+ return 0;<br>
+ uptr name_len = internal_strlen(name);<br>
+ InternalScopedBuffer<char> buffer(kMaxPathLength);<br>
+ const char *beg = path;<br>
+ while (true) {<br>
+ const char *end = internal_strchrnul(beg, ':');<br>
+ uptr prefix_len = end - beg;<br>
+ if (prefix_len + name_len + 2 <= kMaxPathLength) {<br>
+ internal_memcpy(buffer.data(), beg, prefix_len);<br>
+ buffer[prefix_len] = '/';<br>
+ internal_memcpy(&buffer[prefix_len + 1], name, name_len);<br>
+ buffer[prefix_len + 1 + name_len] = '\0';<br>
+ if (FileExists(buffer.data()))<br>
+ return internal_strdup(buffer.data());<br>
+ }<br>
+ if (*end == '\0') break;<br>
+ beg = end + 1;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+void MaybeOpenReportFile() {<br>
+ if (!log_to_file) return;<br>
+ uptr pid = internal_getpid();<br>
+ // If in tracer, use the parent's file.<br>
+ if (pid == stoptheworld_tracer_pid)<br>
+ pid = stoptheworld_tracer_ppid;<br>
+ if (report_fd_pid == pid) return;<br>
+ InternalScopedBuffer<char> report_path_full(4096);<br>
+ internal_snprintf(report_path_full.data(), report_path_full.size(),<br>
+ "%s.%zu", report_path_prefix, pid);<br>
+ uptr openrv = OpenFile(report_path_full.data(), true);<br>
+ if (internal_iserror(openrv)) {<br>
+ report_fd = kStderrFd;<br>
+ log_to_file = false;<br>
+ Report("ERROR: Can't open file: %s\n", report_path_full.data());<br>
+ Die();<br>
+ }<br>
+ if (report_fd != kInvalidFd) {<br>
+ // We're in the child. Close the parent's log.<br>
+ internal_close(report_fd);<br>
+ }<br>
+ report_fd = openrv;<br>
+ report_fd_pid = pid;<br>
+}<br>
+<br>
+void RawWrite(const char *buffer) {<br>
+ static const char *kRawWriteError =<br>
+ "RawWrite can't output requested buffer!\n";<br>
+ uptr length = (uptr)internal_strlen(buffer);<br>
+ MaybeOpenReportFile();<br>
+ if (length != internal_write(report_fd, buffer, length)) {<br>
+ internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError));<br>
+ Die();<br>
+ }<br>
+}<br>
+<br>
+bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {<br>
+ uptr s, e, off, prot;<br>
+ InternalScopedString buff(4096);<br>
+ MemoryMappingLayout proc_maps(/*cache_enabled*/false);<br>
+ while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) {<br>
+ if ((prot & MemoryMappingLayout::kProtectionExecute) != 0<br>
+ && internal_strcmp(module, buff.data()) == 0) {<br>
+ *start = s;<br>
+ *end = e;<br>
+ return true;<br>
+ }<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_LINUX || SANITIZER_MAC<br>
+//===-- sanitizer_posix_libcdep.cc ----------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries and implements libc-dependent POSIX-specific functions<br>
+// from sanitizer_libc.h.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+<br>
+#if SANITIZER_LINUX || SANITIZER_MAC<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_platform_limits_posix.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+<br>
+#include <errno.h><br>
+#include <pthread.h><br>
+#include <signal.h><br>
+#include <stdlib.h><br>
+#include <sys/mman.h><br>
+#include <sys/resource.h><br>
+#include <sys/time.h><br>
+#include <sys/types.h><br>
+#include <unistd.h><br>
+<br>
+namespace __sanitizer {<br>
+<br>
+u32 GetUid() {<br>
+ return getuid();<br>
+}<br>
+<br>
+uptr GetThreadSelf() {<br>
+ return (uptr)pthread_self();<br>
+}<br>
+<br>
+void FlushUnneededShadowMemory(uptr addr, uptr size) {<br>
+ madvise((void*)addr, size, MADV_DONTNEED);<br>
+}<br>
+<br>
+void DisableCoreDumper() {<br>
+ struct rlimit nocore;<br>
+ nocore.rlim_cur = 0;<br>
+ nocore.rlim_max = 0;<br>
+ setrlimit(RLIMIT_CORE, &nocore);<br>
+}<br>
+<br>
+bool StackSizeIsUnlimited() {<br>
+ struct rlimit rlim;<br>
+ CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim));<br>
+ return (rlim.rlim_cur == (uptr)-1);<br>
+}<br>
+<br>
+void SetStackSizeLimitInBytes(uptr limit) {<br>
+ struct rlimit rlim;<br>
+ rlim.rlim_cur = limit;<br>
+ rlim.rlim_max = limit;<br>
+ if (setrlimit(RLIMIT_STACK, &rlim)) {<br>
+ Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);<br>
+ Die();<br>
+ }<br>
+ CHECK(!StackSizeIsUnlimited());<br>
+}<br>
+<br>
+void SleepForSeconds(int seconds) {<br>
+ sleep(seconds);<br>
+}<br>
+<br>
+void SleepForMillis(int millis) {<br>
+ usleep(millis * 1000);<br>
+}<br>
+<br>
+void Abort() {<br>
+ abort();<br>
+}<br>
+<br>
+int Atexit(void (*function)(void)) {<br>
+#ifndef SANITIZER_GO<br>
+ return atexit(function);<br>
+#else<br>
+ return 0;<br>
+#endif<br>
+}<br>
+<br>
+int internal_isatty(fd_t fd) {<br>
+ return isatty(fd);<br>
+}<br>
+<br>
+#ifndef SANITIZER_GO<br>
+// TODO(glider): different tools may require different altstack size.<br>
+static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.<br>
+<br>
+void SetAlternateSignalStack() {<br>
+ stack_t altstack, oldstack;<br>
+ CHECK_EQ(0, sigaltstack(0, &oldstack));<br>
+ // If the alternate stack is already in place, do nothing.<br>
+ // Android always sets an alternate stack, but it's too small for us.<br>
+ if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;<br>
+ // TODO(glider): the mapped stack should have the MAP_STACK flag in the<br>
+ // future. It is not required by man 2 sigaltstack now (they're using<br>
+ // malloc()).<br>
+ void* base = MmapOrDie(kAltStackSize, __func__);<br>
+ altstack.ss_sp = base;<br>
+ altstack.ss_flags = 0;<br>
+ altstack.ss_size = kAltStackSize;<br>
+ CHECK_EQ(0, sigaltstack(&altstack, 0));<br>
+}<br>
+<br>
+void UnsetAlternateSignalStack() {<br>
+ stack_t altstack, oldstack;<br>
+ altstack.ss_sp = 0;<br>
+ altstack.ss_flags = SS_DISABLE;<br>
+ altstack.ss_size = 0;<br>
+ CHECK_EQ(0, sigaltstack(&altstack, &oldstack));<br>
+ UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);<br>
+}<br>
+<br>
+typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);<br>
+static void MaybeInstallSigaction(int signum,<br>
+ SignalHandlerType handler) {<br>
+ if (!IsDeadlySignal(signum))<br>
+ return;<br>
+ struct sigaction sigact;<br>
+ internal_memset(&sigact, 0, sizeof(sigact));<br>
+ sigact.sa_sigaction = (sa_sigaction_t)handler;<br>
+ sigact.sa_flags = SA_SIGINFO;<br>
+ if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;<br>
+ CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));<br>
+ VReport(1, "Installed the sigaction for signal %d\n", signum);<br>
+}<br>
+<br>
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {<br>
+ // Set the alternate signal stack for the main thread.<br>
+ // This will cause SetAlternateSignalStack to be called twice, but the stack<br>
+ // will be actually set only once.<br>
+ if (common_flags()->use_sigaltstack) SetAlternateSignalStack();<br>
+ MaybeInstallSigaction(SIGSEGV, handler);<br>
+ MaybeInstallSigaction(SIGBUS, handler);<br>
+}<br>
+#endif // SANITIZER_GO<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif<br>
+//===-- sanitizer_procmaps_linux.cc ---------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// Information about the process mappings (Linux-specific parts).<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#if SANITIZER_LINUX<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_placement_new.h"<br>
+#include "sanitizer_procmaps.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+// Linker initialized.<br>
+ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;<br>
+StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.<br>
+<br>
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {<br>
+ proc_self_maps_.len =<br>
+ ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data,<br>
+ &proc_self_maps_.mmaped_size, 1 << 26);<br>
+ if (cache_enabled) {<br>
+ if (proc_self_maps_.mmaped_size == 0) {<br>
+ LoadFromCache();<br>
+ CHECK_GT(proc_self_maps_.len, 0);<br>
+ }<br>
+ } else {<br>
+ CHECK_GT(proc_self_maps_.mmaped_size, 0);<br>
+ }<br>
+ Reset();<br>
+ // FIXME: in the future we may want to cache the mappings on demand only.<br>
+ if (cache_enabled)<br>
+ CacheMemoryMappings();<br>
+}<br>
+<br>
+MemoryMappingLayout::~MemoryMappingLayout() {<br>
+ // Only unmap the buffer if it is different from the cached one. Otherwise<br>
+ // it will be unmapped when the cache is refreshed.<br>
+ if (proc_self_maps_.data != cached_proc_self_maps_.data) {<br>
+ UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);<br>
+ }<br>
+}<br>
+<br>
+void MemoryMappingLayout::Reset() {<br>
+ current_ = proc_self_maps_.data;<br>
+}<br>
+<br>
+// static<br>
+void MemoryMappingLayout::CacheMemoryMappings() {<br>
+ SpinMutexLock l(&cache_lock_);<br>
+ // Don't invalidate the cache if the mappings are unavailable.<br>
+ ProcSelfMapsBuff old_proc_self_maps;<br>
+ old_proc_self_maps = cached_proc_self_maps_;<br>
+ cached_proc_self_maps_.len =<br>
+ ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data,<br>
+ &cached_proc_self_maps_.mmaped_size, 1 << 26);<br>
+ if (cached_proc_self_maps_.mmaped_size == 0) {<br>
+ cached_proc_self_maps_ = old_proc_self_maps;<br>
+ } else {<br>
+ if (old_proc_self_maps.mmaped_size) {<br>
+ UnmapOrDie(old_proc_self_maps.data,<br>
+ old_proc_self_maps.mmaped_size);<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+void MemoryMappingLayout::LoadFromCache() {<br>
+ SpinMutexLock l(&cache_lock_);<br>
+ if (cached_proc_self_maps_.data) {<br>
+ proc_self_maps_ = cached_proc_self_maps_;<br>
+ }<br>
+}<br>
+<br>
+// Parse a hex value in str and update str.<br>
+static uptr ParseHex(char **str) {<br>
+ uptr x = 0;<br>
+ char *s;<br>
+ for (s = *str; ; s++) {<br>
+ char c = *s;<br>
+ uptr v = 0;<br>
+ if (c >= '0' && c <= '9')<br>
+ v = c - '0';<br>
+ else if (c >= 'a' && c <= 'f')<br>
+ v = c - 'a' + 10;<br>
+ else if (c >= 'A' && c <= 'F')<br>
+ v = c - 'A' + 10;<br>
+ else<br>
+ break;<br>
+ x = x * 16 + v;<br>
+ }<br>
+ *str = s;<br>
+ return x;<br>
+}<br>
+<br>
+static bool IsOneOf(char c, char c1, char c2) {<br>
+ return c == c1 || c == c2;<br>
+}<br>
+<br>
+static bool IsDecimal(char c) {<br>
+ return c >= '0' && c <= '9';<br>
+}<br>
+<br>
+static bool IsHex(char c) {<br>
+ return (c >= '0' && c <= '9')<br>
+ || (c >= 'a' && c <= 'f');<br>
+}<br>
+<br>
+static uptr ReadHex(const char *p) {<br>
+ uptr v = 0;<br>
+ for (; IsHex(p[0]); p++) {<br>
+ if (p[0] >= '0' && p[0] <= '9')<br>
+ v = v * 16 + p[0] - '0';<br>
+ else<br>
+ v = v * 16 + p[0] - 'a' + 10;<br>
+ }<br>
+ return v;<br>
+}<br>
+<br>
+static uptr ReadDecimal(const char *p) {<br>
+ uptr v = 0;<br>
+ for (; IsDecimal(p[0]); p++)<br>
+ v = v * 10 + p[0] - '0';<br>
+ return v;<br>
+}<br>
+<br>
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,<br>
+ char filename[], uptr filename_size,<br>
+ uptr *protection) {<br>
+ char *last = proc_self_maps_.data + proc_self_maps_.len;<br>
+ if (current_ >= last) return false;<br>
+ uptr dummy;<br>
+ if (!start) start = &dummy;<br>
+ if (!end) end = &dummy;<br>
+ if (!offset) offset = &dummy;<br>
+ char *next_line = (char*)internal_memchr(current_, '\n', last - current_);<br>
+ if (next_line == 0)<br>
+ next_line = last;<br>
+ // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar<br>
+ *start = ParseHex(¤t_);<br>
+ CHECK_EQ(*current_++, '-');<br>
+ *end = ParseHex(¤t_);<br>
+ CHECK_EQ(*current_++, ' ');<br>
+ uptr local_protection = 0;<br>
+ CHECK(IsOneOf(*current_, '-', 'r'));<br>
+ if (*current_++ == 'r')<br>
+ local_protection |= kProtectionRead;<br>
+ CHECK(IsOneOf(*current_, '-', 'w'));<br>
+ if (*current_++ == 'w')<br>
+ local_protection |= kProtectionWrite;<br>
+ CHECK(IsOneOf(*current_, '-', 'x'));<br>
+ if (*current_++ == 'x')<br>
+ local_protection |= kProtectionExecute;<br>
+ CHECK(IsOneOf(*current_, 's', 'p'));<br>
+ if (*current_++ == 's')<br>
+ local_protection |= kProtectionShared;<br>
+ if (protection) {<br>
+ *protection = local_protection;<br>
+ }<br>
+ CHECK_EQ(*current_++, ' ');<br>
+ *offset = ParseHex(¤t_);<br>
+ CHECK_EQ(*current_++, ' ');<br>
+ ParseHex(¤t_);<br>
+ CHECK_EQ(*current_++, ':');<br>
+ ParseHex(¤t_);<br>
+ CHECK_EQ(*current_++, ' ');<br>
+ while (IsDecimal(*current_))<br>
+ current_++;<br>
+ // Qemu may lack the trailing space.<br>
+ // <a href="http://code.google.com/p/address-sanitizer/issues/detail?id=160" target="_blank">http://code.google.com/p/address-sanitizer/issues/detail?id=160</a><br>
+ // CHECK_EQ(*current_++, ' ');<br>
+ // Skip spaces.<br>
+ while (current_ < next_line && *current_ == ' ')<br>
+ current_++;<br>
+ // Fill in the filename.<br>
+ uptr i = 0;<br>
+ while (current_ < next_line) {<br>
+ if (filename && i < filename_size - 1)<br>
+ filename[i++] = *current_;<br>
+ current_++;<br>
+ }<br>
+ if (filename && i < filename_size)<br>
+ filename[i] = 0;<br>
+ current_ = next_line + 1;<br>
+ return true;<br>
+}<br>
+<br>
+uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,<br>
+ uptr max_modules,<br>
+ string_predicate_t filter) {<br>
+ Reset();<br>
+ uptr cur_beg, cur_end, cur_offset;<br>
+ InternalScopedBuffer<char> module_name(kMaxPathLength);<br>
+ uptr n_modules = 0;<br>
+ for (uptr i = 0; n_modules < max_modules &&<br>
+ Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),<br>
+ module_name.size(), 0);<br>
+ i++) {<br>
+ const char *cur_name = module_name.data();<br>
+ if (cur_name[0] == '\0')<br>
+ continue;<br>
+ if (filter && !filter(cur_name))<br>
+ continue;<br>
+ void *mem = &modules[n_modules];<br>
+ // Don't subtract 'cur_beg' from the first entry:<br>
+ // * If a binary is compiled w/o -pie, then the first entry in<br>
+ // process maps is likely the binary itself (all dynamic libs<br>
+ // are mapped higher in address space). For such a binary,<br>
+ // instruction offset in binary coincides with the actual<br>
+ // instruction address in virtual memory (as code section<br>
+ // is mapped to a fixed memory range).<br>
+ // * If a binary is compiled with -pie, all the modules are<br>
+ // mapped high at address space (in particular, higher than<br>
+ // shadow memory of the tool), so the module can't be the<br>
+ // first entry.<br>
+ uptr base_address = (i ? cur_beg : 0) - cur_offset;<br>
+ LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);<br>
+ cur_module->addAddressRange(cur_beg, cur_end);<br>
+ n_modules++;<br>
+ }<br>
+ return n_modules;<br>
+}<br>
+<br>
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {<br>
+ char *smaps = 0;<br>
+ uptr smaps_cap = 0;<br>
+ uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",<br>
+ &smaps, &smaps_cap, 64<<20);<br>
+ uptr start = 0;<br>
+ bool file = false;<br>
+ const char *pos = smaps;<br>
+ while (pos < smaps + smaps_len) {<br>
+ if (IsHex(pos[0])) {<br>
+ start = ReadHex(pos);<br>
+ for (; *pos != '/' && *pos > '\n'; pos++) {}<br>
+ file = *pos == '/';<br>
+ } else if (internal_strncmp(pos, "Rss:", 4) == 0) {<br>
+ for (; *pos < '0' || *pos > '9'; pos++) {}<br>
+ uptr rss = ReadDecimal(pos) * 1024;<br>
+ cb(start, rss, file, stats, stats_size);<br>
+ }<br>
+ while (*pos++ != '\n') {}<br>
+ }<br>
+ UnmapOrDie(smaps, smaps_cap);<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_LINUX<br>
+//===-- sanitizer_linux.cc ------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries and implements linux-specific functions from<br>
+// sanitizer_libc.h.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#if SANITIZER_LINUX<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_internal_defs.h"<br>
+#include "sanitizer_libc.h"<br>
+#include "sanitizer_linux.h"<br>
+#include "sanitizer_mutex.h"<br>
+#include "sanitizer_placement_new.h"<br>
+#include "sanitizer_procmaps.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+<br>
+#include <asm/param.h><br>
+#include <dlfcn.h><br>
+#include <errno.h><br>
+#include <fcntl.h><br>
+#if !SANITIZER_ANDROID<br>
+#include <link.h><br>
+#endif<br>
+#include <pthread.h><br>
+#include <sched.h><br>
+#include <sys/mman.h><br>
+#include <sys/ptrace.h><br>
+#include <sys/resource.h><br>
+#include <sys/stat.h><br>
+#include <sys/syscall.h><br>
+#include <sys/time.h><br>
+#include <sys/types.h><br>
+#include <unistd.h><br>
+#include <unwind.h><br>
+<br>
+#if !SANITIZER_ANDROID<br>
+#include <sys/signal.h><br>
+#endif<br>
+<br>
+#if SANITIZER_ANDROID<br>
+#include <android/log.h><br>
+#include <sys/system_properties.h><br>
+#endif<br>
+<br>
+// <linux/time.h><br>
+struct kernel_timeval {<br>
+ long tv_sec;<br>
+ long tv_usec;<br>
+};<br>
+<br>
+// <linux/futex.h> is broken on some linux distributions.<br>
+const int FUTEX_WAIT = 0;<br>
+const int FUTEX_WAKE = 1;<br>
+<br>
+// Are we using 32-bit or 64-bit syscalls?<br>
+// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32<br>
+// but it still needs to use 64-bit syscalls.<br>
+#if defined(__x86_64__) || SANITIZER_WORDSIZE == 64<br>
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1<br>
+#else<br>
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0<br>
+#endif<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+#ifdef __x86_64__<br>
+#include "sanitizer_syscall_linux_x86_64.inc"<br>
+#else<br>
+#include "sanitizer_syscall_generic.inc"<br>
+#endif<br>
+<br>
+// --------------- sanitizer_libc.h<br>
+uptr internal_mmap(void *addr, uptr length, int prot, int flags,<br>
+ int fd, u64 offset) {<br>
+#if SANITIZER_LINUX_USES_64BIT_SYSCALLS<br>
+ return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd,<br>
+ offset);<br>
+#else<br>
+ return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_munmap(void *addr, uptr length) {<br>
+ return internal_syscall(__NR_munmap, (uptr)addr, length);<br>
+}<br>
+<br>
+uptr internal_close(fd_t fd) {<br>
+ return internal_syscall(__NR_close, fd);<br>
+}<br>
+<br>
+uptr internal_open(const char *filename, int flags) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_openat, AT_FDCWD, (uptr)filename, flags);<br>
+#else<br>
+ return internal_syscall(__NR_open, (uptr)filename, flags);<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_open(const char *filename, int flags, u32 mode) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_openat, AT_FDCWD, (uptr)filename, flags, mode);<br>
+#else<br>
+ return internal_syscall(__NR_open, (uptr)filename, flags, mode);<br>
+#endif<br>
+}<br>
+<br>
+uptr OpenFile(const char *filename, bool write) {<br>
+ return internal_open(filename,<br>
+ write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);<br>
+}<br>
+<br>
+uptr internal_read(fd_t fd, void *buf, uptr count) {<br>
+ sptr res;<br>
+ HANDLE_EINTR(res, (sptr)internal_syscall(__NR_read, fd, (uptr)buf, count));<br>
+ return res;<br>
+}<br>
+<br>
+uptr internal_write(fd_t fd, const void *buf, uptr count) {<br>
+ sptr res;<br>
+ HANDLE_EINTR(res, (sptr)internal_syscall(__NR_write, fd, (uptr)buf, count));<br>
+ return res;<br>
+}<br>
+<br>
+#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS<br>
+static void stat64_to_stat(struct stat64 *in, struct stat *out) {<br>
+ internal_memset(out, 0, sizeof(*out));<br>
+ out->st_dev = in->st_dev;<br>
+ out->st_ino = in->st_ino;<br>
+ out->st_mode = in->st_mode;<br>
+ out->st_nlink = in->st_nlink;<br>
+ out->st_uid = in->st_uid;<br>
+ out->st_gid = in->st_gid;<br>
+ out->st_rdev = in->st_rdev;<br>
+ out->st_size = in->st_size;<br>
+ out->st_blksize = in->st_blksize;<br>
+ out->st_blocks = in->st_blocks;<br>
+ out->st_atime = in->st_atime;<br>
+ out->st_mtime = in->st_mtime;<br>
+ out->st_ctime = in->st_ctime;<br>
+ out->st_ino = in->st_ino;<br>
+}<br>
+#endif<br>
+<br>
+uptr internal_stat(const char *path, void *buf) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_newfstatat, AT_FDCWD, (uptr)path, (uptr)buf, 0);<br>
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS<br>
+ return internal_syscall(__NR_stat, (uptr)path, (uptr)buf);<br>
+#else<br>
+ struct stat64 buf64;<br>
+ int res = internal_syscall(__NR_stat64, path, &buf64);<br>
+ stat64_to_stat(&buf64, (struct stat *)buf);<br>
+ return res;<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_lstat(const char *path, void *buf) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_newfstatat, AT_FDCWD, (uptr)path,<br>
+ (uptr)buf, AT_SYMLINK_NOFOLLOW);<br>
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS<br>
+ return internal_syscall(__NR_lstat, (uptr)path, (uptr)buf);<br>
+#else<br>
+ struct stat64 buf64;<br>
+ int res = internal_syscall(__NR_lstat64, path, &buf64);<br>
+ stat64_to_stat(&buf64, (struct stat *)buf);<br>
+ return res;<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_fstat(fd_t fd, void *buf) {<br>
+#if SANITIZER_LINUX_USES_64BIT_SYSCALLS<br>
+ return internal_syscall(__NR_fstat, fd, (uptr)buf);<br>
+#else<br>
+ struct stat64 buf64;<br>
+ int res = internal_syscall(__NR_fstat64, fd, &buf64);<br>
+ stat64_to_stat(&buf64, (struct stat *)buf);<br>
+ return res;<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_filesize(fd_t fd) {<br>
+ struct stat st;<br>
+ if (internal_fstat(fd, &st))<br>
+ return -1;<br>
+ return (uptr)st.st_size;<br>
+}<br>
+<br>
+uptr internal_dup2(int oldfd, int newfd) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_dup3, oldfd, newfd, 0);<br>
+#else<br>
+ return internal_syscall(__NR_dup2, oldfd, newfd);<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_readlinkat, AT_FDCWD,<br>
+ (uptr)path, (uptr)buf, bufsize);<br>
+#else<br>
+ return internal_syscall(__NR_readlink, (uptr)path, (uptr)buf, bufsize);<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_unlink(const char *path) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_unlinkat, AT_FDCWD, (uptr)path, 0);<br>
+#else<br>
+ return internal_syscall(__NR_unlink, (uptr)path);<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_sched_yield() {<br>
+ return internal_syscall(__NR_sched_yield);<br>
+}<br>
+<br>
+void internal__exit(int exitcode) {<br>
+ internal_syscall(__NR_exit_group, exitcode);<br>
+ Die(); // Unreachable.<br>
+}<br>
+<br>
+uptr internal_execve(const char *filename, char *const argv[],<br>
+ char *const envp[]) {<br>
+ return internal_syscall(__NR_execve, (uptr)filename, (uptr)argv, (uptr)envp);<br>
+}<br>
+<br>
+// ----------------- sanitizer_common.h<br>
+bool FileExists(const char *filename) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ struct stat st;<br>
+ if (internal_syscall(__NR_newfstatat, AT_FDCWD, filename, &st, 0))<br>
+ return false;<br>
+#else<br>
+ struct stat st;<br>
+ if (internal_stat(filename, &st))<br>
+ return false;<br>
+ // Sanity check: filename is a regular file.<br>
+ return S_ISREG(st.st_mode);<br>
+#endif<br>
+}<br>
+<br>
+uptr GetTid() {<br>
+ return internal_syscall(__NR_gettid);<br>
+}<br>
+<br>
+u64 NanoTime() {<br>
+ kernel_timeval tv;<br>
+ internal_memset(&tv, 0, sizeof(tv));<br>
+ internal_syscall(__NR_gettimeofday, (uptr)&tv, 0);<br>
+ return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;<br>
+}<br>
+<br>
+// Like getenv, but reads env directly from /proc and does not use libc.<br>
+// This function should be called first inside __asan_init.<br>
+const char *GetEnv(const char *name) {<br>
+ static char *environ;<br>
+ static uptr len;<br>
+ static bool inited;<br>
+ if (!inited) {<br>
+ inited = true;<br>
+ uptr environ_size;<br>
+ len = ReadFileToBuffer("/proc/self/environ",<br>
+ &environ, &environ_size, 1 << 26);<br>
+ }<br>
+ if (!environ || len == 0) return 0;<br>
+ uptr namelen = internal_strlen(name);<br>
+ const char *p = environ;<br>
+ while (*p != '\0') { // will happen at the \0\0 that terminates the buffer<br>
+ // proc file has the format NAME=value\0NAME=value\0NAME=value\0...<br>
+ const char* endp =<br>
+ (char*)internal_memchr(p, '\0', len - (p - environ));<br>
+ if (endp == 0) // this entry isn't NUL terminated<br>
+ return 0;<br>
+ else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.<br>
+ return p + namelen + 1; // point after =<br>
+ p = endp + 1;<br>
+ }<br>
+ return 0; // Not found.<br>
+}<br>
+<br>
+extern "C" {<br>
+ SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end;<br>
+}<br>
+<br>
+#if !SANITIZER_GO<br>
+static void ReadNullSepFileToArray(const char *path, char ***arr,<br>
+ int arr_size) {<br>
+ char *buff;<br>
+ uptr buff_size = 0;<br>
+ *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");<br>
+ ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024);<br>
+ (*arr)[0] = buff;<br>
+ int count, i;<br>
+ for (count = 1, i = 1; ; i++) {<br>
+ if (buff[i] == 0) {<br>
+ if (buff[i+1] == 0) break;<br>
+ (*arr)[count] = &buff[i+1];<br>
+ CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.<br>
+ count++;<br>
+ }<br>
+ }<br>
+ (*arr)[count] = 0;<br>
+}<br>
+#endif<br>
+<br>
+static void GetArgsAndEnv(char*** argv, char*** envp) {<br>
+#if !SANITIZER_GO<br>
+ if (&__libc_stack_end) {<br>
+#endif<br>
+ uptr* stack_end = (uptr*)__libc_stack_end;<br>
+ int argc = *stack_end;<br>
+ *argv = (char**)(stack_end + 1);<br>
+ *envp = (char**)(stack_end + argc + 2);<br>
+#if !SANITIZER_GO<br>
+ } else {<br>
+ static const int kMaxArgv = 2000, kMaxEnvp = 2000;<br>
+ ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv);<br>
+ ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp);<br>
+ }<br>
+#endif<br>
+}<br>
+<br>
+void ReExec() {<br>
+ char **argv, **envp;<br>
+ GetArgsAndEnv(&argv, &envp);<br>
+ uptr rv = internal_execve("/proc/self/exe", argv, envp);<br>
+ int rverrno;<br>
+ CHECK_EQ(internal_iserror(rv, &rverrno), true);<br>
+ Printf("execve failed, errno %d\n", rverrno);<br>
+ Die();<br>
+}<br>
+<br>
+void PrepareForSandboxing() {<br>
+ // Some kinds of sandboxes may forbid filesystem access, so we won't be able<br>
+ // to read the file mappings from /proc/self/maps. Luckily, neither the<br>
+ // process will be able to load additional libraries, so it's fine to use the<br>
+ // cached mappings.<br>
+ MemoryMappingLayout::CacheMemoryMappings();<br>
+ // Same for /proc/self/exe in the symbolizer.<br>
+#if !SANITIZER_GO<br>
+ if (Symbolizer *sym = Symbolizer::GetOrNull())<br>
+ sym->PrepareForSandboxing();<br>
+#endif<br>
+}<br>
+<br>
+enum MutexState {<br>
+ MtxUnlocked = 0,<br>
+ MtxLocked = 1,<br>
+ MtxSleeping = 2<br>
+};<br>
+<br>
+BlockingMutex::BlockingMutex(LinkerInitialized) {<br>
+ CHECK_EQ(owner_, 0);<br>
+}<br>
+<br>
+BlockingMutex::BlockingMutex() {<br>
+ internal_memset(this, 0, sizeof(*this));<br>
+}<br>
+<br>
+void BlockingMutex::Lock() {<br>
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);<br>
+ if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)<br>
+ return;<br>
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked)<br>
+ internal_syscall(__NR_futex, (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);<br>
+}<br>
+<br>
+void BlockingMutex::Unlock() {<br>
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);<br>
+ u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed);<br>
+ CHECK_NE(v, MtxUnlocked);<br>
+ if (v == MtxSleeping)<br>
+ internal_syscall(__NR_futex, (uptr)m, FUTEX_WAKE, 1, 0, 0, 0);<br>
+}<br>
+<br>
+void BlockingMutex::CheckLocked() {<br>
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);<br>
+ CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));<br>
+}<br>
+<br>
+// ----------------- sanitizer_linux.h<br>
+// The actual size of this structure is specified by d_reclen.<br>
+// Note that getdents64 uses a different structure format. We only provide the<br>
+// 32-bit syscall here.<br>
+struct linux_dirent {<br>
+ unsigned long d_ino;<br>
+ unsigned long d_off;<br>
+ unsigned short d_reclen;<br>
+ char d_name[256];<br>
+};<br>
+<br>
+// Syscall wrappers.<br>
+uptr internal_ptrace(int request, int pid, void *addr, void *data) {<br>
+ return internal_syscall(__NR_ptrace, request, pid, (uptr)addr, (uptr)data);<br>
+}<br>
+<br>
+uptr internal_waitpid(int pid, int *status, int options) {<br>
+ return internal_syscall(__NR_wait4, pid, (uptr)status, options,<br>
+ 0 /* rusage */);<br>
+}<br>
+<br>
+uptr internal_getpid() {<br>
+ return internal_syscall(__NR_getpid);<br>
+}<br>
+<br>
+uptr internal_getppid() {<br>
+ return internal_syscall(__NR_getppid);<br>
+}<br>
+<br>
+uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {<br>
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS<br>
+ return internal_syscall(__NR_getdents64, fd, (uptr)dirp, count);<br>
+#else<br>
+ return internal_syscall(__NR_getdents, fd, (uptr)dirp, count);<br>
+#endif<br>
+}<br>
+<br>
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {<br>
+ return internal_syscall(__NR_lseek, fd, offset, whence);<br>
+}<br>
+<br>
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {<br>
+ return internal_syscall(__NR_prctl, option, arg2, arg3, arg4, arg5);<br>
+}<br>
+<br>
+uptr internal_sigaltstack(const struct sigaltstack *ss,<br>
+ struct sigaltstack *oss) {<br>
+ return internal_syscall(__NR_sigaltstack, (uptr)ss, (uptr)oss);<br>
+}<br>
+<br>
+// Doesn't set sa_restorer, use with caution (see below).<br>
+int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {<br>
+ __sanitizer_kernel_sigaction_t k_act, k_oldact;<br>
+ internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));<br>
+ internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));<br>
+ const __sanitizer_sigaction *u_act = (__sanitizer_sigaction *)act;<br>
+ __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;<br>
+ if (u_act) {<br>
+ k_act.handler = u_act->handler;<br>
+ k_act.sigaction = u_act->sigaction;<br>
+ internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,<br>
+ sizeof(__sanitizer_kernel_sigset_t));<br>
+ k_act.sa_flags = u_act->sa_flags;<br>
+ // FIXME: most often sa_restorer is unset, however the kernel requires it<br>
+ // to point to a valid signal restorer that calls the rt_sigreturn syscall.<br>
+ // If sa_restorer passed to the kernel is NULL, the program may crash upon<br>
+ // signal delivery or fail to unwind the stack in the signal handler.<br>
+ // libc implementation of sigaction() passes its own restorer to<br>
+ // rt_sigaction, so we need to do the same (we'll need to reimplement the<br>
+ // restorers; for x86_64 the restorer address can be obtained from<br>
+ // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).<br>
+ k_act.sa_restorer = u_act->sa_restorer;<br>
+ }<br>
+<br>
+ uptr result = internal_syscall(__NR_rt_sigaction, (uptr)signum,<br>
+ (uptr)(u_act ? &k_act : NULL),<br>
+ (uptr)(u_oldact ? &k_oldact : NULL),<br>
+ (uptr)sizeof(__sanitizer_kernel_sigset_t));<br>
+<br>
+ if ((result == 0) && u_oldact) {<br>
+ u_oldact->handler = k_oldact.handler;<br>
+ u_oldact->sigaction = k_oldact.sigaction;<br>
+ internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,<br>
+ sizeof(__sanitizer_kernel_sigset_t));<br>
+ u_oldact->sa_flags = k_oldact.sa_flags;<br>
+ u_oldact->sa_restorer = k_oldact.sa_restorer;<br>
+ }<br>
+ return result;<br>
+}<br>
+<br>
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,<br>
+ __sanitizer_sigset_t *oldset) {<br>
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;<br>
+ __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;<br>
+ return internal_syscall(__NR_rt_sigprocmask, (uptr)how, &k_set->sig[0],<br>
+ &k_oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t));<br>
+}<br>
+<br>
+void internal_sigfillset(__sanitizer_sigset_t *set) {<br>
+ internal_memset(set, 0xff, sizeof(*set));<br>
+}<br>
+<br>
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {<br>
+ signum -= 1;<br>
+ CHECK_GE(signum, 0);<br>
+ CHECK_LT(signum, sizeof(*set) * 8);<br>
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;<br>
+ const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);<br>
+ const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);<br>
+ k_set->sig[idx] &= ~(1 << bit);<br>
+}<br>
+<br>
+// ThreadLister implementation.<br>
+ThreadLister::ThreadLister(int pid)<br>
+ : pid_(pid),<br>
+ descriptor_(-1),<br>
+ buffer_(4096),<br>
+ error_(true),<br>
+ entry_((struct linux_dirent *)buffer_.data()),<br>
+ bytes_read_(0) {<br>
+ char task_directory_path[80];<br>
+ internal_snprintf(task_directory_path, sizeof(task_directory_path),<br>
+ "/proc/%d/task/", pid);<br>
+ uptr openrv = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);<br>
+ if (internal_iserror(openrv)) {<br>
+ error_ = true;<br>
+ Report("Can't open /proc/%d/task for reading.\n", pid);<br>
+ } else {<br>
+ error_ = false;<br>
+ descriptor_ = openrv;<br>
+ }<br>
+}<br>
+<br>
+int ThreadLister::GetNextTID() {<br>
+ int tid = -1;<br>
+ do {<br>
+ if (error_)<br>
+ return -1;<br>
+ if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries())<br>
+ return -1;<br>
+ if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' &&<br>
+ entry_->d_name[0] <= '9') {<br>
+ // Found a valid tid.<br>
+ tid = (int)internal_atoll(entry_->d_name);<br>
+ }<br>
+ entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen);<br>
+ } while (tid < 0);<br>
+ return tid;<br>
+}<br>
+<br>
+void ThreadLister::Reset() {<br>
+ if (error_ || descriptor_ < 0)<br>
+ return;<br>
+ internal_lseek(descriptor_, 0, SEEK_SET);<br>
+}<br>
+<br>
+ThreadLister::~ThreadLister() {<br>
+ if (descriptor_ >= 0)<br>
+ internal_close(descriptor_);<br>
+}<br>
+<br>
+bool ThreadLister::error() { return error_; }<br>
+<br>
+bool ThreadLister::GetDirectoryEntries() {<br>
+ CHECK_GE(descriptor_, 0);<br>
+ CHECK_NE(error_, true);<br>
+ bytes_read_ = internal_getdents(descriptor_,<br>
+ (struct linux_dirent *)buffer_.data(),<br>
+ buffer_.size());<br>
+ if (internal_iserror(bytes_read_)) {<br>
+ Report("Can't read directory entries from /proc/%d/task.\n", pid_);<br>
+ error_ = true;<br>
+ return false;<br>
+ } else if (bytes_read_ == 0) {<br>
+ return false;<br>
+ }<br>
+ entry_ = (struct linux_dirent *)buffer_.data();<br>
+ return true;<br>
+}<br>
+<br>
+uptr GetPageSize() {<br>
+#if defined(__x86_64__) || defined(__i386__)<br>
+ return EXEC_PAGESIZE;<br>
+#else<br>
+ return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.<br>
+#endif<br>
+}<br>
+<br>
+static char proc_self_exe_cache_str[kMaxPathLength];<br>
+static uptr proc_self_exe_cache_len = 0;<br>
+<br>
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {<br>
+ uptr module_name_len = internal_readlink(<br>
+ "/proc/self/exe", buf, buf_len);<br>
+ int readlink_error;<br>
+ if (internal_iserror(module_name_len, &readlink_error)) {<br>
+ if (proc_self_exe_cache_len) {<br>
+ // If available, use the cached module name.<br>
+ CHECK_LE(proc_self_exe_cache_len, buf_len);<br>
+ internal_strncpy(buf, proc_self_exe_cache_str, buf_len);<br>
+ module_name_len = internal_strlen(proc_self_exe_cache_str);<br>
+ } else {<br>
+ // We can't read /proc/self/exe for some reason, assume the name of the<br>
+ // binary is unknown.<br>
+ Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "<br>
+ "some stack frames may not be symbolized\n", readlink_error);<br>
+ module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");<br>
+ }<br>
+ CHECK_LT(module_name_len, buf_len);<br>
+ buf[module_name_len] = '\0';<br>
+ }<br>
+ return module_name_len;<br>
+}<br>
+<br>
+void CacheBinaryName() {<br>
+ if (!proc_self_exe_cache_len) {<br>
+ proc_self_exe_cache_len =<br>
+ ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength);<br>
+ }<br>
+}<br>
+<br>
+// Match full names of the form /path/to/base_name{-,.}*<br>
+bool LibraryNameIs(const char *full_name, const char *base_name) {<br>
+ const char *name = full_name;<br>
+ // Strip path.<br>
+ while (*name != '\0') name++;<br>
+ while (name > full_name && *name != '/') name--;<br>
+ if (*name == '/') name++;<br>
+ uptr base_name_length = internal_strlen(base_name);<br>
+ if (internal_strncmp(name, base_name, base_name_length)) return false;<br>
+ return (name[base_name_length] == '-' || name[base_name_length] == '.');<br>
+}<br>
+<br>
+#if !SANITIZER_ANDROID<br>
+// Call cb for each region mapped by map.<br>
+void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {<br>
+ typedef ElfW(Phdr) Elf_Phdr;<br>
+ typedef ElfW(Ehdr) Elf_Ehdr;<br>
+ char *base = (char *)map->l_addr;<br>
+ Elf_Ehdr *ehdr = (Elf_Ehdr *)base;<br>
+ char *phdrs = base + ehdr->e_phoff;<br>
+ char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize;<br>
+<br>
+ // Find the segment with the minimum base so we can "relocate" the p_vaddr<br>
+ // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC<br>
+ // objects have a non-zero base.<br>
+ uptr preferred_base = (uptr)-1;<br>
+ for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {<br>
+ Elf_Phdr *phdr = (Elf_Phdr *)iter;<br>
+ if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr)<br>
+ preferred_base = (uptr)phdr->p_vaddr;<br>
+ }<br>
+<br>
+ // Compute the delta from the real base to get a relocation delta.<br>
+ sptr delta = (uptr)base - preferred_base;<br>
+ // Now we can figure out what the loader really mapped.<br>
+ for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {<br>
+ Elf_Phdr *phdr = (Elf_Phdr *)iter;<br>
+ if (phdr->p_type == PT_LOAD) {<br>
+ uptr seg_start = phdr->p_vaddr + delta;<br>
+ uptr seg_end = seg_start + phdr->p_memsz;<br>
+ // None of these values are aligned. We consider the ragged edges of the<br>
+ // load command as defined, since they are mapped from the file.<br>
+ seg_start = RoundDownTo(seg_start, GetPageSizeCached());<br>
+ seg_end = RoundUpTo(seg_end, GetPageSizeCached());<br>
+ cb((void *)seg_start, seg_end - seg_start);<br>
+ }<br>
+ }<br>
+}<br>
+#endif<br>
+<br>
+#if defined(__x86_64__)<br>
+// We cannot use glibc's clone wrapper, because it messes with the child<br>
+// task's TLS. It writes the PID and TID of the child task to its thread<br>
+// descriptor, but in our case the child task shares the thread descriptor with<br>
+// the parent (because we don't know how to allocate a new thread<br>
+// descriptor to keep glibc happy). So the stock version of clone(), when<br>
+// used with CLONE_VM, would end up corrupting the parent's thread descriptor.<br>
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,<br>
+ int *parent_tidptr, void *newtls, int *child_tidptr) {<br>
+ long long res;<br>
+ if (!fn || !child_stack)<br>
+ return -EINVAL;<br>
+ CHECK_EQ(0, (uptr)child_stack % 16);<br>
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);<br>
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;<br>
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;<br>
+ register void *r8 __asm__("r8") = newtls;<br>
+ register int *r10 __asm__("r10") = child_tidptr;<br>
+ __asm__ __volatile__(<br>
+ /* %rax = syscall(%rax = __NR_clone,<br>
+ * %rdi = flags,<br>
+ * %rsi = child_stack,<br>
+ * %rdx = parent_tidptr,<br>
+ * %r8 = new_tls,<br>
+ * %r10 = child_tidptr)<br>
+ */<br>
+ "syscall\n"<br>
+<br>
+ /* if (%rax != 0)<br>
+ * return;<br>
+ */<br>
+ "testq %%rax,%%rax\n"<br>
+ "jnz 1f\n"<br>
+<br>
+ /* In the child. Terminate unwind chain. */<br>
+ // XXX: We should also terminate the CFI unwind chain<br>
+ // here. Unfortunately clang 3.2 doesn't support the<br>
+ // necessary CFI directives, so we skip that part.<br>
+ "xorq %%rbp,%%rbp\n"<br>
+<br>
+ /* Call "fn(arg)". */<br>
+ "popq %%rax\n"<br>
+ "popq %%rdi\n"<br>
+ "call *%%rax\n"<br>
+<br>
+ /* Call _exit(%rax). */<br>
+ "movq %%rax,%%rdi\n"<br>
+ "movq %2,%%rax\n"<br>
+ "syscall\n"<br>
+<br>
+ /* Return to parent. */<br>
+ "1:\n"<br>
+ : "=a" (res)<br>
+ : "a"(__NR_clone), "i"(__NR_exit),<br>
+ "S"(child_stack),<br>
+ "D"(flags),<br>
+ "d"(parent_tidptr),<br>
+ "r"(r8),<br>
+ "r"(r10)<br>
+ : "rsp", "memory", "r11", "rcx");<br>
+ return res;<br>
+}<br>
+#endif // defined(__x86_64__)<br>
+<br>
+#if SANITIZER_ANDROID<br>
+// This thing is not, strictly speaking, async signal safe, but it does not seem<br>
+// to cause any issues. Alternative is writing to log devices directly, but<br>
+// their location and message format might change in the future, so we'd really<br>
+// like to avoid that.<br>
+void AndroidLogWrite(const char *buffer) {<br>
+ char *copy = internal_strdup(buffer);<br>
+ char *p = copy;<br>
+ char *q;<br>
+ // __android_log_write has an implicit message length limit.<br>
+ // Print one line at a time.<br>
+ do {<br>
+ q = internal_strchr(p, '\n');<br>
+ if (q) *q = '\0';<br>
+ __android_log_write(ANDROID_LOG_INFO, NULL, p);<br>
+ if (q) p = q + 1;<br>
+ } while (q);<br>
+ InternalFree(copy);<br>
+}<br>
+<br>
+void GetExtraActivationFlags(char *buf, uptr size) {<br>
+ CHECK(size > PROP_VALUE_MAX);<br>
+ __system_property_get("asan.options", buf);<br>
+}<br>
+#endif<br>
+<br>
+bool IsDeadlySignal(int signum) {<br>
+ return (signum == SIGSEGV) && common_flags()->handle_segv;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_LINUX<br>
+//===-- sanitizer_linux_libcdep.cc ----------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries and implements linux-specific functions from<br>
+// sanitizer_libc.h.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#if SANITIZER_LINUX<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_linux.h"<br>
+#include "sanitizer_placement_new.h"<br>
+#include "sanitizer_procmaps.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+#include "sanitizer_atomic.h"<br>
+<br>
+#include <dlfcn.h><br>
+#include <pthread.h><br>
+#include <signal.h><br>
+#include <sys/prctl.h><br>
+#include <sys/resource.h><br>
+#include <unwind.h><br>
+<br>
+#if !SANITIZER_ANDROID<br>
+#include <elf.h><br>
+#include <link.h><br>
+#include <unistd.h><br>
+#endif<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+#ifndef SANITIZER_GO<br>
+// This function is defined elsewhere if we intercepted pthread_attr_getstack.<br>
+SANITIZER_WEAK_ATTRIBUTE int<br>
+real_pthread_attr_getstack(void *attr, void **addr, size_t *size);<br>
+<br>
+static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) {<br>
+ if (real_pthread_attr_getstack)<br>
+ return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, size);<br>
+ return pthread_attr_getstack((pthread_attr_t *)attr, addr, size);<br>
+}<br>
+<br>
+SANITIZER_WEAK_ATTRIBUTE int<br>
+real_sigaction(int signum, const void *act, void *oldact);<br>
+<br>
+int internal_sigaction(int signum, const void *act, void *oldact) {<br>
+ if (real_sigaction)<br>
+ return real_sigaction(signum, act, oldact);<br>
+ return sigaction(signum, (struct sigaction *)act, (struct sigaction *)oldact);<br>
+}<br>
+<br>
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,<br>
+ uptr *stack_bottom) {<br>
+ CHECK(stack_top);<br>
+ CHECK(stack_bottom);<br>
+ if (at_initialization) {<br>
+ // This is the main thread. Libpthread may not be initialized yet.<br>
+ struct rlimit rl;<br>
+ CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);<br>
+<br>
+ // Find the mapping that contains a stack variable.<br>
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);<br>
+ uptr start, end, offset;<br>
+ uptr prev_end = 0;<br>
+ while (proc_maps.Next(&start, &end, &offset, 0, 0, /* protection */0)) {<br>
+ if ((uptr)&rl < end)<br>
+ break;<br>
+ prev_end = end;<br>
+ }<br>
+ CHECK((uptr)&rl >= start && (uptr)&rl < end);<br>
+<br>
+ // Get stacksize from rlimit, but clip it so that it does not overlap<br>
+ // with other mappings.<br>
+ uptr stacksize = rl.rlim_cur;<br>
+ if (stacksize > end - prev_end)<br>
+ stacksize = end - prev_end;<br>
+ // When running with unlimited stack size, we still want to set some limit.<br>
+ // The unlimited stack size is caused by 'ulimit -s unlimited'.<br>
+ // Also, for some reason, GNU make spawns subprocesses with unlimited stack.<br>
+ if (stacksize > kMaxThreadStackSize)<br>
+ stacksize = kMaxThreadStackSize;<br>
+ *stack_top = end;<br>
+ *stack_bottom = end - stacksize;<br>
+ return;<br>
+ }<br>
+ pthread_attr_t attr;<br>
+ CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);<br>
+ uptr stacksize = 0;<br>
+ void *stackaddr = 0;<br>
+ my_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize);<br>
+ pthread_attr_destroy(&attr);<br>
+<br>
+ CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check.<br>
+ *stack_top = (uptr)stackaddr + stacksize;<br>
+ *stack_bottom = (uptr)stackaddr;<br>
+}<br>
+#endif // #ifndef SANITIZER_GO<br>
+<br>
+// Does not compile for Go because dlsym() requires -ldl<br>
+#ifndef SANITIZER_GO<br>
+bool SetEnv(const char *name, const char *value) {<br>
+ void *f = dlsym(RTLD_NEXT, "setenv");<br>
+ if (f == 0)<br>
+ return false;<br>
+ typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);<br>
+ setenv_ft setenv_f;<br>
+ CHECK_EQ(sizeof(setenv_f), sizeof(f));<br>
+ internal_memcpy(&setenv_f, &f, sizeof(f));<br>
+ return IndirectExternCall(setenv_f)(name, value, 1) == 0;<br>
+}<br>
+#endif<br>
+<br>
+bool SanitizerSetThreadName(const char *name) {<br>
+#ifdef PR_SET_NAME<br>
+ return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT<br>
+#else<br>
+ return false;<br>
+#endif<br>
+}<br>
+<br>
+bool SanitizerGetThreadName(char *name, int max_len) {<br>
+#ifdef PR_GET_NAME<br>
+ char buff[17];<br>
+ if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT<br>
+ return false;<br>
+ internal_strncpy(name, buff, max_len);<br>
+ name[max_len] = 0;<br>
+ return true;<br>
+#else<br>
+ return false;<br>
+#endif<br>
+}<br>
+<br>
+#ifndef SANITIZER_GO<br>
+//------------------------- SlowUnwindStack -----------------------------------<br>
+<br>
+typedef struct {<br>
+ uptr absolute_pc;<br>
+ uptr stack_top;<br>
+ uptr stack_size;<br>
+} backtrace_frame_t;<br>
+<br>
+extern "C" {<br>
+typedef void *(*acquire_my_map_info_list_func)();<br>
+typedef void (*release_my_map_info_list_func)(void *map);<br>
+typedef sptr (*unwind_backtrace_signal_arch_func)(<br>
+ void *siginfo, void *sigcontext, void *map_info_list,<br>
+ backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);<br>
+acquire_my_map_info_list_func acquire_my_map_info_list;<br>
+release_my_map_info_list_func release_my_map_info_list;<br>
+unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;<br>
+} // extern "C"<br>
+<br>
+#if SANITIZER_ANDROID<br>
+void SanitizerInitializeUnwinder() {<br>
+ void *p = dlopen("libcorkscrew.so", RTLD_LAZY);<br>
+ if (!p) {<br>
+ VReport(1,<br>
+ "Failed to open libcorkscrew.so. You may see broken stack traces "<br>
+ "in SEGV reports.");<br>
+ return;<br>
+ }<br>
+ acquire_my_map_info_list =<br>
+ (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");<br>
+ release_my_map_info_list =<br>
+ (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");<br>
+ unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(<br>
+ p, "unwind_backtrace_signal_arch");<br>
+ if (!acquire_my_map_info_list || !release_my_map_info_list ||<br>
+ !unwind_backtrace_signal_arch) {<br>
+ VReport(1,<br>
+ "Failed to find one of the required symbols in libcorkscrew.so. "<br>
+ "You may see broken stack traces in SEGV reports.");<br>
+ acquire_my_map_info_list = NULL;<br>
+ unwind_backtrace_signal_arch = NULL;<br>
+ release_my_map_info_list = NULL;<br>
+ }<br>
+}<br>
+#endif<br>
+<br>
+#ifdef __arm__<br>
+#define UNWIND_STOP _URC_END_OF_STACK<br>
+#define UNWIND_CONTINUE _URC_NO_REASON<br>
+#else<br>
+#define UNWIND_STOP _URC_NORMAL_STOP<br>
+#define UNWIND_CONTINUE _URC_NO_REASON<br>
+#endif<br>
+<br>
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {<br>
+#ifdef __arm__<br>
+ uptr val;<br>
+ _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,<br>
+ 15 /* r15 = PC */, _UVRSD_UINT32, &val);<br>
+ CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");<br>
+ // Clear the Thumb bit.<br>
+ return val & ~(uptr)1;<br>
+#else<br>
+ return _Unwind_GetIP(ctx);<br>
+#endif<br>
+}<br>
+<br>
+struct UnwindTraceArg {<br>
+ StackTrace *stack;<br>
+ uptr max_depth;<br>
+};<br>
+<br>
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {<br>
+ UnwindTraceArg *arg = (UnwindTraceArg*)param;<br>
+ CHECK_LT(arg->stack->size, arg->max_depth);<br>
+ uptr pc = Unwind_GetIP(ctx);<br>
+ arg->stack->trace[arg->stack->size++] = pc;<br>
+ if (arg->stack->size == arg->max_depth) return UNWIND_STOP;<br>
+ return UNWIND_CONTINUE;<br>
+}<br>
+<br>
+void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {<br>
+ size = 0;<br>
+ if (max_depth == 0)<br>
+ return;<br>
+ UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};<br>
+ _Unwind_Backtrace(Unwind_Trace, &arg);<br>
+ // We need to pop a few frames so that pc is on top.<br>
+ uptr to_pop = LocatePcInTrace(pc);<br>
+ // trace[0] belongs to the current function so we always pop it.<br>
+ if (to_pop == 0)<br>
+ to_pop = 1;<br>
+ PopStackFrames(to_pop);<br>
+ trace[0] = pc;<br>
+}<br>
+<br>
+void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,<br>
+ uptr max_depth) {<br>
+ if (!unwind_backtrace_signal_arch) {<br>
+ SlowUnwindStack(pc, max_depth);<br>
+ return;<br>
+ }<br>
+<br>
+ size = 0;<br>
+ if (max_depth == 0) return;<br>
+<br>
+ void *map = acquire_my_map_info_list();<br>
+ CHECK(map);<br>
+ InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);<br>
+ // siginfo argument appears to be unused.<br>
+ sptr res = unwind_backtrace_signal_arch(/* siginfo */ NULL, context, map,<br>
+ frames.data(),<br>
+ /* ignore_depth */ 0, max_depth);<br>
+ release_my_map_info_list(map);<br>
+ if (res < 0) return;<br>
+ CHECK((uptr)res <= kStackTraceMax);<br>
+<br>
+ // +2 compensate for libcorkscrew unwinder returning addresses of call<br>
+ // instructions instead of raw return addresses.<br>
+ for (sptr i = 0; i < res; ++i)<br>
+ trace[size++] = frames[i].absolute_pc + 2;<br>
+}<br>
+<br>
+#endif // !SANITIZER_GO<br>
+<br>
+static uptr g_tls_size;<br>
+<br>
+#ifdef __i386__<br>
+# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))<br>
+#else<br>
+# define DL_INTERNAL_FUNCTION<br>
+#endif<br>
+<br>
+void InitTlsSize() {<br>
+#if !defined(SANITIZER_GO) && !SANITIZER_ANDROID<br>
+ typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;<br>
+ get_tls_func get_tls;<br>
+ void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");<br>
+ CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr));<br>
+ internal_memcpy(&get_tls, &get_tls_static_info_ptr,<br>
+ sizeof(get_tls_static_info_ptr));<br>
+ CHECK_NE(get_tls, 0);<br>
+ size_t tls_size = 0;<br>
+ size_t tls_align = 0;<br>
+ IndirectExternCall(get_tls)(&tls_size, &tls_align);<br>
+ g_tls_size = tls_size;<br>
+#endif<br>
+}<br>
+<br>
+uptr GetTlsSize() {<br>
+ return g_tls_size;<br>
+}<br>
+<br>
+#if defined(__x86_64__) || defined(__i386__)<br>
+// sizeof(struct thread) from glibc.<br>
+static atomic_uintptr_t kThreadDescriptorSize;<br>
+<br>
+uptr ThreadDescriptorSize() {<br>
+ char buf[64];<br>
+ uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);<br>
+ if (val)<br>
+ return val;<br>
+#ifdef _CS_GNU_LIBC_VERSION<br>
+ uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));<br>
+ if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {<br>
+ char *end;<br>
+ int minor = internal_simple_strtoll(buf + 8, &end, 10);<br>
+ if (end != buf + 8 && (*end == '\0' || *end == '.')) {<br>
+ /* sizeof(struct thread) values from various glibc versions. */<br>
+ if (minor <= 3)<br>
+ val = FIRST_32_SECOND_64(1104, 1696);<br>
+ else if (minor == 4)<br>
+ val = FIRST_32_SECOND_64(1120, 1728);<br>
+ else if (minor == 5)<br>
+ val = FIRST_32_SECOND_64(1136, 1728);<br>
+ else if (minor <= 9)<br>
+ val = FIRST_32_SECOND_64(1136, 1712);<br>
+ else if (minor == 10)<br>
+ val = FIRST_32_SECOND_64(1168, 1776);<br>
+ else if (minor <= 12)<br>
+ val = FIRST_32_SECOND_64(1168, 2288);<br>
+ else<br>
+ val = FIRST_32_SECOND_64(1216, 2304);<br>
+ }<br>
+ if (val)<br>
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);<br>
+ return val;<br>
+ }<br>
+#endif<br>
+ return 0;<br>
+}<br>
+<br>
+// The offset at which pointer to self is located in the thread descriptor.<br>
+const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16);<br>
+<br>
+uptr ThreadSelfOffset() {<br>
+ return kThreadSelfOffset;<br>
+}<br>
+<br>
+uptr ThreadSelf() {<br>
+ uptr descr_addr;<br>
+#ifdef __i386__<br>
+ asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));<br>
+#else<br>
+ asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));<br>
+#endif<br>
+ return descr_addr;<br>
+}<br>
+#endif // defined(__x86_64__) || defined(__i386__)<br>
+<br>
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,<br>
+ uptr *tls_addr, uptr *tls_size) {<br>
+#ifndef SANITIZER_GO<br>
+#if defined(__x86_64__) || defined(__i386__)<br>
+ *tls_addr = ThreadSelf();<br>
+ *tls_size = GetTlsSize();<br>
+ *tls_addr -= *tls_size;<br>
+ *tls_addr += ThreadDescriptorSize();<br>
+#else<br>
+ *tls_addr = 0;<br>
+ *tls_size = 0;<br>
+#endif<br>
+<br>
+ uptr stack_top, stack_bottom;<br>
+ GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);<br>
+ *stk_addr = stack_bottom;<br>
+ *stk_size = stack_top - stack_bottom;<br>
+<br>
+ if (!main) {<br>
+ // If stack and tls intersect, make them non-intersecting.<br>
+ if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {<br>
+ CHECK_GT(*tls_addr + *tls_size, *stk_addr);<br>
+ CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);<br>
+ *stk_size -= *tls_size;<br>
+ *tls_addr = *stk_addr + *stk_size;<br>
+ }<br>
+ }<br>
+#else // SANITIZER_GO<br>
+ *stk_addr = 0;<br>
+ *stk_size = 0;<br>
+ *tls_addr = 0;<br>
+ *tls_size = 0;<br>
+#endif // SANITIZER_GO<br>
+}<br>
+<br>
+#ifndef SANITIZER_GO<br>
+void AdjustStackSize(void *attr_) {<br>
+ pthread_attr_t *attr = (pthread_attr_t *)attr_;<br>
+ uptr stackaddr = 0;<br>
+ size_t stacksize = 0;<br>
+ my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);<br>
+ // GLibC will return (0 - stacksize) as the stack address in the case when<br>
+ // stacksize is set, but stackaddr is not.<br>
+ bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);<br>
+ // We place a lot of tool data into TLS, account for that.<br>
+ const uptr minstacksize = GetTlsSize() + 128*1024;<br>
+ if (stacksize < minstacksize) {<br>
+ if (!stack_set) {<br>
+ if (stacksize != 0)<br>
+ VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,<br>
+ minstacksize);<br>
+ pthread_attr_setstacksize(attr, minstacksize);<br>
+ } else {<br>
+ Printf("Sanitizer: pre-allocated stack size is insufficient: "<br>
+ "%zu < %zu\n", stacksize, minstacksize);<br>
+ Printf("Sanitizer: pthread_create is likely to fail.\n");<br>
+ }<br>
+ }<br>
+}<br>
+#endif // SANITIZER_GO<br>
+<br>
+#if SANITIZER_ANDROID<br>
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules,<br>
+ string_predicate_t filter) {<br>
+ MemoryMappingLayout memory_mapping(false);<br>
+ return memory_mapping.DumpListOfModules(modules, max_modules, filter);<br>
+}<br>
+#else // SANITIZER_ANDROID<br>
+typedef ElfW(Phdr) Elf_Phdr;<br>
+<br>
+struct DlIteratePhdrData {<br>
+ LoadedModule *modules;<br>
+ uptr current_n;<br>
+ bool first;<br>
+ uptr max_n;<br>
+ string_predicate_t filter;<br>
+};<br>
+<br>
+static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {<br>
+ DlIteratePhdrData *data = (DlIteratePhdrData*)arg;<br>
+ if (data->current_n == data->max_n)<br>
+ return 0;<br>
+ InternalScopedBuffer<char> module_name(kMaxPathLength);<br>
+ module_name.data()[0] = '\0';<br>
+ if (data->first) {<br>
+ data->first = false;<br>
+ // First module is the binary itself.<br>
+ ReadBinaryName(module_name.data(), module_name.size());<br>
+ } else if (info->dlpi_name) {<br>
+ internal_strncpy(module_name.data(), info->dlpi_name, module_name.size());<br>
+ }<br>
+ if (module_name.data()[0] == '\0')<br>
+ return 0;<br>
+ if (data->filter && !data->filter(module_name.data()))<br>
+ return 0;<br>
+ void *mem = &data->modules[data->current_n];<br>
+ LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(),<br>
+ info->dlpi_addr);<br>
+ data->current_n++;<br>
+ for (int i = 0; i < info->dlpi_phnum; i++) {<br>
+ const Elf_Phdr *phdr = &info->dlpi_phdr[i];<br>
+ if (phdr->p_type == PT_LOAD) {<br>
+ uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;<br>
+ uptr cur_end = cur_beg + phdr->p_memsz;<br>
+ cur_module->addAddressRange(cur_beg, cur_end);<br>
+ }<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules,<br>
+ string_predicate_t filter) {<br>
+ CHECK(modules);<br>
+ DlIteratePhdrData data = {modules, 0, true, max_modules, filter};<br>
+ dl_iterate_phdr(dl_iterate_phdr_cb, &data);<br>
+ return data.current_n;<br>
+}<br>
+#endif // SANITIZER_ANDROID<br>
+<br>
+#ifndef SANITIZER_GO<br>
+uptr indirect_call_wrapper;<br>
+<br>
+void SetIndirectCallWrapper(uptr wrapper) {<br>
+ CHECK(!indirect_call_wrapper);<br>
+ CHECK(wrapper);<br>
+ indirect_call_wrapper = wrapper;<br>
+}<br>
+#endif<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_LINUX<br>
+//===-- sanitizer_stoptheworld_linux_libcdep.cc ---------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// See sanitizer_stoptheworld.h for details.<br>
+// This implementation was inspired by Markus Gutschke's linuxthreads.cc.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#if SANITIZER_LINUX && defined(__x86_64__)<br>
+<br>
+#include "sanitizer_stoptheworld.h"<br>
+<br>
+#include "sanitizer_platform_limits_posix.h"<br>
+<br>
+#include <errno.h><br>
+#include <sched.h> // for CLONE_* definitions<br>
+#include <stddef.h><br>
+#include <sys/prctl.h> // for PR_* definitions<br>
+#include <sys/ptrace.h> // for PTRACE_* definitions<br>
+#include <sys/types.h> // for pid_t<br>
+#if SANITIZER_ANDROID && defined(__arm__)<br>
+# include <linux/user.h> // for pt_regs<br>
+#else<br>
+# include <sys/user.h> // for user_regs_struct<br>
+#endif<br>
+#include <sys/wait.h> // for signal-related stuff<br>
+<br>
+#ifdef sa_handler<br>
+# undef sa_handler<br>
+#endif<br>
+<br>
+#ifdef sa_sigaction<br>
+# undef sa_sigaction<br>
+#endif<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_libc.h"<br>
+#include "sanitizer_linux.h"<br>
+#include "sanitizer_mutex.h"<br>
+#include "sanitizer_placement_new.h"<br>
+<br>
+// This module works by spawning a Linux task which then attaches to every<br>
+// thread in the caller process with ptrace. This suspends the threads, and<br>
+// PTRACE_GETREGS can then be used to obtain their register state. The callback<br>
+// supplied to StopTheWorld() is run in the tracer task while the threads are<br>
+// suspended.<br>
+// The tracer task must be placed in a different thread group for ptrace to<br>
+// work, so it cannot be spawned as a pthread. Instead, we use the low-level<br>
+// clone() interface (we want to share the address space with the caller<br>
+// process, so we prefer clone() over fork()).<br>
+//<br>
+// We don't use any libc functions, relying instead on direct syscalls. There<br>
+// are two reasons for this:<br>
+// 1. calling a library function while threads are suspended could cause a<br>
+// deadlock, if one of the treads happens to be holding a libc lock;<br>
+// 2. it's generally not safe to call libc functions from the tracer task,<br>
+// because clone() does not set up a thread-local storage for it. Any<br>
+// thread-local variables used by libc will be shared between the tracer task<br>
+// and the thread which spawned it.<br>
+<br>
+COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));<br>
+<br>
+namespace __sanitizer {<br>
+// This class handles thread suspending/unsuspending in the tracer thread.<br>
+class ThreadSuspender {<br>
+ public:<br>
+ explicit ThreadSuspender(pid_t pid)<br>
+ : pid_(pid) {<br>
+ CHECK_GE(pid, 0);<br>
+ }<br>
+ bool SuspendAllThreads();<br>
+ void ResumeAllThreads();<br>
+ void KillAllThreads();<br>
+ SuspendedThreadsList &suspended_threads_list() {<br>
+ return suspended_threads_list_;<br>
+ }<br>
+ private:<br>
+ SuspendedThreadsList suspended_threads_list_;<br>
+ pid_t pid_;<br>
+ bool SuspendThread(SuspendedThreadID thread_id);<br>
+};<br>
+<br>
+bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) {<br>
+ // Are we already attached to this thread?<br>
+ // Currently this check takes linear time, however the number of threads is<br>
+ // usually small.<br>
+ if (suspended_threads_list_.Contains(thread_id))<br>
+ return false;<br>
+ int pterrno;<br>
+ if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL),<br>
+ &pterrno)) {<br>
+ // Either the thread is dead, or something prevented us from attaching.<br>
+ // Log this event and move on.<br>
+ VReport(1, "Could not attach to thread %d (errno %d).\n", thread_id,<br>
+ pterrno);<br>
+ return false;<br>
+ } else {<br>
+ VReport(1, "Attached to thread %d.\n", thread_id);<br>
+ // The thread is not guaranteed to stop before ptrace returns, so we must<br>
+ // wait on it.<br>
+ uptr waitpid_status;<br>
+ HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __WALL));<br>
+ int wperrno;<br>
+ if (internal_iserror(waitpid_status, &wperrno)) {<br>
+ // Got a ECHILD error. I don't think this situation is possible, but it<br>
+ // doesn't hurt to report it.<br>
+ VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",<br>
+ thread_id, wperrno);<br>
+ internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL);<br>
+ return false;<br>
+ }<br>
+ suspended_threads_list_.Append(thread_id);<br>
+ return true;<br>
+ }<br>
+}<br>
+<br>
+void ThreadSuspender::ResumeAllThreads() {<br>
+ for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) {<br>
+ pid_t tid = suspended_threads_list_.GetThreadID(i);<br>
+ int pterrno;<br>
+ if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL),<br>
+ &pterrno)) {<br>
+ VReport(1, "Detached from thread %d.\n", tid);<br>
+ } else {<br>
+ // Either the thread is dead, or we are already detached.<br>
+ // The latter case is possible, for instance, if this function was called<br>
+ // from a signal handler.<br>
+ VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno);<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+void ThreadSuspender::KillAllThreads() {<br>
+ for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++)<br>
+ internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),<br>
+ NULL, NULL);<br>
+}<br>
+<br>
+bool ThreadSuspender::SuspendAllThreads() {<br>
+ ThreadLister thread_lister(pid_);<br>
+ bool added_threads;<br>
+ do {<br>
+ // Run through the directory entries once.<br>
+ added_threads = false;<br>
+ pid_t tid = thread_lister.GetNextTID();<br>
+ while (tid >= 0) {<br>
+ if (SuspendThread(tid))<br>
+ added_threads = true;<br>
+ tid = thread_lister.GetNextTID();<br>
+ }<br>
+ if (thread_lister.error()) {<br>
+ // Detach threads and fail.<br>
+ ResumeAllThreads();<br>
+ return false;<br>
+ }<br>
+ thread_lister.Reset();<br>
+ } while (added_threads);<br>
+ return true;<br>
+}<br>
+<br>
+// Pointer to the ThreadSuspender instance for use in signal handler.<br>
+static ThreadSuspender *thread_suspender_instance = NULL;<br>
+<br>
+// Signals that should not be blocked (this is used in the parent thread as well<br>
+// as the tracer thread).<br>
+static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV,<br>
+ SIGBUS, SIGXCPU, SIGXFSZ };<br>
+<br>
+// Structure for passing arguments into the tracer thread.<br>
+struct TracerThreadArgument {<br>
+ StopTheWorldCallback callback;<br>
+ void *callback_argument;<br>
+ // The tracer thread waits on this mutex while the parent finishes its<br>
+ // preparations.<br>
+ BlockingMutex mutex;<br>
+ uptr parent_pid;<br>
+};<br>
+<br>
+static DieCallbackType old_die_callback;<br>
+<br>
+// Signal handler to wake up suspended threads when the tracer thread dies.<br>
+void TracerThreadSignalHandler(int signum, void *siginfo, void *) {<br>
+ if (thread_suspender_instance != NULL) {<br>
+ if (signum == SIGABRT)<br>
+ thread_suspender_instance->KillAllThreads();<br>
+ else<br>
+ thread_suspender_instance->ResumeAllThreads();<br>
+ }<br>
+ internal__exit((signum == SIGABRT) ? 1 : 2);<br>
+}<br>
+<br>
+static void TracerThreadDieCallback() {<br>
+ // Generally a call to Die() in the tracer thread should be fatal to the<br>
+ // parent process as well, because they share the address space.<br>
+ // This really only works correctly if all the threads are suspended at this<br>
+ // point. So we correctly handle calls to Die() from within the callback, but<br>
+ // not those that happen before or after the callback. Hopefully there aren't<br>
+ // a lot of opportunities for that to happen...<br>
+ if (thread_suspender_instance)<br>
+ thread_suspender_instance->KillAllThreads();<br>
+ if (old_die_callback)<br>
+ old_die_callback();<br>
+}<br>
+<br>
+// Size of alternative stack for signal handlers in the tracer thread.<br>
+static const int kHandlerStackSize = 4096;<br>
+<br>
+// This function will be run as a cloned task.<br>
+static int TracerThread(void* argument) {<br>
+ TracerThreadArgument *tracer_thread_argument =<br>
+ (TracerThreadArgument *)argument;<br>
+<br>
+ internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);<br>
+ // Check if parent is already dead.<br>
+ if (internal_getppid() != tracer_thread_argument->parent_pid)<br>
+ internal__exit(4);<br>
+<br>
+ // Wait for the parent thread to finish preparations.<br>
+ tracer_thread_argument->mutex.Lock();<br>
+ tracer_thread_argument->mutex.Unlock();<br>
+<br>
+ SetDieCallback(TracerThreadDieCallback);<br>
+<br>
+ ThreadSuspender thread_suspender(internal_getppid());<br>
+ // Global pointer for the signal handler.<br>
+ thread_suspender_instance = &thread_suspender;<br>
+<br>
+ // Alternate stack for signal handling.<br>
+ InternalScopedBuffer<char> handler_stack_memory(kHandlerStackSize);<br>
+ struct sigaltstack handler_stack;<br>
+ internal_memset(&handler_stack, 0, sizeof(handler_stack));<br>
+ handler_stack.ss_sp = handler_stack_memory.data();<br>
+ handler_stack.ss_size = kHandlerStackSize;<br>
+ internal_sigaltstack(&handler_stack, NULL);<br>
+<br>
+ // Install our handler for fatal signals. Other signals should be blocked by<br>
+ // the mask we inherited from the caller thread.<br>
+ for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);<br>
+ signal_index++) {<br>
+ __sanitizer_sigaction new_sigaction;<br>
+ internal_memset(&new_sigaction, 0, sizeof(new_sigaction));<br>
+ new_sigaction.sigaction = TracerThreadSignalHandler;<br>
+ new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO;<br>
+ internal_sigfillset(&new_sigaction.sa_mask);<br>
+ internal_sigaction_norestorer(kUnblockedSignals[signal_index],<br>
+ &new_sigaction, NULL);<br>
+ }<br>
+<br>
+ int exit_code = 0;<br>
+ if (!thread_suspender.SuspendAllThreads()) {<br>
+ VReport(1, "Failed suspending threads.\n");<br>
+ exit_code = 3;<br>
+ } else {<br>
+ tracer_thread_argument->callback(thread_suspender.suspended_threads_list(),<br>
+ tracer_thread_argument->callback_argument);<br>
+ thread_suspender.ResumeAllThreads();<br>
+ exit_code = 0;<br>
+ }<br>
+ thread_suspender_instance = NULL;<br>
+ handler_stack.ss_flags = SS_DISABLE;<br>
+ internal_sigaltstack(&handler_stack, NULL);<br>
+ return exit_code;<br>
+}<br>
+<br>
+class ScopedStackSpaceWithGuard {<br>
+ public:<br>
+ explicit ScopedStackSpaceWithGuard(uptr stack_size) {<br>
+ stack_size_ = stack_size;<br>
+ guard_size_ = GetPageSizeCached();<br>
+ // FIXME: Omitting MAP_STACK here works in current kernels but might break<br>
+ // in the future.<br>
+ guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,<br>
+ "ScopedStackWithGuard");<br>
+ CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_));<br>
+ }<br>
+ ~ScopedStackSpaceWithGuard() {<br>
+ UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);<br>
+ }<br>
+ void *Bottom() const {<br>
+ return (void *)(guard_start_ + stack_size_ + guard_size_);<br>
+ }<br>
+<br>
+ private:<br>
+ uptr stack_size_;<br>
+ uptr guard_size_;<br>
+ uptr guard_start_;<br>
+};<br>
+<br>
+// We have a limitation on the stack frame size, so some stuff had to be moved<br>
+// into globals.<br>
+static __sanitizer_sigset_t blocked_sigset;<br>
+static __sanitizer_sigset_t old_sigset;<br>
+static __sanitizer_sigaction old_sigactions<br>
+ [ARRAY_SIZE(kUnblockedSignals)];<br>
+<br>
+class StopTheWorldScope {<br>
+ public:<br>
+ StopTheWorldScope() {<br>
+ // Block all signals that can be blocked safely, and install<br>
+ // default handlers for the remaining signals.<br>
+ // We cannot allow user-defined handlers to run while the ThreadSuspender<br>
+ // thread is active, because they could conceivably call some libc functions<br>
+ // which modify errno (which is shared between the two threads).<br>
+ internal_sigfillset(&blocked_sigset);<br>
+ for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);<br>
+ signal_index++) {<br>
+ // Remove the signal from the set of blocked signals.<br>
+ internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);<br>
+ // Install the default handler.<br>
+ __sanitizer_sigaction new_sigaction;<br>
+ internal_memset(&new_sigaction, 0, sizeof(new_sigaction));<br>
+ new_sigaction.handler = SIG_DFL;<br>
+ internal_sigfillset(&new_sigaction.sa_mask);<br>
+ internal_sigaction_norestorer(kUnblockedSignals[signal_index],<br>
+ &new_sigaction, &old_sigactions[signal_index]);<br>
+ }<br>
+ int sigprocmask_status =<br>
+ internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);<br>
+ CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail<br>
+ // Make this process dumpable. Processes that are not dumpable cannot be<br>
+ // attached to.<br>
+ process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);<br>
+ if (!process_was_dumpable_)<br>
+ internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);<br>
+ old_die_callback = GetDieCallback();<br>
+ }<br>
+<br>
+ ~StopTheWorldScope() {<br>
+ SetDieCallback(old_die_callback);<br>
+ // Restore the dumpable flag.<br>
+ if (!process_was_dumpable_)<br>
+ internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);<br>
+ // Restore the signal handlers.<br>
+ for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);<br>
+ signal_index++) {<br>
+ internal_sigaction_norestorer(kUnblockedSignals[signal_index],<br>
+ &old_sigactions[signal_index], NULL);<br>
+ }<br>
+ internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);<br>
+ }<br>
+<br>
+ private:<br>
+ int process_was_dumpable_;<br>
+};<br>
+<br>
+// When sanitizer output is being redirected to file (i.e. by using log_path),<br>
+// the tracer should write to the parent's log instead of trying to open a new<br>
+// file. Alert the logging code to the fact that we have a tracer.<br>
+struct ScopedSetTracerPID {<br>
+ explicit ScopedSetTracerPID(uptr tracer_pid) {<br>
+ stoptheworld_tracer_pid = tracer_pid;<br>
+ stoptheworld_tracer_ppid = internal_getpid();<br>
+ }<br>
+ ~ScopedSetTracerPID() {<br>
+ stoptheworld_tracer_pid = 0;<br>
+ stoptheworld_tracer_ppid = 0;<br>
+ }<br>
+};<br>
+<br>
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {<br>
+ StopTheWorldScope in_stoptheworld;<br>
+ // Prepare the arguments for TracerThread.<br>
+ struct TracerThreadArgument tracer_thread_argument;<br>
+ tracer_thread_argument.callback = callback;<br>
+ tracer_thread_argument.callback_argument = argument;<br>
+ tracer_thread_argument.parent_pid = internal_getpid();<br>
+ const uptr kTracerStackSize = 2 * 1024 * 1024;<br>
+ ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);<br>
+ // Block the execution of TracerThread until after we have set ptrace<br>
+ // permissions.<br>
+ tracer_thread_argument.mutex.Lock();<br>
+ uptr tracer_pid = internal_clone(<br>
+ TracerThread, tracer_stack.Bottom(),<br>
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,<br>
+ &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0<br>
+ /* child_tidptr */);<br>
+ int local_errno = 0;<br>
+ if (internal_iserror(tracer_pid, &local_errno)) {<br>
+ VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);<br>
+ tracer_thread_argument.mutex.Unlock();<br>
+ } else {<br>
+ ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid);<br>
+ // On some systems we have to explicitly declare that we want to be traced<br>
+ // by the tracer thread.<br>
+#ifdef PR_SET_PTRACER<br>
+ internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);<br>
+#endif<br>
+ // Allow the tracer thread to start.<br>
+ tracer_thread_argument.mutex.Unlock();<br>
+ // Since errno is shared between this thread and the tracer thread, we<br>
+ // must avoid using errno while the tracer thread is running.<br>
+ // At this point, any signal will either be blocked or kill us, so waitpid<br>
+ // should never return (and set errno) while the tracer thread is alive.<br>
+ uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);<br>
+ if (internal_iserror(waitpid_status, &local_errno))<br>
+ VReport(1, "Waiting on the tracer thread failed (errno %d).\n",<br>
+ local_errno);<br>
+ }<br>
+}<br>
+<br>
+// Platform-specific methods from SuspendedThreadsList.<br>
+#if SANITIZER_ANDROID && defined(__arm__)<br>
+typedef pt_regs regs_struct;<br>
+#define REG_SP ARM_sp<br>
+<br>
+#elif SANITIZER_LINUX && defined(__arm__)<br>
+typedef user_regs regs_struct;<br>
+#define REG_SP uregs[13]<br>
+<br>
+#elif defined(__i386__) || defined(__x86_64__)<br>
+typedef user_regs_struct regs_struct;<br>
+#if defined(__i386__)<br>
+#define REG_SP esp<br>
+#else<br>
+#define REG_SP rsp<br>
+#endif<br>
+<br>
+#elif defined(__powerpc__) || defined(__powerpc64__)<br>
+typedef pt_regs regs_struct;<br>
+#define REG_SP gpr[PT_R1]<br>
+<br>
+#elif defined(__mips__)<br>
+typedef struct user regs_struct;<br>
+#define REG_SP regs[EF_REG29]<br>
+<br>
+#else<br>
+#error "Unsupported architecture"<br>
+#endif // SANITIZER_ANDROID && defined(__arm__)<br>
+<br>
+int SuspendedThreadsList::GetRegistersAndSP(uptr index,<br>
+ uptr *buffer,<br>
+ uptr *sp) const {<br>
+ pid_t tid = GetThreadID(index);<br>
+ regs_struct regs;<br>
+ int pterrno;<br>
+ if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s),<br>
+ &pterrno)) {<br>
+ VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,<br>
+ pterrno);<br>
+ return -1;<br>
+ }<br>
+<br>
+ *sp = regs.REG_SP;<br>
+ internal_memcpy(buffer, ®s, sizeof(regs));<br>
+ return 0;<br>
+}<br>
+<br>
+uptr SuspendedThreadsList::RegisterCount() {<br>
+ return sizeof(regs_struct) / sizeof(uptr);<br>
+}<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_LINUX && defined(__x86_64__)<br>
+//===-- sanitizer_stackdepot.cc -------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_stackdepot.h"<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_internal_defs.h"<br>
+#include "sanitizer_mutex.h"<br>
+#include "sanitizer_atomic.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+const int kTabSize = 1024 * 1024; // Hash table size.<br>
+const int kPartBits = 8;<br>
+const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;<br>
+const int kPartCount = 1 << kPartBits; // Number of subparts in the table.<br>
+const int kPartSize = kTabSize / kPartCount;<br>
+const int kMaxId = 1 << kPartShift;<br>
+<br>
+struct StackDesc {<br>
+ StackDesc *link;<br>
+ u32 id;<br>
+ u32 hash;<br>
+ uptr size;<br>
+ uptr stack[1]; // [size]<br>
+};<br>
+<br>
+static struct {<br>
+ StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.<br>
+ atomic_uintptr_t region_pos; // Region allocator for StackDesc's.<br>
+ atomic_uintptr_t region_end;<br>
+ atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's.<br>
+ atomic_uint32_t seq[kPartCount]; // Unique id generators.<br>
+} depot;<br>
+<br>
+static StackDepotStats stats;<br>
+<br>
+StackDepotStats *StackDepotGetStats() {<br>
+ return &stats;<br>
+}<br>
+<br>
+static u32 hash(const uptr *stack, uptr size) {<br>
+ // murmur2<br>
+ const u32 m = 0x5bd1e995;<br>
+ const u32 seed = 0x9747b28c;<br>
+ const u32 r = 24;<br>
+ u32 h = seed ^ (size * sizeof(uptr));<br>
+ for (uptr i = 0; i < size; i++) {<br>
+ u32 k = stack[i];<br>
+ k *= m;<br>
+ k ^= k >> r;<br>
+ k *= m;<br>
+ h *= m;<br>
+ h ^= k;<br>
+ }<br>
+ h ^= h >> 13;<br>
+ h *= m;<br>
+ h ^= h >> 15;<br>
+ return h;<br>
+}<br>
+<br>
+static StackDesc *tryallocDesc(uptr memsz) {<br>
+ // Optimisic lock-free allocation, essentially try to bump the region ptr.<br>
+ for (;;) {<br>
+ uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);<br>
+ uptr end = atomic_load(&depot.region_end, memory_order_acquire);<br>
+ if (cmp == 0 || cmp + memsz > end)<br>
+ return 0;<br>
+ if (atomic_compare_exchange_weak(<br>
+ &depot.region_pos, &cmp, cmp + memsz,<br>
+ memory_order_acquire))<br>
+ return (StackDesc*)cmp;<br>
+ }<br>
+}<br>
+<br>
+static StackDesc *allocDesc(uptr size) {<br>
+ // First, try to allocate optimisitically.<br>
+ uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);<br>
+ StackDesc *s = tryallocDesc(memsz);<br>
+ if (s)<br>
+ return s;<br>
+ // If failed, lock, retry and alloc new superblock.<br>
+ SpinMutexLock l(&depot.mtx);<br>
+ for (;;) {<br>
+ s = tryallocDesc(memsz);<br>
+ if (s)<br>
+ return s;<br>
+ atomic_store(&depot.region_pos, 0, memory_order_relaxed);<br>
+ uptr allocsz = 64 * 1024;<br>
+ if (allocsz < memsz)<br>
+ allocsz = memsz;<br>
+ uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");<br>
+ stats.mapped += allocsz;<br>
+ atomic_store(&depot.region_end, mem + allocsz, memory_order_release);<br>
+ atomic_store(&depot.region_pos, mem, memory_order_release);<br>
+ }<br>
+}<br>
+<br>
+static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {<br>
+ // Searches linked list s for the stack, returns its id.<br>
+ for (; s; s = s->link) {<br>
+ if (s->hash == hash && s->size == size) {<br>
+ uptr i = 0;<br>
+ for (; i < size; i++) {<br>
+ if (stack[i] != s->stack[i])<br>
+ break;<br>
+ }<br>
+ if (i == size)<br>
+ return s->id;<br>
+ }<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+static StackDesc *lock(atomic_uintptr_t *p) {<br>
+ // Uses the pointer lsb as mutex.<br>
+ for (int i = 0;; i++) {<br>
+ uptr cmp = atomic_load(p, memory_order_relaxed);<br>
+ if ((cmp & 1) == 0<br>
+ && atomic_compare_exchange_weak(p, &cmp, cmp | 1,<br>
+ memory_order_acquire))<br>
+ return (StackDesc*)cmp;<br>
+ if (i < 10)<br>
+ proc_yield(10);<br>
+ else<br>
+ internal_sched_yield();<br>
+ }<br>
+}<br>
+<br>
+static void unlock(atomic_uintptr_t *p, StackDesc *s) {<br>
+ DCHECK_EQ((uptr)s & 1, 0);<br>
+ atomic_store(p, (uptr)s, memory_order_release);<br>
+}<br>
+<br>
+u32 StackDepotPut(const uptr *stack, uptr size) {<br>
+ if (stack == 0 || size == 0)<br>
+ return 0;<br>
+ uptr h = hash(stack, size);<br>
+ atomic_uintptr_t *p = &depot.tab[h % kTabSize];<br>
+ uptr v = atomic_load(p, memory_order_consume);<br>
+ StackDesc *s = (StackDesc*)(v & ~1);<br>
+ // First, try to find the existing stack.<br>
+ u32 id = find(s, stack, size, h);<br>
+ if (id)<br>
+ return id;<br>
+ // If failed, lock, retry and insert new.<br>
+ StackDesc *s2 = lock(p);<br>
+ if (s2 != s) {<br>
+ id = find(s2, stack, size, h);<br>
+ if (id) {<br>
+ unlock(p, s2);<br>
+ return id;<br>
+ }<br>
+ }<br>
+ uptr part = (h % kTabSize) / kPartSize;<br>
+ id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;<br>
+ stats.n_uniq_ids++;<br>
+ CHECK_LT(id, kMaxId);<br>
+ id |= part << kPartShift;<br>
+ CHECK_NE(id, 0);<br>
+ CHECK_EQ(id & (1u << 31), 0);<br>
+ s = allocDesc(size);<br>
+ s->id = id;<br>
+ s->hash = h;<br>
+ s->size = size;<br>
+ internal_memcpy(s->stack, stack, size * sizeof(uptr));<br>
+ s->link = s2;<br>
+ unlock(p, s);<br>
+ return id;<br>
+}<br>
+<br>
+const uptr *StackDepotGet(u32 id, uptr *size) {<br>
+ if (id == 0)<br>
+ return 0;<br>
+ CHECK_EQ(id & (1u << 31), 0);<br>
+ // High kPartBits contain part id, so we need to scan at most kPartSize lists.<br>
+ uptr part = id >> kPartShift;<br>
+ for (int i = 0; i != kPartSize; i++) {<br>
+ uptr idx = part * kPartSize + i;<br>
+ CHECK_LT(idx, kTabSize);<br>
+ atomic_uintptr_t *p = &depot.tab[idx];<br>
+ uptr v = atomic_load(p, memory_order_consume);<br>
+ StackDesc *s = (StackDesc*)(v & ~1);<br>
+ for (; s; s = s->link) {<br>
+ if (s->id == id) {<br>
+ *size = s->size;<br>
+ return s->stack;<br>
+ }<br>
+ }<br>
+ }<br>
+ *size = 0;<br>
+ return 0;<br>
+}<br>
+<br>
+bool StackDepotReverseMap::IdDescPair::IdComparator(<br>
+ const StackDepotReverseMap::IdDescPair &a,<br>
+ const StackDepotReverseMap::IdDescPair &b) {<br>
+ return <a href="http://a.id" target="_blank">a.id</a> < <a href="http://b.id" target="_blank">b.id</a>;<br>
+}<br>
+<br>
+StackDepotReverseMap::StackDepotReverseMap()<br>
+ : map_(StackDepotGetStats()->n_uniq_ids + 100) {<br>
+ for (int idx = 0; idx < kTabSize; idx++) {<br>
+ atomic_uintptr_t *p = &depot.tab[idx];<br>
+ uptr v = atomic_load(p, memory_order_consume);<br>
+ StackDesc *s = (StackDesc*)(v & ~1);<br>
+ for (; s; s = s->link) {<br>
+ IdDescPair pair = {s->id, s};<br>
+ map_.push_back(pair);<br>
+ }<br>
+ }<br>
+ InternalSort(&map_, map_.size(), IdDescPair::IdComparator);<br>
+}<br>
+<br>
+const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {<br>
+ if (!map_.size()) return 0;<br>
+ IdDescPair pair = {id, 0};<br>
+ uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,<br>
+ IdDescPair::IdComparator);<br>
+ if (idx > map_.size()) {<br>
+ *size = 0;<br>
+ return 0;<br>
+ }<br>
+ StackDesc *desc = map_[idx].desc;<br>
+ *size = desc->size;<br>
+ return desc->stack;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_stacktrace.cc -------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+uptr StackTrace::GetPreviousInstructionPc(uptr pc) {<br>
+#ifdef __arm__<br>
+ // Cancel Thumb bit.<br>
+ pc = pc & (~1);<br>
+#endif<br>
+#if defined(__sparc__)<br>
+ return pc - 8;<br>
+#else<br>
+ return pc - 1;<br>
+#endif<br>
+}<br>
+<br>
+uptr StackTrace::GetCurrentPc() {<br>
+ return GET_CALLER_PC();<br>
+}<br>
+<br>
+void StackTrace::FastUnwindStack(uptr pc, uptr bp,<br>
+ uptr stack_top, uptr stack_bottom,<br>
+ uptr max_depth) {<br>
+ if (max_depth == 0) {<br>
+ size = 0;<br>
+ return;<br>
+ }<br>
+ trace[0] = pc;<br>
+ size = 1;<br>
+ uptr *frame = (uptr *)bp;<br>
+ uptr *prev_frame = frame - 1;<br>
+ if (stack_top < 4096) return; // Sanity check for stack top.<br>
+ // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.<br>
+ while (frame > prev_frame &&<br>
+ frame < (uptr *)stack_top - 2 &&<br>
+ frame > (uptr *)stack_bottom &&<br>
+ IsAligned((uptr)frame, sizeof(*frame)) &&<br>
+ size < max_depth) {<br>
+ uptr pc1 = frame[1];<br>
+ if (pc1 != pc) {<br>
+ trace[size++] = pc1;<br>
+ }<br>
+ prev_frame = frame;<br>
+ frame = (uptr*)frame[0];<br>
+ }<br>
+}<br>
+<br>
+static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) {<br>
+ return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold;<br>
+}<br>
+<br>
+void StackTrace::PopStackFrames(uptr count) {<br>
+ CHECK_LT(count, size);<br>
+ size -= count;<br>
+ for (uptr i = 0; i < size; ++i) {<br>
+ trace[i] = trace[i + count];<br>
+ }<br>
+}<br>
+<br>
+uptr StackTrace::LocatePcInTrace(uptr pc) {<br>
+ // Use threshold to find PC in stack trace, as PC we want to unwind from may<br>
+ // slightly differ from return address in the actual unwinded stack trace.<br>
+ const int kPcThreshold = 192;<br>
+ for (uptr i = 0; i < size; ++i) {<br>
+ if (MatchPc(pc, trace[i], kPcThreshold))<br>
+ return i;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_stacktrace.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num,<br>
+ uptr pc) {<br>
+ buffer->append(" #%zu 0x%zx", frame_num, pc);<br>
+}<br>
+<br>
+void StackTrace::PrintStack(const uptr *addr, uptr size) {<br>
+ if (addr == 0 || size == 0) {<br>
+ Printf(" <empty stack>\n\n");<br>
+ return;<br>
+ }<br>
+ InternalScopedBuffer<char> buff(GetPageSizeCached() * 2);<br>
+ InternalScopedBuffer<AddressInfo> addr_frames(64);<br>
+ InternalScopedString frame_desc(GetPageSizeCached() * 2);<br>
+ uptr frame_num = 0;<br>
+ for (uptr i = 0; i < size && addr[i]; i++) {<br>
+ // PCs in stack traces are actually the return addresses, that is,<br>
+ // addresses of the next instructions after the call.<br>
+ uptr pc = GetPreviousInstructionPc(addr[i]);<br>
+ uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(<br>
+ pc, addr_frames.data(), addr_frames.size());<br>
+ for (uptr j = 0; j < addr_frames_num; j++) {<br>
+ AddressInfo &info = addr_frames[j];<br>
+ frame_desc.clear();<br>
+ PrintStackFramePrefix(&frame_desc, frame_num, pc);<br>
+ if (info.function) {<br>
+ frame_desc.append(" in %s", info.function);<br>
+ // Print offset in function if we don't know the source file.<br>
+ if (!info.file && info.function_offset != AddressInfo::kUnknown)<br>
+ frame_desc.append("+0x%zx", info.function_offset);<br>
+ }<br>
+ if (info.file) {<br>
+ frame_desc.append(" ");<br>
+ PrintSourceLocation(&frame_desc, info.file, info.line, info.column);<br>
+ } else if (info.module) {<br>
+ frame_desc.append(" ");<br>
+ PrintModuleAndOffset(&frame_desc, info.module, info.module_offset);<br>
+ }<br>
+ Printf("%s\n", frame_desc.data());<br>
+ frame_num++;<br>
+ info.Clear();<br>
+ }<br>
+ }<br>
+ // Always print a trailing empty line after stack trace.<br>
+ Printf("\n");<br>
+}<br>
+<br>
+void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,<br>
+ uptr stack_top, uptr stack_bottom,<br>
+ bool request_fast_unwind) {<br>
+ if (!WillUseFastUnwind(request_fast_unwind)) {<br>
+ if (context)<br>
+ SlowUnwindStackWithContext(pc, context, max_depth);<br>
+ else<br>
+ SlowUnwindStack(pc, max_depth);<br>
+ } else {<br>
+ FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);<br>
+ }<br>
+<br>
+ top_frame_bp = size ? bp : 0;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_symbolizer.cc -------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#include "sanitizer_internal_defs.h"<br>
+#include "sanitizer_placement_new.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+Symbolizer *Symbolizer::symbolizer_;<br>
+StaticSpinMutex Symbolizer::init_mu_;<br>
+LowLevelAllocator Symbolizer::symbolizer_allocator_;<br>
+<br>
+Symbolizer *Symbolizer::GetOrNull() {<br>
+ SpinMutexLock l(&init_mu_);<br>
+ return symbolizer_;<br>
+}<br>
+<br>
+Symbolizer *Symbolizer::Get() {<br>
+ SpinMutexLock l(&init_mu_);<br>
+ RAW_CHECK_MSG(symbolizer_ != 0, "Using uninitialized symbolizer!");<br>
+ return symbolizer_;<br>
+}<br>
+<br>
+Symbolizer *Symbolizer::Disable() {<br>
+ CHECK_EQ(0, symbolizer_);<br>
+ // Initialize a dummy symbolizer.<br>
+ symbolizer_ = new(symbolizer_allocator_) Symbolizer;<br>
+ return symbolizer_;<br>
+}<br>
+<br>
+void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,<br>
+ Symbolizer::EndSymbolizationHook end_hook) {<br>
+ CHECK(start_hook_ == 0 && end_hook_ == 0);<br>
+ start_hook_ = start_hook;<br>
+ end_hook_ = end_hook;<br>
+}<br>
+<br>
+Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {}<br>
+<br>
+Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)<br>
+ : sym_(sym) {<br>
+ if (sym_->start_hook_)<br>
+ sym_->start_hook_();<br>
+}<br>
+<br>
+Symbolizer::SymbolizerScope::~SymbolizerScope() {<br>
+ if (sym_->end_hook_)<br>
+ sym_->end_hook_();<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_symbolizer_libcdep.cc -----------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_internal_defs.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+Symbolizer *Symbolizer::CreateAndStore(const char *path_to_external) {<br>
+ Symbolizer *platform_symbolizer = PlatformInit(path_to_external);<br>
+ if (!platform_symbolizer)<br>
+ return Disable();<br>
+ symbolizer_ = platform_symbolizer;<br>
+ return platform_symbolizer;<br>
+}<br>
+<br>
+Symbolizer *Symbolizer::Init(const char *path_to_external) {<br>
+ CHECK_EQ(0, symbolizer_);<br>
+ return CreateAndStore(path_to_external);<br>
+}<br>
+<br>
+Symbolizer *Symbolizer::GetOrInit() {<br>
+ SpinMutexLock l(&init_mu_);<br>
+ if (symbolizer_ == 0)<br>
+ return CreateAndStore(0);<br>
+ return symbolizer_;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+// POSIX-specific implementation of symbolizer parts.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+#if SANITIZER_POSIX<br>
+#include "sanitizer_allocator_internal.h"<br>
+#include "sanitizer_common.h"<br>
+#include "sanitizer_flags.h"<br>
+#include "sanitizer_internal_defs.h"<br>
+#include "sanitizer_linux.h"<br>
+#include "sanitizer_placement_new.h"<br>
+#include "sanitizer_procmaps.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+#include "sanitizer_symbolizer_libbacktrace.h"<br>
+<br>
+#include <errno.h><br>
+#include <stdlib.h><br>
+#include <sys/wait.h><br>
+#include <unistd.h><br>
+<br>
+// C++ demangling function, as required by Itanium C++ ABI. This is weak,<br>
+// because we do not require a C++ ABI library to be linked to a program<br>
+// using sanitizers; if it's not present, we'll just use the mangled name.<br>
+//<br>
+// On Android, this is not weak, because we are using shared runtime library<br>
+// AND static libstdc++, and there is no good way to conditionally export<br>
+// __cxa_demangle. By making this a non-weak symbol, we statically link<br>
+// __cxa_demangle into ASan runtime library.<br>
+namespace __cxxabiv1 {<br>
+ extern "C"<br>
+#if !SANITIZER_ANDROID<br>
+ SANITIZER_WEAK_ATTRIBUTE<br>
+#endif<br>
+ char *__cxa_demangle(const char *mangled, char *buffer, size_t *length,<br>
+ int *status);<br>
+}<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.<br>
+static const char *DemangleCXXABI(const char *name) {<br>
+ // FIXME: __cxa_demangle aggressively insists on allocating memory.<br>
+ // There's not much we can do about that, short of providing our<br>
+ // own demangler (libc++abi's implementation could be adapted so that<br>
+ // it does not allocate). For now, we just call it anyway, and we leak<br>
+ // the returned value.<br>
+ if (SANITIZER_ANDROID || &__cxxabiv1::__cxa_demangle)<br>
+ if (const char *demangled_name =<br>
+ __cxxabiv1::__cxa_demangle(name, 0, 0, 0))<br>
+ return demangled_name;<br>
+<br>
+ return name;<br>
+}<br>
+<br>
+// Extracts the prefix of "str" that consists of any characters not<br>
+// present in "delims" string, and copies this prefix to "result", allocating<br>
+// space for it.<br>
+// Returns a pointer to "str" after skipping extracted prefix and first<br>
+// delimiter char.<br>
+static const char *ExtractToken(const char *str, const char *delims,<br>
+ char **result) {<br>
+ uptr prefix_len = internal_strcspn(str, delims);<br>
+ *result = (char*)InternalAlloc(prefix_len + 1);<br>
+ internal_memcpy(*result, str, prefix_len);<br>
+ (*result)[prefix_len] = '\0';<br>
+ const char *prefix_end = str + prefix_len;<br>
+ if (*prefix_end != '\0') prefix_end++;<br>
+ return prefix_end;<br>
+}<br>
+<br>
+// Same as ExtractToken, but converts extracted token to integer.<br>
+static const char *ExtractInt(const char *str, const char *delims,<br>
+ int *result) {<br>
+ char *buff;<br>
+ const char *ret = ExtractToken(str, delims, &buff);<br>
+ if (buff != 0) {<br>
+ *result = (int)internal_atoll(buff);<br>
+ }<br>
+ InternalFree(buff);<br>
+ return ret;<br>
+}<br>
+<br>
+static const char *ExtractUptr(const char *str, const char *delims,<br>
+ uptr *result) {<br>
+ char *buff;<br>
+ const char *ret = ExtractToken(str, delims, &buff);<br>
+ if (buff != 0) {<br>
+ *result = (uptr)internal_atoll(buff);<br>
+ }<br>
+ InternalFree(buff);<br>
+ return ret;<br>
+}<br>
+<br>
+class ExternalSymbolizerInterface {<br>
+ public:<br>
+ // Can't declare pure virtual functions in sanitizer runtimes:<br>
+ // __cxa_pure_virtual might be unavailable.<br>
+ virtual char *SendCommand(bool is_data, const char *module_name,<br>
+ uptr module_offset) {<br>
+ UNIMPLEMENTED();<br>
+ }<br>
+};<br>
+<br>
+// SymbolizerProcess encapsulates communication between the tool and<br>
+// external symbolizer program, running in a different subprocess.<br>
+// SymbolizerProcess may not be used from two threads simultaneously.<br>
+class SymbolizerProcess : public ExternalSymbolizerInterface {<br>
+ public:<br>
+ explicit SymbolizerProcess(const char *path)<br>
+ : path_(path),<br>
+ input_fd_(kInvalidFd),<br>
+ output_fd_(kInvalidFd),<br>
+ times_restarted_(0),<br>
+ failed_to_start_(false),<br>
+ reported_invalid_path_(false) {<br>
+ CHECK(path_);<br>
+ CHECK_NE(path_[0], '\0');<br>
+ }<br>
+<br>
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {<br>
+ for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {<br>
+ // Start or restart symbolizer if we failed to send command to it.<br>
+ if (char *res = SendCommandImpl(is_data, module_name, module_offset))<br>
+ return res;<br>
+ Restart();<br>
+ }<br>
+ if (!failed_to_start_) {<br>
+ Report("WARNING: Failed to use and restart external symbolizer!\n");<br>
+ failed_to_start_ = true;<br>
+ }<br>
+ return 0;<br>
+ }<br>
+<br>
+ private:<br>
+ bool Restart() {<br>
+ if (input_fd_ != kInvalidFd)<br>
+ internal_close(input_fd_);<br>
+ if (output_fd_ != kInvalidFd)<br>
+ internal_close(output_fd_);<br>
+ return StartSymbolizerSubprocess();<br>
+ }<br>
+<br>
+ char *SendCommandImpl(bool is_data, const char *module_name,<br>
+ uptr module_offset) {<br>
+ if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)<br>
+ return 0;<br>
+ CHECK(module_name);<br>
+ if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name,<br>
+ module_offset))<br>
+ return 0;<br>
+ if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))<br>
+ return 0;<br>
+ if (!readFromSymbolizer(buffer_, kBufferSize))<br>
+ return 0;<br>
+ return buffer_;<br>
+ }<br>
+<br>
+ bool readFromSymbolizer(char *buffer, uptr max_length) {<br>
+ if (max_length == 0)<br>
+ return true;<br>
+ uptr read_len = 0;<br>
+ while (true) {<br>
+ uptr just_read = internal_read(input_fd_, buffer + read_len,<br>
+ max_length - read_len - 1);<br>
+ // We can't read 0 bytes, as we don't expect external symbolizer to close<br>
+ // its stdout.<br>
+ if (just_read == 0 || just_read == (uptr)-1) {<br>
+ Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);<br>
+ return false;<br>
+ }<br>
+ read_len += just_read;<br>
+ if (ReachedEndOfOutput(buffer, read_len))<br>
+ break;<br>
+ }<br>
+ buffer[read_len] = '\0';<br>
+ return true;<br>
+ }<br>
+<br>
+ bool writeToSymbolizer(const char *buffer, uptr length) {<br>
+ if (length == 0)<br>
+ return true;<br>
+ uptr write_len = internal_write(output_fd_, buffer, length);<br>
+ if (write_len == 0 || write_len == (uptr)-1) {<br>
+ Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);<br>
+ return false;<br>
+ }<br>
+ return true;<br>
+ }<br>
+<br>
+ bool StartSymbolizerSubprocess() {<br>
+ if (!FileExists(path_)) {<br>
+ if (!reported_invalid_path_) {<br>
+ Report("WARNING: invalid path to external symbolizer!\n");<br>
+ reported_invalid_path_ = true;<br>
+ }<br>
+ return false;<br>
+ }<br>
+<br>
+ int *infd = NULL;<br>
+ int *outfd = NULL;<br>
+ // The client program may close its stdin and/or stdout and/or stderr<br>
+ // thus allowing socketpair to reuse file descriptors 0, 1 or 2.<br>
+ // In this case the communication between the forked processes may be<br>
+ // broken if either the parent or the child tries to close or duplicate<br>
+ // these descriptors. The loop below produces two pairs of file<br>
+ // descriptors, each greater than 2 (stderr).<br>
+ int sock_pair[5][2];<br>
+ for (int i = 0; i < 5; i++) {<br>
+ if (pipe(sock_pair[i]) == -1) {<br>
+ for (int j = 0; j < i; j++) {<br>
+ internal_close(sock_pair[j][0]);<br>
+ internal_close(sock_pair[j][1]);<br>
+ }<br>
+ Report("WARNING: Can't create a socket pair to start "<br>
+ "external symbolizer (errno: %d)\n", errno);<br>
+ return false;<br>
+ } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {<br>
+ if (infd == NULL) {<br>
+ infd = sock_pair[i];<br>
+ } else {<br>
+ outfd = sock_pair[i];<br>
+ for (int j = 0; j < i; j++) {<br>
+ if (sock_pair[j] == infd) continue;<br>
+ internal_close(sock_pair[j][0]);<br>
+ internal_close(sock_pair[j][1]);<br>
+ }<br>
+ break;<br>
+ }<br>
+ }<br>
+ }<br>
+ CHECK(infd);<br>
+ CHECK(outfd);<br>
+<br>
+ int pid = fork();<br>
+ if (pid == -1) {<br>
+ // Fork() failed.<br>
+ internal_close(infd[0]);<br>
+ internal_close(infd[1]);<br>
+ internal_close(outfd[0]);<br>
+ internal_close(outfd[1]);<br>
+ Report("WARNING: failed to fork external symbolizer "<br>
+ " (errno: %d)\n", errno);<br>
+ return false;<br>
+ } else if (pid == 0) {<br>
+ // Child subprocess.<br>
+ internal_close(STDOUT_FILENO);<br>
+ internal_close(STDIN_FILENO);<br>
+ internal_dup2(outfd[0], STDIN_FILENO);<br>
+ internal_dup2(infd[1], STDOUT_FILENO);<br>
+ internal_close(outfd[0]);<br>
+ internal_close(outfd[1]);<br>
+ internal_close(infd[0]);<br>
+ internal_close(infd[1]);<br>
+ for (int fd = getdtablesize(); fd > 2; fd--)<br>
+ internal_close(fd);<br>
+ ExecuteWithDefaultArgs(path_);<br>
+ internal__exit(1);<br>
+ }<br>
+<br>
+ // Continue execution in parent process.<br>
+ internal_close(outfd[0]);<br>
+ internal_close(infd[1]);<br>
+ input_fd_ = infd[0];<br>
+ output_fd_ = outfd[1];<br>
+<br>
+ // Check that symbolizer subprocess started successfully.<br>
+ int pid_status;<br>
+ SleepForMillis(kSymbolizerStartupTimeMillis);<br>
+ int exited_pid = waitpid(pid, &pid_status, WNOHANG);<br>
+ if (exited_pid != 0) {<br>
+ // Either waitpid failed, or child has already exited.<br>
+ Report("WARNING: external symbolizer didn't start up correctly!\n");<br>
+ return false;<br>
+ }<br>
+<br>
+ return true;<br>
+ }<br>
+<br>
+ virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,<br>
+ const char *module_name,<br>
+ uptr module_offset) const {<br>
+ UNIMPLEMENTED();<br>
+ }<br>
+<br>
+ virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {<br>
+ UNIMPLEMENTED();<br>
+ }<br>
+<br>
+ virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const {<br>
+ UNIMPLEMENTED();<br>
+ }<br>
+<br>
+ const char *path_;<br>
+ int input_fd_;<br>
+ int output_fd_;<br>
+<br>
+ static const uptr kBufferSize = 16 * 1024;<br>
+ char buffer_[kBufferSize];<br>
+<br>
+ static const uptr kMaxTimesRestarted = 5;<br>
+ static const int kSymbolizerStartupTimeMillis = 10;<br>
+ uptr times_restarted_;<br>
+ bool failed_to_start_;<br>
+ bool reported_invalid_path_;<br>
+};<br>
+<br>
+// For now we assume the following protocol:<br>
+// For each request of the form<br>
+// <module_name> <module_offset><br>
+// passed to STDIN, external symbolizer prints to STDOUT response:<br>
+// <function_name><br>
+// <file_name>:<line_number>:<column_number><br>
+// <function_name><br>
+// <file_name>:<line_number>:<column_number><br>
+// ...<br>
+// <empty line><br>
+class LLVMSymbolizerProcess : public SymbolizerProcess {<br>
+ public:<br>
+ explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}<br>
+<br>
+ private:<br>
+ bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,<br>
+ const char *module_name, uptr module_offset) const {<br>
+ internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n",<br>
+ is_data ? "DATA " : "", module_name, module_offset);<br>
+ return true;<br>
+ }<br>
+<br>
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const {<br>
+ // Empty line marks the end of llvm-symbolizer output.<br>
+ return length >= 2 && buffer[length - 1] == '\n' &&<br>
+ buffer[length - 2] == '\n';<br>
+ }<br>
+<br>
+ void ExecuteWithDefaultArgs(const char *path_to_binary) const {<br>
+#if defined(__x86_64__)<br>
+ const char* const kSymbolizerArch = "--default-arch=x86_64";<br>
+#elif defined(__i386__)<br>
+ const char* const kSymbolizerArch = "--default-arch=i386";<br>
+#elif defined(__powerpc64__)<br>
+ const char* const kSymbolizerArch = "--default-arch=powerpc64";<br>
+#else<br>
+ const char* const kSymbolizerArch = "--default-arch=unknown";<br>
+#endif<br>
+ execl(path_to_binary, path_to_binary, kSymbolizerArch, (char *)0);<br>
+ }<br>
+};<br>
+<br>
+class Addr2LineProcess : public SymbolizerProcess {<br>
+ public:<br>
+ Addr2LineProcess(const char *path, const char *module_name)<br>
+ : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}<br>
+<br>
+ const char *module_name() const { return module_name_; }<br>
+<br>
+ private:<br>
+ bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,<br>
+ const char *module_name, uptr module_offset) const {<br>
+ if (is_data)<br>
+ return false;<br>
+ CHECK_EQ(0, internal_strcmp(module_name, module_name_));<br>
+ internal_snprintf(buffer, max_length, "0x%zx\n", module_offset);<br>
+ return true;<br>
+ }<br>
+<br>
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const {<br>
+ // Output should consist of two lines.<br>
+ int num_lines = 0;<br>
+ for (uptr i = 0; i < length; ++i) {<br>
+ if (buffer[i] == '\n')<br>
+ num_lines++;<br>
+ if (num_lines >= 2)<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+ }<br>
+<br>
+ void ExecuteWithDefaultArgs(const char *path_to_binary) const {<br>
+ execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0);<br>
+ }<br>
+<br>
+ const char *module_name_; // Owned, leaked.<br>
+};<br>
+<br>
+class Addr2LinePool : public ExternalSymbolizerInterface {<br>
+ public:<br>
+ explicit Addr2LinePool(const char *addr2line_path,<br>
+ LowLevelAllocator *allocator)<br>
+ : addr2line_path_(addr2line_path), allocator_(allocator),<br>
+ addr2line_pool_(16) {}<br>
+<br>
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {<br>
+ if (is_data)<br>
+ return 0;<br>
+ Addr2LineProcess *addr2line = 0;<br>
+ for (uptr i = 0; i < addr2line_pool_.size(); ++i) {<br>
+ if (0 ==<br>
+ internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {<br>
+ addr2line = addr2line_pool_[i];<br>
+ break;<br>
+ }<br>
+ }<br>
+ if (!addr2line) {<br>
+ addr2line =<br>
+ new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);<br>
+ addr2line_pool_.push_back(addr2line);<br>
+ }<br>
+ return addr2line->SendCommand(is_data, module_name, module_offset);<br>
+ }<br>
+<br>
+ private:<br>
+ const char *addr2line_path_;<br>
+ LowLevelAllocator *allocator_;<br>
+ InternalMmapVector<Addr2LineProcess*> addr2line_pool_;<br>
+};<br>
+<br>
+#if SANITIZER_SUPPORTS_WEAK_HOOKS<br>
+extern "C" {<br>
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE<br>
+bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,<br>
+ char *Buffer, int MaxLength);<br>
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE<br>
+bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,<br>
+ char *Buffer, int MaxLength);<br>
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE<br>
+void __sanitizer_symbolize_flush();<br>
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE<br>
+int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,<br>
+ int MaxLength);<br>
+} // extern "C"<br>
+<br>
+class InternalSymbolizer {<br>
+ public:<br>
+ typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int);<br>
+<br>
+ static InternalSymbolizer *get(LowLevelAllocator *alloc) {<br>
+ if (__sanitizer_symbolize_code != 0 &&<br>
+ __sanitizer_symbolize_data != 0) {<br>
+ return new(*alloc) InternalSymbolizer();<br>
+ }<br>
+ return 0;<br>
+ }<br>
+<br>
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {<br>
+ SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data<br>
+ : __sanitizer_symbolize_code;<br>
+ if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize))<br>
+ return buffer_;<br>
+ return 0;<br>
+ }<br>
+<br>
+ void Flush() {<br>
+ if (__sanitizer_symbolize_flush)<br>
+ __sanitizer_symbolize_flush();<br>
+ }<br>
+<br>
+ const char *Demangle(const char *name) {<br>
+ if (__sanitizer_symbolize_demangle) {<br>
+ for (uptr res_length = 1024;<br>
+ res_length <= InternalSizeClassMap::kMaxSize;) {<br>
+ char *res_buff = static_cast<char*>(InternalAlloc(res_length));<br>
+ uptr req_length =<br>
+ __sanitizer_symbolize_demangle(name, res_buff, res_length);<br>
+ if (req_length > res_length) {<br>
+ res_length = req_length + 1;<br>
+ InternalFree(res_buff);<br>
+ continue;<br>
+ }<br>
+ return res_buff;<br>
+ }<br>
+ }<br>
+ return name;<br>
+ }<br>
+<br>
+ private:<br>
+ InternalSymbolizer() { }<br>
+<br>
+ static const int kBufferSize = 16 * 1024;<br>
+ static const int kMaxDemangledNameSize = 1024;<br>
+ char buffer_[kBufferSize];<br>
+};<br>
+#else // SANITIZER_SUPPORTS_WEAK_HOOKS<br>
+<br>
+class InternalSymbolizer {<br>
+ public:<br>
+ static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }<br>
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {<br>
+ return 0;<br>
+ }<br>
+ void Flush() { }<br>
+ const char *Demangle(const char *name) { return name; }<br>
+};<br>
+<br>
+#endif // SANITIZER_SUPPORTS_WEAK_HOOKS<br>
+<br>
+class POSIXSymbolizer : public Symbolizer {<br>
+ public:<br>
+ POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer,<br>
+ InternalSymbolizer *internal_symbolizer,<br>
+ LibbacktraceSymbolizer *libbacktrace_symbolizer)<br>
+ : Symbolizer(),<br>
+ external_symbolizer_(external_symbolizer),<br>
+ internal_symbolizer_(internal_symbolizer),<br>
+ libbacktrace_symbolizer_(libbacktrace_symbolizer) {}<br>
+<br>
+ uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) {<br>
+ BlockingMutexLock l(&mu_);<br>
+ if (max_frames == 0)<br>
+ return 0;<br>
+ const char *module_name;<br>
+ uptr module_offset;<br>
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))<br>
+ return 0;<br>
+ // First, try to use libbacktrace symbolizer (if it's available).<br>
+ if (libbacktrace_symbolizer_ != 0) {<br>
+ mu_.CheckLocked();<br>
+ uptr res = libbacktrace_symbolizer_->SymbolizeCode(<br>
+ addr, frames, max_frames, module_name, module_offset);<br>
+ if (res > 0)<br>
+ return res;<br>
+ }<br>
+ const char *str = SendCommand(false, module_name, module_offset);<br>
+ if (str == 0) {<br>
+ // Symbolizer was not initialized or failed. Fill only data<br>
+ // about module name and offset.<br>
+ AddressInfo *info = &frames[0];<br>
+ info->Clear();<br>
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);<br>
+ return 1;<br>
+ }<br>
+ uptr frame_id = 0;<br>
+ for (frame_id = 0; frame_id < max_frames; frame_id++) {<br>
+ AddressInfo *info = &frames[frame_id];<br>
+ char *function_name = 0;<br>
+ str = ExtractToken(str, "\n", &function_name);<br>
+ CHECK(function_name);<br>
+ if (function_name[0] == '\0') {<br>
+ // There are no more frames.<br>
+ break;<br>
+ }<br>
+ info->Clear();<br>
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);<br>
+ info->function = function_name;<br>
+ // Parse <file>:<line>:<column> buffer.<br>
+ char *file_line_info = 0;<br>
+ str = ExtractToken(str, "\n", &file_line_info);<br>
+ CHECK(file_line_info);<br>
+ const char *line_info = ExtractToken(file_line_info, ":", &info->file);<br>
+ line_info = ExtractInt(line_info, ":", &info->line);<br>
+ line_info = ExtractInt(line_info, "", &info->column);<br>
+ InternalFree(file_line_info);<br>
+<br>
+ // Functions and filenames can be "??", in which case we write 0<br>
+ // to address info to mark that names are unknown.<br>
+ if (0 == internal_strcmp(info->function, "??")) {<br>
+ InternalFree(info->function);<br>
+ info->function = 0;<br>
+ }<br>
+ if (0 == internal_strcmp(info->file, "??")) {<br>
+ InternalFree(info->file);<br>
+ info->file = 0;<br>
+ }<br>
+ }<br>
+ if (frame_id == 0) {<br>
+ // Make sure we return at least one frame.<br>
+ AddressInfo *info = &frames[0];<br>
+ info->Clear();<br>
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);<br>
+ frame_id = 1;<br>
+ }<br>
+ return frame_id;<br>
+ }<br>
+<br>
+ bool SymbolizeData(uptr addr, DataInfo *info) {<br>
+ BlockingMutexLock l(&mu_);<br>
+ LoadedModule *module = FindModuleForAddress(addr);<br>
+ if (module == 0)<br>
+ return false;<br>
+ const char *module_name = module->full_name();<br>
+ uptr module_offset = addr - module->base_address();<br>
+ internal_memset(info, 0, sizeof(*info));<br>
+ info->address = addr;<br>
+ info->module = internal_strdup(module_name);<br>
+ info->module_offset = module_offset;<br>
+ // First, try to use libbacktrace symbolizer (if it's available).<br>
+ if (libbacktrace_symbolizer_ != 0) {<br>
+ mu_.CheckLocked();<br>
+ if (libbacktrace_symbolizer_->SymbolizeData(info))<br>
+ return true;<br>
+ }<br>
+ const char *str = SendCommand(true, module_name, module_offset);<br>
+ if (str == 0)<br>
+ return true;<br>
+ str = ExtractToken(str, "\n", &info->name);<br>
+ str = ExtractUptr(str, " ", &info->start);<br>
+ str = ExtractUptr(str, "\n", &info->size);<br>
+ info->start += module->base_address();<br>
+ return true;<br>
+ }<br>
+<br>
+ bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,<br>
+ uptr *module_address) {<br>
+ BlockingMutexLock l(&mu_);<br>
+ return FindModuleNameAndOffsetForAddress(pc, module_name, module_address);<br>
+ }<br>
+<br>
+ bool CanReturnFileLineInfo() {<br>
+ return internal_symbolizer_ != 0 || external_symbolizer_ != 0 ||<br>
+ libbacktrace_symbolizer_ != 0;<br>
+ }<br>
+<br>
+ void Flush() {<br>
+ BlockingMutexLock l(&mu_);<br>
+ if (internal_symbolizer_ != 0) {<br>
+ SymbolizerScope sym_scope(this);<br>
+ internal_symbolizer_->Flush();<br>
+ }<br>
+ }<br>
+<br>
+ const char *Demangle(const char *name) {<br>
+ BlockingMutexLock l(&mu_);<br>
+ // Run hooks even if we don't use internal symbolizer, as cxxabi<br>
+ // demangle may call system functions.<br>
+ SymbolizerScope sym_scope(this);<br>
+ // Try to use libbacktrace demangler (if available).<br>
+ if (libbacktrace_symbolizer_ != 0) {<br>
+ if (const char *demangled = libbacktrace_symbolizer_->Demangle(name))<br>
+ return demangled;<br>
+ }<br>
+ if (internal_symbolizer_ != 0)<br>
+ return internal_symbolizer_->Demangle(name);<br>
+ return DemangleCXXABI(name);<br>
+ }<br>
+<br>
+ void PrepareForSandboxing() {<br>
+#if SANITIZER_LINUX && !SANITIZER_ANDROID<br>
+ BlockingMutexLock l(&mu_);<br>
+ // Cache /proc/self/exe on Linux.<br>
+ CacheBinaryName();<br>
+#endif<br>
+ }<br>
+<br>
+ private:<br>
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {<br>
+ mu_.CheckLocked();<br>
+ // First, try to use internal symbolizer.<br>
+ if (internal_symbolizer_) {<br>
+ SymbolizerScope sym_scope(this);<br>
+ return internal_symbolizer_->SendCommand(is_data, module_name,<br>
+ module_offset);<br>
+ }<br>
+ // Otherwise, fall back to external symbolizer.<br>
+ if (external_symbolizer_) {<br>
+ SymbolizerScope sym_scope(this);<br>
+ return external_symbolizer_->SendCommand(is_data, module_name,<br>
+ module_offset);<br>
+ }<br>
+ return 0;<br>
+ }<br>
+<br>
+ LoadedModule *FindModuleForAddress(uptr address) {<br>
+ mu_.CheckLocked();<br>
+ bool modules_were_reloaded = false;<br>
+ if (modules_ == 0 || !modules_fresh_) {<br>
+ modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate(<br>
+ kMaxNumberOfModuleContexts * sizeof(LoadedModule)));<br>
+ CHECK(modules_);<br>
+ n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts,<br>
+ /* filter */ 0);<br>
+ CHECK_GT(n_modules_, 0);<br>
+ CHECK_LT(n_modules_, kMaxNumberOfModuleContexts);<br>
+ modules_fresh_ = true;<br>
+ modules_were_reloaded = true;<br>
+ }<br>
+ for (uptr i = 0; i < n_modules_; i++) {<br>
+ if (modules_[i].containsAddress(address)) {<br>
+ return &modules_[i];<br>
+ }<br>
+ }<br>
+ // Reload the modules and look up again, if we haven't tried it yet.<br>
+ if (!modules_were_reloaded) {<br>
+ // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.<br>
+ // It's too aggressive to reload the list of modules each time we fail<br>
+ // to find a module for a given address.<br>
+ modules_fresh_ = false;<br>
+ return FindModuleForAddress(address);<br>
+ }<br>
+ return 0;<br>
+ }<br>
+<br>
+ bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,<br>
+ uptr *module_offset) {<br>
+ mu_.CheckLocked();<br>
+ LoadedModule *module = FindModuleForAddress(address);<br>
+ if (module == 0)<br>
+ return false;<br>
+ *module_name = module->full_name();<br>
+ *module_offset = address - module->base_address();<br>
+ return true;<br>
+ }<br>
+<br>
+ // 16K loaded modules should be enough for everyone.<br>
+ static const uptr kMaxNumberOfModuleContexts = 1 << 14;<br>
+ LoadedModule *modules_; // Array of module descriptions is leaked.<br>
+ uptr n_modules_;<br>
+ // If stale, need to reload the modules before looking up addresses.<br>
+ bool modules_fresh_;<br>
+ BlockingMutex mu_;<br>
+<br>
+ ExternalSymbolizerInterface *external_symbolizer_; // Leaked.<br>
+ InternalSymbolizer *const internal_symbolizer_; // Leaked.<br>
+ LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.<br>
+};<br>
+<br>
+Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {<br>
+ if (!common_flags()->symbolize) {<br>
+ return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0);<br>
+ }<br>
+ InternalSymbolizer* internal_symbolizer =<br>
+ InternalSymbolizer::get(&symbolizer_allocator_);<br>
+ ExternalSymbolizerInterface *external_symbolizer = 0;<br>
+ LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;<br>
+<br>
+ if (!internal_symbolizer) {<br>
+ libbacktrace_symbolizer =<br>
+ LibbacktraceSymbolizer::get(&symbolizer_allocator_);<br>
+ if (!libbacktrace_symbolizer) {<br>
+ if (path_to_external && path_to_external[0] == '\0') {<br>
+ // External symbolizer is explicitly disabled. Do nothing.<br>
+ } else {<br>
+ // Find path to llvm-symbolizer if it's not provided.<br>
+ if (!path_to_external)<br>
+ path_to_external = FindPathToBinary("llvm-symbolizer");<br>
+ if (path_to_external) {<br>
+ external_symbolizer = new(symbolizer_allocator_)<br>
+ LLVMSymbolizerProcess(path_to_external);<br>
+ } else if (common_flags()->allow_addr2line) {<br>
+ // If llvm-symbolizer is not found, try to use addr2line.<br>
+ if (const char *addr2line_path = FindPathToBinary("addr2line")) {<br>
+ external_symbolizer = new(symbolizer_allocator_)<br>
+ Addr2LinePool(addr2line_path, &symbolizer_allocator_);<br>
+ }<br>
+ }<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
+ return new(symbolizer_allocator_) POSIXSymbolizer(<br>
+ external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+<br>
+#endif // SANITIZER_POSIX<br>
+//===-- sanitizer_symbolizer_libbacktrace.cc ------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is shared between AddressSanitizer and ThreadSanitizer<br>
+// run-time libraries.<br>
+// Libbacktrace implementation of symbolizer parts.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "sanitizer_platform.h"<br>
+<br>
+#include "sanitizer_internal_defs.h"<br>
+#include "sanitizer_symbolizer.h"<br>
+#include "sanitizer_symbolizer_libbacktrace.h"<br>
+<br>
+#if SANITIZER_LIBBACKTRACE<br>
+# include "backtrace-supported.h"<br>
+# if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC<br>
+# include "backtrace.h"<br>
+# if SANITIZER_CP_DEMANGLE<br>
+# undef ARRAY_SIZE<br>
+# include "demangle.h"<br>
+# endif<br>
+# else<br>
+# define SANITIZER_LIBBACKTRACE 0<br>
+# endif<br>
+#endif<br>
+<br>
+namespace __sanitizer {<br>
+<br>
+#if SANITIZER_LIBBACKTRACE<br>
+<br>
+namespace {<br>
+<br>
+# if SANITIZER_CP_DEMANGLE<br>
+struct CplusV3DemangleData {<br>
+ char *buf;<br>
+ uptr size, allocated;<br>
+};<br>
+<br>
+extern "C" {<br>
+static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) {<br>
+ CplusV3DemangleData *data = (CplusV3DemangleData *)vdata;<br>
+ uptr needed = data->size + l + 1;<br>
+ if (needed > data->allocated) {<br>
+ data->allocated *= 2;<br>
+ if (needed > data->allocated)<br>
+ data->allocated = needed;<br>
+ char *buf = (char *)InternalAlloc(data->allocated);<br>
+ if (data->buf) {<br>
+ internal_memcpy(buf, data->buf, data->size);<br>
+ InternalFree(data->buf);<br>
+ }<br>
+ data->buf = buf;<br>
+ }<br>
+ internal_memcpy(data->buf + data->size, s, l);<br>
+ data->buf[data->size + l] = '\0';<br>
+ data->size += l;<br>
+}<br>
+} // extern "C"<br>
+<br>
+char *CplusV3Demangle(const char *name) {<br>
+ CplusV3DemangleData data;<br>
+ data.buf = 0;<br>
+ data.size = 0;<br>
+ data.allocated = 0;<br>
+ if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI,<br>
+ CplusV3DemangleCallback, &data)) {<br>
+ if (data.size + 64 > data.allocated)<br>
+ return data.buf;<br>
+ char *buf = internal_strdup(data.buf);<br>
+ InternalFree(data.buf);<br>
+ return buf;<br>
+ }<br>
+ if (data.buf)<br>
+ InternalFree(data.buf);<br>
+ return 0;<br>
+}<br>
+# endif // SANITIZER_CP_DEMANGLE<br>
+<br>
+struct SymbolizeCodeData {<br>
+ AddressInfo *frames;<br>
+ uptr n_frames;<br>
+ uptr max_frames;<br>
+ const char *module_name;<br>
+ uptr module_offset;<br>
+};<br>
+<br>
+extern "C" {<br>
+static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,<br>
+ const char *filename, int lineno,<br>
+ const char *function) {<br>
+ SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata;<br>
+ if (function) {<br>
+ AddressInfo *info = &cdata->frames[cdata->n_frames++];<br>
+ info->Clear();<br>
+ info->FillAddressAndModuleInfo(addr, cdata->module_name,<br>
+ cdata->module_offset);<br>
+ info->function = LibbacktraceSymbolizer::Demangle(function, true);<br>
+ if (filename)<br>
+ info->file = internal_strdup(filename);<br>
+ info->line = lineno;<br>
+ if (cdata->n_frames == cdata->max_frames)<br>
+ return 1;<br>
+ }<br>
+ return 0;<br>
+}<br>
+<br>
+static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,<br>
+ const char *symname, uintptr_t, uintptr_t) {<br>
+ SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata;<br>
+ if (symname) {<br>
+ AddressInfo *info = &cdata->frames[0];<br>
+ info->Clear();<br>
+ info->FillAddressAndModuleInfo(addr, cdata->module_name,<br>
+ cdata->module_offset);<br>
+ info->function = LibbacktraceSymbolizer::Demangle(symname, true);<br>
+ cdata->n_frames = 1;<br>
+ }<br>
+}<br>
+<br>
+static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname,<br>
+ uintptr_t symval, uintptr_t symsize) {<br>
+ DataInfo *info = (DataInfo *)vdata;<br>
+ if (symname && symval) {<br>
+ info->name = LibbacktraceSymbolizer::Demangle(symname, true);<br>
+ info->start = symval;<br>
+ info->size = symsize;<br>
+ }<br>
+}<br>
+<br>
+static void ErrorCallback(void *, const char *, int) {}<br>
+} // extern "C"<br>
+<br>
+} // namespace<br>
+<br>
+LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {<br>
+ // State created in backtrace_create_state is leaked.<br>
+ void *state = (void *)(backtrace_create_state("/proc/self/exe", 0,<br>
+ ErrorCallback, NULL));<br>
+ if (!state)<br>
+ return 0;<br>
+ return new(*alloc) LibbacktraceSymbolizer(state);<br>
+}<br>
+<br>
+uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,<br>
+ uptr max_frames,<br>
+ const char *module_name,<br>
+ uptr module_offset) {<br>
+ SymbolizeCodeData data;<br>
+ data.frames = frames;<br>
+ data.n_frames = 0;<br>
+ data.max_frames = max_frames;<br>
+ data.module_name = module_name;<br>
+ data.module_offset = module_offset;<br>
+ backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,<br>
+ ErrorCallback, &data);<br>
+ if (data.n_frames)<br>
+ return data.n_frames;<br>
+ backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,<br>
+ ErrorCallback, &data);<br>
+ return data.n_frames;<br>
+}<br>
+<br>
+bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) {<br>
+ backtrace_syminfo((backtrace_state *)state_, info->address,<br>
+ SymbolizeDataCallback, ErrorCallback, info);<br>
+ return true;<br>
+}<br>
+<br>
+#else // SANITIZER_LIBBACKTRACE<br>
+<br>
+LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {<br>
+ return 0;<br>
+}<br>
+<br>
+uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,<br>
+ uptr max_frames,<br>
+ const char *module_name,<br>
+ uptr module_offset) {<br>
+ (void)state_;<br>
+ return 0;<br>
+}<br>
+<br>
+bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) {<br>
+ return false;<br>
+}<br>
+<br>
+#endif // SANITIZER_LIBBACKTRACE<br>
+<br>
+char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) {<br>
+#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE<br>
+ if (char *demangled = CplusV3Demangle(name))<br>
+ return demangled;<br>
+#endif<br>
+ if (always_alloc)<br>
+ return internal_strdup(name);<br>
+ return 0;<br>
+}<br>
+<br>
+} // namespace __sanitizer<br>
+//===-- interception_linux.cc -----------------------------------*- C++ -*-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+//<br>
+// This file is a part of AddressSanitizer, an address sanity checker.<br>
+//<br>
+// Linux-specific interception methods.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#if defined(__linux__) || defined(__FreeBSD__)<br>
+#include "interception.h"<br>
+<br>
+#include <dlfcn.h> // for dlsym() and dlvsym()<br>
+<br>
+namespace __interception {<br>
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,<br>
+ uptr real, uptr wrapper) {<br>
+ *func_addr = (uptr)dlsym(RTLD_NEXT, func_name);<br>
+ return real == wrapper;<br>
+}<br>
+<br>
+#if !defined(__ANDROID__) // android does not have dlvsym<br>
+void *GetFuncAddrVer(const char *func_name, const char *ver) {<br>
+ return dlvsym(RTLD_NEXT, func_name, ver);<br>
+}<br>
+#endif // !defined(__ANDROID__)<br>
+<br>
+} // namespace __interception<br>
+<br>
+<br>
+#endif // __linux__ || __FreeBSD__<br>
<br>
Added: compiler-rt/trunk/lib/tsan/dd/dd_interceptors.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_interceptors.cc?rev=202505&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_interceptors.cc?rev=202505&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/dd_interceptors.cc (added)<br>
+++ compiler-rt/trunk/lib/tsan/dd/dd_interceptors.cc Fri Feb 28 08:52:20 2014<br>
@@ -0,0 +1,68 @@<br>
+//===-- dd_interceptors.cc ------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "dd_rtl.h"<br>
+#include "interception/interception.h"<br>
+#include <pthread.h><br>
+<br>
+using namespace __dsan;<br>
+<br>
+extern "C" void *__libc_malloc(uptr size);<br>
+extern "C" void __libc_free(void *ptr);<br>
+<br>
+static __thread Thread *thr;<br>
+<br>
+static void InitThread() {<br>
+ if (thr != 0)<br>
+ return;<br>
+ thr = (Thread*)InternalAlloc(sizeof(*thr));<br>
+ internal_memset(thr, 0, sizeof(*thr));<br>
+ ThreadInit(thr);<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ int res = REAL(pthread_mutex_destroy)(m);<br>
+ MutexDestroy(thr, (uptr)m);<br>
+ return res;<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ int res = REAL(pthread_mutex_lock)(m);<br>
+ if (res == 0)<br>
+ MutexLock(thr, (uptr)m, true, false);<br>
+ return res;<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ int res = REAL(pthread_mutex_trylock)(m);<br>
+ if (res == 0)<br>
+ MutexLock(thr, (uptr)m, true, true);<br>
+ return res;<br>
+}<br>
+<br>
+INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) {<br>
+ InitThread();<br>
+ MutexUnlock(thr, (uptr)m, true);<br>
+ int res = REAL(pthread_mutex_unlock)(m);<br>
+ return res;<br>
+}<br>
+<br>
+namespace __dsan {<br>
+<br>
+void InitializeInterceptors() {<br>
+ INTERCEPT_FUNCTION(pthread_mutex_destroy);<br>
+ INTERCEPT_FUNCTION(pthread_mutex_lock);<br>
+ INTERCEPT_FUNCTION(pthread_mutex_trylock);<br>
+ INTERCEPT_FUNCTION(pthread_mutex_unlock);<br>
+}<br>
+<br>
+} // namespace __dsan<br>
<br>
Added: compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc?rev=202505&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc?rev=202505&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc (added)<br>
+++ compiler-rt/trunk/lib/tsan/dd/dd_rtl.cc Fri Feb 28 08:52:20 2014<br>
@@ -0,0 +1,128 @@<br>
+//===-- dd_rtl.cc ---------------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "dd_rtl.h"<br>
+#include "sanitizer_common/sanitizer_common.h"<br>
+#include "sanitizer_common/sanitizer_flags.h"<br>
+#include "sanitizer_common/sanitizer_stacktrace.h"<br>
+#include "sanitizer_common/sanitizer_stackdepot.h"<br>
+<br>
+namespace __dsan {<br>
+<br>
+static Context ctx0;<br>
+static Context * const ctx = &ctx0;<br>
+<br>
+void Initialize() {<br>
+ InitializeInterceptors();<br>
+ //common_flags()->allow_addr2line = true;<br>
+ common_flags()->symbolize = true;<br>
+ ctx->dd = DDetector::Create();<br>
+}<br>
+<br>
+void ThreadInit(Thread *thr) {<br>
+ thr->dd_pt = ctx->dd->CreatePhysicalThread();<br>
+ thr->dd_lt = ctx->dd->CreateLogicalThread(0);<br>
+}<br>
+<br>
+void ThreadDestroy(Thread *thr) {<br>
+ ctx->dd->DestroyPhysicalThread(thr->dd_pt);<br>
+ ctx->dd->DestroyLogicalThread(thr->dd_lt);<br>
+}<br>
+<br>
+static u32 CurrentStackTrace(Thread *thr) {<br>
+ StackTrace trace;<br>
+ thr->in_symbolizer = true;<br>
+ trace.Unwind(1000, 0, 0, 0, 0, 0, false);<br>
+ thr->in_symbolizer = false;<br>
+ const uptr skip = 4;<br>
+ if (trace.size <= skip)<br>
+ return 0;<br>
+ return StackDepotPut(trace.trace + skip, trace.size - skip);<br>
+}<br>
+<br>
+static void PrintStackTrace(Thread *thr, u32 stk) {<br>
+ uptr size = 0;<br>
+ const uptr *trace = StackDepotGet(stk, &size);<br>
+ thr->in_symbolizer = true;<br>
+ StackTrace::PrintStack(trace, size);<br>
+ thr->in_symbolizer = false;<br>
+}<br>
+<br>
+static Mutex *FindMutex(Thread *thr, uptr m) {<br>
+ SpinMutexLock l(&ctx->mutex_mtx);<br>
+ for (Mutex *mtx = ctx->mutex_list; mtx; mtx = mtx->link) {<br>
+ if (mtx->addr == m)<br>
+ return mtx;<br>
+ }<br>
+ Mutex *mtx = (Mutex*)InternalAlloc(sizeof(*mtx));<br>
+ internal_memset(mtx, 0, sizeof(*mtx));<br>
+ mtx->addr = m;<br>
+ ctx->dd->MutexInit(&mtx->dd, CurrentStackTrace(thr), ctx->mutex_seq++);<br>
+ mtx->link = ctx->mutex_list;<br>
+ ctx->mutex_list = mtx;<br>
+ return mtx;<br>
+}<br>
+<br>
+static Mutex *FindMutexAndRemove(uptr m) {<br>
+ SpinMutexLock l(&ctx->mutex_mtx);<br>
+ Mutex **prev = &ctx->mutex_list;<br>
+ for (;;) {<br>
+ Mutex *mtx = *prev;<br>
+ if (mtx == 0)<br>
+ return 0;<br>
+ if (mtx->addr == m) {<br>
+ *prev = mtx->link;<br>
+ return mtx;<br>
+ }<br>
+ prev = &mtx->link;<br>
+ }<br>
+}<br>
+<br>
+static void ReportDeadlock(Thread *thr, DDReport *rep) {<br>
+ Printf("==============================\n");<br>
+ Printf("DEADLOCK\n");<br>
+ PrintStackTrace(thr, CurrentStackTrace(thr));<br>
+ for (int i = 0; i < rep->n; i++) {<br>
+ Printf("Mutex %llu created at:\n", rep->loop[i].mtx_ctx0);<br>
+ PrintStackTrace(thr, rep->loop[i].stk);<br>
+ }<br>
+ Printf("==============================\n");<br>
+}<br>
+<br>
+void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) {<br>
+ if (thr->in_symbolizer)<br>
+ return;<br>
+ Mutex *mtx = FindMutex(thr, m);<br>
+ DDReport *rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &mtx->dd,<br>
+ writelock, trylock);<br>
+ if (rep)<br>
+ ReportDeadlock(thr, rep);<br>
+}<br>
+<br>
+void MutexUnlock(Thread *thr, uptr m, bool writelock) {<br>
+ if (thr->in_symbolizer)<br>
+ return;<br>
+ Mutex *mtx = FindMutex(thr, m);<br>
+ ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &mtx->dd, writelock);<br>
+}<br>
+<br>
+void MutexDestroy(Thread *thr, uptr m) {<br>
+ if (thr->in_symbolizer)<br>
+ return;<br>
+ Mutex *mtx = FindMutexAndRemove(m);<br>
+ if (mtx == 0)<br>
+ return;<br>
+ ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &mtx->dd);<br>
+ InternalFree(mtx);<br>
+}<br>
+<br>
+} // namespace __dsan<br>
+<br>
+__attribute__((section(".preinit_array"), used))<br>
+void (*__local_dsan_preinit)(void) = __dsan::Initialize;<br>
<br>
Added: compiler-rt/trunk/lib/tsan/dd/dd_rtl.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.h?rev=202505&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/dd/dd_rtl.h?rev=202505&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/tsan/dd/dd_rtl.h (added)<br>
+++ compiler-rt/trunk/lib/tsan/dd/dd_rtl.h Fri Feb 28 08:52:20 2014<br>
@@ -0,0 +1,50 @@<br>
+//===-- dd_rtl.h ----------------------------------------------------------===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+#ifndef DD_RTL_H<br>
+#define DD_RTL_H<br>
+<br>
+#include "sanitizer_common/sanitizer_internal_defs.h"<br>
+#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"<br>
+#include "sanitizer_common/sanitizer_allocator_internal.h"<br>
+#include "sanitizer_common/sanitizer_mutex.h"<br>
+<br>
+namespace __dsan {<br>
+<br>
+struct Mutex {<br>
+ Mutex *link;<br>
+ uptr addr;<br>
+ DDMutex dd;<br>
+};<br>
+<br>
+struct Thread {<br>
+ DDPhysicalThread *dd_pt;<br>
+ DDLogicalThread *dd_lt;<br>
+<br>
+ bool in_symbolizer;<br>
+};<br>
+<br>
+struct Context {<br>
+ DDetector *dd;<br>
+<br>
+ SpinMutex mutex_mtx;<br>
+ Mutex *mutex_list;<br>
+ u64 mutex_seq;<br>
+};<br>
+<br>
+void InitializeInterceptors();<br>
+<br>
+void ThreadInit(Thread *thr);<br>
+void ThreadDestroy(Thread *thr);<br>
+<br>
+void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock);<br>
+void MutexUnlock(Thread *thr, uptr m, bool writelock);<br>
+void MutexDestroy(Thread *thr, uptr m);<br>
+<br>
+} // namespace __dsan<br>
+#endif // DD_RTL_H<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu" target="_blank">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><span class="HOEnZb"><font color="#888888"><br>
</font></span></blockquote></div><span class="HOEnZb"><font color="#888888"><br><br clear="all"><div><br></div>-- <br><div>Alexey Samsonov, MSK</div>
</font></span></div>
<br>_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
<br></blockquote></div><br></div></div>