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