diff --git a/CMakeLists.txt b/CMakeLists.txt index cbde83113e..32358a1262 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,16 +14,6 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR COMPILER_RT_STANDALONE endif() cmake_minimum_required(VERSION 3.4.3) -# FIXME: -# The OLD behavior (pre 3.2) for this policy is to not set the value of the -# CMAKE_EXE_LINKER_FLAGS variable in the generated test project. The NEW behavior -# for this policy is to set the value of the CMAKE_EXE_LINKER_FLAGS variable -# in the test project to the same as it is in the calling project. The new -# behavior cause the compiler_rt test to fail during try_compile: see -# projects/compiler-rt/cmake/Modules/CompilerRTUtils.cmake:121 such that -# CAN_TARGET_${arch} is not set properly. This results in COMPILER_RT_SUPPORTED_ARCH -# not being updated properly leading to poblems. -cmake_policy(SET CMP0056 OLD) # Add path for custom compiler-rt modules. list(INSERT CMAKE_MODULE_PATH 0 @@ -134,7 +124,7 @@ if(NOT WIN32) endif() append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COMMON_CFLAGS) -if(NOT COMPILER_RT_DEBUG) +if(NOT COMPILER_RT_DEBUG AND NOT APPLE) append_list_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS) endif() append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS) @@ -253,12 +243,12 @@ else() endif() set(COMPILER_RT_LLD_PATH ${LLVM_MAIN_SRC_DIR}/tools/lld) -if(EXISTS ${COMPILER_RT_LLD_PATH}/) - set(COMPILER_RT_HAS_LLD_SOURCES TRUE) +if(EXISTS ${COMPILER_RT_LLD_PATH}/ AND LLVM_TOOL_LLD_BUILD) + set(COMPILER_RT_HAS_LLD TRUE) else() - set(COMPILER_RT_HAS_LLD_SOURCES FALSE) + set(COMPILER_RT_HAS_LLD FALSE) endif() -pythonize_bool(COMPILER_RT_HAS_LLD_SOURCES) +pythonize_bool(COMPILER_RT_HAS_LLD) add_subdirectory(lib) diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index d4533e6319..b64eb42463 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -110,6 +110,13 @@ function(add_compiler_rt_runtime name type) "OS;ARCHS;SOURCES;CFLAGS;LINK_FLAGS;DEFS;LINK_LIBS;OBJECT_LIBS" ${ARGN}) set(libnames) + # Until we support this some other way, build compiler-rt runtime without LTO + # to allow non-LTO projects to link with it. + if(COMPILER_RT_HAS_FNO_LTO_FLAG) + set(NO_LTO_FLAGS "-fno-lto") + else() + set(NO_LTO_FLAGS "") + endif() if(APPLE) foreach(os ${LIB_OS}) if(type STREQUAL "STATIC") @@ -121,7 +128,7 @@ function(add_compiler_rt_runtime name type) list_intersect(LIB_ARCHS_${libname} DARWIN_${os}_ARCHS LIB_ARCHS) if(LIB_ARCHS_${libname}) list(APPEND libnames ${libname}) - set(extra_cflags_${libname} ${DARWIN_${os}_CFLAGS} ${LIB_CFLAGS}) + set(extra_cflags_${libname} ${DARWIN_${os}_CFLAGS} ${NO_LTO_FLAGS} ${LIB_CFLAGS}) set(output_name_${libname} ${libname}${COMPILER_RT_OS_SUFFIX}) set(sources_${libname} ${LIB_SOURCES}) format_object_libs(sources_${libname} ${os} ${LIB_OBJECT_LIBS}) @@ -149,7 +156,7 @@ function(add_compiler_rt_runtime name type) set(sources_${libname} ${LIB_SOURCES}) format_object_libs(sources_${libname} ${arch} ${LIB_OBJECT_LIBS}) set(libnames ${libnames} ${libname}) - set(extra_cflags_${libname} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set(extra_cflags_${libname} ${TARGET_${arch}_CFLAGS} ${NO_LTO_FLAGS} ${LIB_CFLAGS}) endforeach() endif() diff --git a/cmake/Modules/CompilerRTDarwinUtils.cmake b/cmake/Modules/CompilerRTDarwinUtils.cmake index 3c89381f92..baea4a067f 100644 --- a/cmake/Modules/CompilerRTDarwinUtils.cmake +++ b/cmake/Modules/CompilerRTDarwinUtils.cmake @@ -85,10 +85,12 @@ function(darwin_test_archs os valid_archs) if(TEST_COMPILE_ONLY) try_compile_only(CAN_TARGET_${os}_${arch} -v -arch ${arch} ${DARWIN_${os}_CFLAGS}) else() + set(SAVED_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${arch_linker_flags}") try_compile(CAN_TARGET_${os}_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_C} COMPILE_DEFINITIONS "-v -arch ${arch}" ${DARWIN_${os}_CFLAGS} - CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS=${arch_linker_flags}" OUTPUT_VARIABLE TEST_OUTPUT) + set(CMAKE_EXE_LINKER_FLAGS ${SAVED_CMAKE_EXE_LINKER_FLAGS}) endif() if(${CAN_TARGET_${os}_${arch}}) list(APPEND working_archs ${arch}) diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake index ed946d88f2..3b3a0c1532 100644 --- a/cmake/Modules/CompilerRTUtils.cmake +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -138,15 +138,16 @@ macro(test_target_arch arch def) elseif(TEST_COMPILE_ONLY) try_compile_only(CAN_TARGET_${arch} ${TARGET_${arch}_CFLAGS}) else() - set(argstring "${CMAKE_EXE_LINKER_FLAGS} ${argstring}") set(FLAG_NO_EXCEPTIONS "") if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG) set(FLAG_NO_EXCEPTIONS " -fno-exceptions ") endif() + set(SAVED_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${argstring}") try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE} COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS} ${FLAG_NO_EXCEPTIONS}" - OUTPUT_VARIABLE TARGET_${arch}_OUTPUT - CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}") + OUTPUT_VARIABLE TARGET_${arch}_OUTPUT) + set(CMAKE_EXE_LINKER_FLAGS ${SAVED_CMAKE_EXE_LINKER_FLAGS}) endif() endif() if(${CAN_TARGET_${arch}}) diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 26eb532553..e61a4f146d 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -164,15 +164,7 @@ set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64} set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X}) set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64}) - -# Darwin does not support 32-bit thread-local storage on ios versions -# below 9.0. Until the min ios version is bumped to 9.0, lsan will -# not build for 32-bit darwin targets. -if(APPLE) - set(ALL_LSAN_SUPPORTED_ARCH ${X86_64} ${ARM64}) -else() - set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64}) -endif() +set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64}) set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64}) set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64} ${MIPS32} ${MIPS64} ${S390X}) @@ -208,7 +200,7 @@ if(APPLE) list(APPEND DARWIN_EMBEDDED_PLATFORMS ios) set(DARWIN_ios_MIN_VER_FLAG -miphoneos-version-min) set(DARWIN_ios_SANITIZER_MIN_VER_FLAG - ${DARWIN_ios_MIN_VER_FLAG}=7.0) + ${DARWIN_ios_MIN_VER_FLAG}=8.0) endif() if(COMPILER_RT_ENABLE_WATCHOS) list(APPEND DARWIN_EMBEDDED_PLATFORMS watchos) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 1f8b481e77..3d6c96be93 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -8,10 +8,12 @@ set(SANITIZER_HEADERS sanitizer/linux_syscall_hooks.h sanitizer/lsan_interface.h sanitizer/msan_interface.h + sanitizer/tsan_interface.h sanitizer/tsan_interface_atomic.h) set(XRAY_HEADERS - xray/xray_interface.h) + xray/xray_interface.h + xray/xray_log_interface.h) set(COMPILER_RT_HEADERS ${SANITIZER_HEADERS} diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h index f9f9302235..4a1de968b0 100644 --- a/include/sanitizer/common_interface_defs.h +++ b/include/sanitizer/common_interface_defs.h @@ -158,8 +158,10 @@ extern "C" { // Prints stack traces for all live heap allocations ordered by total // allocation size until `top_percent` of total live heap is shown. // `top_percent` should be between 1 and 100. + // At most `max_number_of_contexts` contexts (stack traces) is printed. // Experimental feature currently available only with asan on Linux/x86_64. - void __sanitizer_print_memory_profile(size_t top_percent); + void __sanitizer_print_memory_profile(size_t top_percent, + size_t max_number_of_contexts); // Fiber annotation interface. // Before switching to a different stack, one must call diff --git a/include/sanitizer/tsan_interface.h b/include/sanitizer/tsan_interface.h new file mode 100644 index 0000000000..34b74d537e --- /dev/null +++ b/include/sanitizer/tsan_interface.h @@ -0,0 +1,121 @@ +//===-- tsan_interface.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Public interface header for TSan. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_TSAN_INTERFACE_H +#define SANITIZER_TSAN_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// __tsan_release establishes a happens-before relation with a preceding +// __tsan_acquire on the same address. +void __tsan_acquire(void *addr); +void __tsan_release(void *addr); + +// Annotations for custom mutexes. +// The annotations allow to get better reports (with sets of locked mutexes), +// detect more types of bugs (e.g. mutex misuses, races between lock/unlock and +// destruction and potential deadlocks) and improve precision and performance +// (by ignoring individual atomic operations in mutex code). However, the +// downside is that annotated mutex code itself is not checked for correctness. + +// Mutex creation flags are passed to __tsan_mutex_create annotation. +// If mutex has no constructor and __tsan_mutex_create is not called, +// the flags may be passed to __tsan_mutex_pre_lock/__tsan_mutex_post_lock +// annotations. + +// Mutex has static storage duration and no-op constructor and destructor. +// This effectively makes tsan ignore destroy annotation. +const unsigned __tsan_mutex_linker_init = 1 << 0; +// Mutex is write reentrant. +const unsigned __tsan_mutex_write_reentrant = 1 << 1; +// Mutex is read reentrant. +const unsigned __tsan_mutex_read_reentrant = 1 << 2; + +// Mutex operation flags: + +// Denotes read lock operation. +const unsigned __tsan_mutex_read_lock = 1 << 3; +// Denotes try lock operation. +const unsigned __tsan_mutex_try_lock = 1 << 4; +// Denotes that a try lock operation has failed to acquire the mutex. +const unsigned __tsan_mutex_try_lock_failed = 1 << 5; +// Denotes that the lock operation acquires multiple recursion levels. +// Number of levels is passed in recursion parameter. +// This is useful for annotation of e.g. Java builtin monitors, +// for which wait operation releases all recursive acquisitions of the mutex. +const unsigned __tsan_mutex_recursive_lock = 1 << 6; +// Denotes that the unlock operation releases all recursion levels. +// Number of released levels is returned and later must be passed to +// the corresponding __tsan_mutex_post_lock annotation. +const unsigned __tsan_mutex_recursive_unlock = 1 << 7; + +// Annotate creation of a mutex. +// Supported flags: mutex creation flags. +void __tsan_mutex_create(void *addr, unsigned flags); + +// Annotate destruction of a mutex. +// Supported flags: none. +void __tsan_mutex_destroy(void *addr, unsigned flags); + +// Annotate start of lock operation. +// Supported flags: +// - __tsan_mutex_read_lock +// - __tsan_mutex_try_lock +// - all mutex creation flags +void __tsan_mutex_pre_lock(void *addr, unsigned flags); + +// Annotate end of lock operation. +// Supported flags: +// - __tsan_mutex_read_lock (must match __tsan_mutex_pre_lock) +// - __tsan_mutex_try_lock (must match __tsan_mutex_pre_lock) +// - __tsan_mutex_try_lock_failed +// - __tsan_mutex_recursive_lock +// - all mutex creation flags +void __tsan_mutex_post_lock(void *addr, unsigned flags, int recursion); + +// Annotate start of unlock operation. +// Supported flags: +// - __tsan_mutex_read_lock +// - __tsan_mutex_recursive_unlock +int __tsan_mutex_pre_unlock(void *addr, unsigned flags); + +// Annotate end of unlock operation. +// Supported flags: +// - __tsan_mutex_read_lock (must match __tsan_mutex_pre_unlock) +void __tsan_mutex_post_unlock(void *addr, unsigned flags); + +// Annotate start/end of notify/signal/broadcast operation. +// Supported flags: none. +void __tsan_mutex_pre_signal(void *addr, unsigned flags); +void __tsan_mutex_post_signal(void *addr, unsigned flags); + +// Annotate start/end of a region of code where lock/unlock/signal operation +// diverts to do something else unrelated to the mutex. This can be used to +// annotate, for example, calls into cooperative scheduler or contention +// profiling code. +// These annotations must be called only from within +// __tsan_mutex_pre/post_lock, __tsan_mutex_pre/post_unlock, +// __tsan_mutex_pre/post_signal regions. +// Supported flags: none. +void __tsan_mutex_pre_divert(void *addr, unsigned flags); +void __tsan_mutex_post_divert(void *addr, unsigned flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_TSAN_INTERFACE_H diff --git a/include/xray/xray_log_interface.h b/include/xray/xray_log_interface.h index f98b331101..a8709c3a7c 100644 --- a/include/xray/xray_log_interface.h +++ b/include/xray/xray_log_interface.h @@ -48,4 +48,13 @@ XRayLogFlushStatus __xray_log_flushLog(); } // extern "C" +namespace __xray { +// Options used by the LLVM XRay FDR implementation. +struct FDRLoggingOptions { + bool ReportErrors = false; + int Fd = -1; +}; + +} // namespace __xray + #endif // XRAY_XRAY_LOG_INTERFACE_H diff --git a/include/xray/xray_records.h b/include/xray/xray_records.h index 71637d1fe9..feb8d228b2 100644 --- a/include/xray/xray_records.h +++ b/include/xray/xray_records.h @@ -24,6 +24,14 @@ enum FileTypes { FDR_LOG = 1, }; +// FDR mode use of the union field in the XRayFileHeader. +struct alignas(16) FdrAdditionalHeaderData { + uint64_t ThreadBufferSize; +}; + +static_assert(sizeof(FdrAdditionalHeaderData) == 16, + "FdrAdditionalHeaderData != 16 bytes"); + // This data structure is used to describe the contents of the file. We use this // for versioning the supported XRay file formats. struct alignas(32) XRayFileHeader { @@ -42,10 +50,15 @@ struct alignas(32) XRayFileHeader { // The frequency by which TSC increases per-second. alignas(8) uint64_t CycleFrequency = 0; - // The current civiltime timestamp, as retrived from 'clock_gettime'. This - // allows readers of the file to determine when the file was created or - // written down. - struct timespec TS; + union { + char FreeForm[16]; + // The current civiltime timestamp, as retrived from 'clock_gettime'. This + // allows readers of the file to determine when the file was created or + // written down. + struct timespec TS; + + struct FdrAdditionalHeaderData FdrData; + }; } __attribute__((packed)); static_assert(sizeof(XRayFileHeader) == 32, "XRayFileHeader != 32 bytes"); diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc index 4be1f1ce2f..7010b60236 100644 --- a/lib/asan/asan_allocator.cc +++ b/lib/asan/asan_allocator.cc @@ -523,6 +523,18 @@ struct Allocator { AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; m->free_context_id = StackDepotPut(*stack); + + Flags &fl = *flags(); + if (fl.max_free_fill_size > 0) { + // We have to skip the chunk header, it contains free_context_id. + uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size; + if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area. + uptr size_to_fill = m->UsedSize() - kChunkHeader2Size; + size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size); + REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill); + } + } + // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), @@ -800,8 +812,12 @@ void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { if (!p) return instance.Allocate(size, 8, stack, FROM_MALLOC, true); if (size == 0) { - instance.Deallocate(p, 0, stack, FROM_MALLOC); - return nullptr; + if (flags()->allocator_frees_and_returns_null_on_realloc_zero) { + instance.Deallocate(p, 0, stack, FROM_MALLOC); + return nullptr; + } + // Allocate a size of 1 if we shouldn't free() on Realloc to 0 + size = 1; } return instance.Reallocate(p, size, stack); } diff --git a/lib/asan/asan_descriptions.cc b/lib/asan/asan_descriptions.cc index 0ecbe091c8..822a6a62d6 100644 --- a/lib/asan/asan_descriptions.cc +++ b/lib/asan/asan_descriptions.cc @@ -252,6 +252,9 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, str.append("%c", var.name_pos[i]); } str.append("'"); + if (var.line > 0) { + str.append(" (line %d)", var.line); + } if (pos_descr) { Decorator d; // FIXME: we may want to also print the size of the access here, diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc index 74c441a04c..555cc04db4 100644 --- a/lib/asan/asan_flags.cc +++ b/lib/asan/asan_flags.cc @@ -95,6 +95,18 @@ void InitializeFlags() { RegisterCommonFlags(&ubsan_parser); #endif + if (SANITIZER_MAC) { + // Support macOS MallocScribble and MallocPreScribble: + // + if (GetEnv("MallocScribble")) { + f->max_free_fill_size = 0x1000; + } + if (GetEnv("MallocPreScribble")) { + f->malloc_fill_byte = 0xaa; + } + } + // Override from ASan compile definition. const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); asan_parser.ParseString(asan_compile_def); diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc index 4712efb862..f2216c2e9b 100644 --- a/lib/asan/asan_flags.inc +++ b/lib/asan/asan_flags.inc @@ -63,8 +63,14 @@ ASAN_FLAG( int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K. "ASan allocator flag. max_malloc_fill_size is the maximal amount of " "bytes that will be filled with malloc_fill_byte on malloc.") +ASAN_FLAG( + int, max_free_fill_size, 0, + "ASan allocator flag. max_free_fill_size is the maximal amount of " + "bytes that will be filled with free_fill_byte during free.") ASAN_FLAG(int, malloc_fill_byte, 0xbe, "Value used to fill the newly allocated memory.") +ASAN_FLAG(int, free_fill_byte, 0x55, + "Value used to fill deallocated memory.") ASAN_FLAG(bool, allow_user_poisoning, true, "If set, user may manually mark memory regions as poisoned or " "unpoisoned.") @@ -148,3 +154,10 @@ ASAN_FLAG(bool, halt_on_error, true, "(WARNING: USE AT YOUR OWN RISK!)") ASAN_FLAG(bool, use_odr_indicator, false, "Use special ODR indicator symbol for ODR violation detection") +ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true, + "realloc(p, 0) is equivalent to free(p) by default (Same as the " + "POSIX standard). If set to false, realloc(p, 0) will return a " + "pointer to an allocated space which can not be used.") +ASAN_FLAG(bool, verify_asan_link_order, true, + "Check position of ASan runtime in library list (needs to be disabled" + " when other library has to be preloaded system-wide)") diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 6d150de790..50ef84c39a 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -111,7 +111,7 @@ static void ReportIncompatibleRT() { } void AsanCheckDynamicRTPrereqs() { - if (!ASAN_DYNAMIC) + if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order) return; // Ensure that dynamic RT is the first DSO in the list diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index 5163c04f7e..efa0582439 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -100,7 +100,7 @@ void *realloc(void *ptr, size_t size) { ALLOCATION_FUNCTION_ATTRIBUTE void *_realloc_dbg(void *ptr, size_t size, int) { - CHECK(!"_realloc_dbg should not exist!"); + UNREACHABLE("_realloc_dbg should not exist!"); return 0; } diff --git a/lib/asan/asan_memory_profile.cc b/lib/asan/asan_memory_profile.cc index c2678b974f..05846c37cb 100644 --- a/lib/asan/asan_memory_profile.cc +++ b/lib/asan/asan_memory_profile.cc @@ -48,7 +48,7 @@ class HeapProfile { } } - void Print(uptr top_percent) { + void Print(uptr top_percent, uptr max_number_of_contexts) { InternalSort(&allocations_, allocations_.size(), [](const AllocationSite &a, const AllocationSite &b) { return a.total_size > b.total_size; @@ -57,12 +57,14 @@ class HeapProfile { uptr total_shown = 0; Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: " "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; " - "showing top %zd%%\n", + "showing top %zd%% (at most %zd unique contexts)\n", total_allocated_user_size_, total_allocated_count_, total_quarantined_user_size_, total_quarantined_count_, total_other_count_, total_allocated_count_ + - total_quarantined_count_ + total_other_count_, top_percent); - for (uptr i = 0; i < allocations_.size(); i++) { + total_quarantined_count_ + total_other_count_, top_percent, + max_number_of_contexts); + for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts); + i++) { auto &a = allocations_[i]; Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, a.total_size * 100 / total_allocated_user_size_, a.count); @@ -103,16 +105,23 @@ static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, void *argument) { HeapProfile hp; __lsan::ForEachChunk(ChunkCallback, &hp); - hp.Print(reinterpret_cast(argument)); + uptr *Arg = reinterpret_cast(argument); + hp.Print(Arg[0], Arg[1]); } } // namespace __asan +#endif // CAN_SANITIZE_LEAKS + extern "C" { SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_print_memory_profile(uptr top_percent) { - __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent); +void __sanitizer_print_memory_profile(uptr top_percent, + uptr max_number_of_contexts) { +#if CAN_SANITIZE_LEAKS + uptr Arg[2]; + Arg[0] = top_percent; + Arg[1] = max_number_of_contexts; + __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg); +#endif // CAN_SANITIZE_LEAKS } } // extern "C" - -#endif // CAN_SANITIZE_LEAKS diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index cd44ba873f..f751b6184c 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -88,7 +88,8 @@ bool ParseFrameDescription(const char *frame_descr, char *p; // This string is created by the compiler and has the following form: // "n alloc_1 alloc_2 ... alloc_n" - // where alloc_i looks like "offset size len ObjectName". + // where alloc_i looks like "offset size len ObjectName" + // or "offset size len ObjectName:line". uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); if (n_objects == 0) return false; @@ -101,7 +102,14 @@ bool ParseFrameDescription(const char *frame_descr, return false; } p++; - StackVarDescr var = {beg, size, p, len}; + char *colon_pos = internal_strchr(p, ':'); + uptr line = 0; + uptr name_len = len; + if (colon_pos != nullptr && colon_pos < p + len) { + name_len = colon_pos - p; + line = (uptr)internal_simple_strtoll(colon_pos + 1, nullptr, 10); + } + StackVarDescr var = {beg, size, p, name_len, line}; vars->push_back(var); p += len; } diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h index 5ebfda693d..5a3533a319 100644 --- a/lib/asan/asan_report.h +++ b/lib/asan/asan_report.h @@ -23,6 +23,7 @@ struct StackVarDescr { uptr size; const char *name_pos; uptr name_len; + uptr line; }; // Returns the number of globals close to the provided address and copies diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc index 651886c915..189b4b141b 100644 --- a/lib/asan/asan_win_dll_thunk.cc +++ b/lib/asan/asan_win_dll_thunk.cc @@ -82,6 +82,7 @@ INTERCEPT_LIBRARY_FUNCTION(strpbrk); INTERCEPT_LIBRARY_FUNCTION(strrchr); INTERCEPT_LIBRARY_FUNCTION(strspn); INTERCEPT_LIBRARY_FUNCTION(strstr); +INTERCEPT_LIBRARY_FUNCTION(strtok); INTERCEPT_LIBRARY_FUNCTION(strtol); INTERCEPT_LIBRARY_FUNCTION(wcslen); diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 424a79e00a..31215cf566 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -337,8 +337,9 @@ void *ManyThreadsWorker(void *a) { return 0; } -#if !defined(__aarch64__) +#if !defined(__aarch64__) && !defined(__powerpc64__) // FIXME: Infinite loop in AArch64 (PR24389). +// FIXME: Also occasional hang on powerpc. Maybe same problem as on AArch64? TEST(AddressSanitizer, ManyThreadsTest) { const size_t kNumThreads = (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000; @@ -945,7 +946,7 @@ TEST(AddressSanitizer, ShadowGapTest) { char *addr = (char*)0x0000100000080000; # endif #endif - EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown"); + EXPECT_DEATH(*addr = 1, "AddressSanitizer: (SEGV|BUS) on unknown"); } #endif // ASAN_NEEDS_SEGV diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt index ad9059c336..3f648dcb35 100644 --- a/lib/builtins/CMakeLists.txt +++ b/lib/builtins/CMakeLists.txt @@ -499,7 +499,7 @@ else () # Needed for clear_cache on debug mode, due to r7's usage in inline asm. # Release mode already sets it via -O2/3, Debug mode doesn't. if (${arch} STREQUAL "armhf") - list(APPEND BUILTIN_CFLAGS -fomit-frame-pointer) + list(APPEND BUILTIN_CFLAGS -fomit-frame-pointer -DCOMPILER_RT_ARMHF_TARGET) endif() add_compiler_rt_runtime(clang_rt.builtins diff --git a/lib/builtins/int_lib.h b/lib/builtins/int_lib.h index 39eee18d91..8a202dde70 100644 --- a/lib/builtins/int_lib.h +++ b/lib/builtins/int_lib.h @@ -32,7 +32,11 @@ #if __ARM_EABI__ # define ARM_EABI_FNALIAS(aeabi_name, name) \ void __aeabi_##aeabi_name() __attribute__((alias("__" #name))); -# define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) +# ifdef COMPILER_RT_ARMHF_TARGET +# define COMPILER_RT_ABI +# else +# define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) +# endif #else # define ARM_EABI_FNALIAS(aeabi_name, name) # define COMPILER_RT_ABI diff --git a/lib/builtins/os_version_check.c b/lib/builtins/os_version_check.c index 16d7854c9a..74ade2f5b9 100644 --- a/lib/builtins/os_version_check.c +++ b/lib/builtins/os_version_check.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,58 @@ static dispatch_once_t DispatchOnceCounter; /* Find and parse the SystemVersion.plist file. */ static void parseSystemVersionPList(void *Unused) { (void)Unused; + /* Load CoreFoundation dynamically */ + const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); + if (!NullAllocator) + return; + const CFAllocatorRef kCFAllocatorNull = + *(const CFAllocatorRef *)NullAllocator; + typeof(CFDataCreateWithBytesNoCopy) *CFDataCreateWithBytesNoCopyFunc = + (typeof(CFDataCreateWithBytesNoCopy) *)dlsym( + RTLD_DEFAULT, "CFDataCreateWithBytesNoCopy"); + if (!CFDataCreateWithBytesNoCopyFunc) + return; + typeof(CFPropertyListCreateWithData) *CFPropertyListCreateWithDataFunc = + (typeof(CFPropertyListCreateWithData) *)dlsym( + RTLD_DEFAULT, "CFPropertyListCreateWithData"); + /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it + * will be NULL on earlier OS versions. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + typeof(CFPropertyListCreateFromXMLData) *CFPropertyListCreateFromXMLDataFunc = + (typeof(CFPropertyListCreateFromXMLData) *)dlsym( + RTLD_DEFAULT, "CFPropertyListCreateFromXMLData"); +#pragma clang diagnostic pop + /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it + * might be NULL in future OS versions. */ + if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc) + return; + typeof(CFStringCreateWithCStringNoCopy) *CFStringCreateWithCStringNoCopyFunc = + (typeof(CFStringCreateWithCStringNoCopy) *)dlsym( + RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy"); + if (!CFStringCreateWithCStringNoCopyFunc) + return; + typeof(CFDictionaryGetValue) *CFDictionaryGetValueFunc = + (typeof(CFDictionaryGetValue) *)dlsym(RTLD_DEFAULT, + "CFDictionaryGetValue"); + if (!CFDictionaryGetValueFunc) + return; + typeof(CFGetTypeID) *CFGetTypeIDFunc = + (typeof(CFGetTypeID) *)dlsym(RTLD_DEFAULT, "CFGetTypeID"); + if (!CFGetTypeIDFunc) + return; + typeof(CFStringGetTypeID) *CFStringGetTypeIDFunc = + (typeof(CFStringGetTypeID) *)dlsym(RTLD_DEFAULT, "CFStringGetTypeID"); + if (!CFStringGetTypeIDFunc) + return; + typeof(CFStringGetCString) *CFStringGetCStringFunc = + (typeof(CFStringGetCString) *)dlsym(RTLD_DEFAULT, "CFStringGetCString"); + if (!CFStringGetCStringFunc) + return; + typeof(CFRelease) *CFReleaseFunc = + (typeof(CFRelease) *)dlsym(RTLD_DEFAULT, "CFRelease"); + if (!CFReleaseFunc) + return; char *PListPath = "/System/Library/CoreServices/SystemVersion.plist"; @@ -67,36 +120,41 @@ static void parseSystemVersionPList(void *Unused) { /* Get the file buffer into CF's format. We pass in a null allocator here * * because we free PListBuf ourselves */ - FileContentsRef = CFDataCreateWithBytesNoCopy( + FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)( NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull); if (!FileContentsRef) goto Fail; - if (&CFPropertyListCreateWithData) - PListRef = CFPropertyListCreateWithData( + if (CFPropertyListCreateWithDataFunc) + PListRef = (*CFPropertyListCreateWithDataFunc)( NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL); else - PListRef = CFPropertyListCreateFromXMLData(NULL, FileContentsRef, - kCFPropertyListImmutable, NULL); + PListRef = (*CFPropertyListCreateFromXMLDataFunc)( + NULL, FileContentsRef, kCFPropertyListImmutable, NULL); if (!PListRef) goto Fail; - CFTypeRef OpaqueValue = - CFDictionaryGetValue(PListRef, CFSTR("ProductVersion")); - if (!OpaqueValue || CFGetTypeID(OpaqueValue) != CFStringGetTypeID()) + CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)( + NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull); + if (!ProductVersion) + goto Fail; + CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion); + (*CFReleaseFunc)(ProductVersion); + if (!OpaqueValue || + (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)()) goto Fail; char VersionStr[32]; - if (!CFStringGetCString((CFStringRef)OpaqueValue, VersionStr, - sizeof(VersionStr), kCFStringEncodingUTF8)) + if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr, + sizeof(VersionStr), kCFStringEncodingUTF8)) goto Fail; sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor); Fail: if (PListRef) - CFRelease(PListRef); + (*CFReleaseFunc)(PListRef); if (FileContentsRef) - CFRelease(FileContentsRef); + (*CFReleaseFunc)(FileContentsRef); free(PListBuf); fclose(PropertyList); } diff --git a/lib/esan/esan_interceptors.cpp b/lib/esan/esan_interceptors.cpp index 9ae5482a3c..9740f4dae8 100644 --- a/lib/esan/esan_interceptors.cpp +++ b/lib/esan/esan_interceptors.cpp @@ -304,20 +304,6 @@ INTERCEPTOR(int, unlink, char *path) { return REAL(unlink)(path); } -INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, fread, ptr, size, nmemb, f); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size * nmemb); - return REAL(fread)(ptr, size, nmemb, f); -} - -INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, fwrite, p, size, nmemb, f); - COMMON_INTERCEPTOR_READ_RANGE(ctx, p, size * nmemb); - return REAL(fwrite)(p, size, nmemb, f); -} - INTERCEPTOR(int, puts, const char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, puts, s); diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt index a48b85fa60..4e66100c3e 100644 --- a/lib/lsan/CMakeLists.txt +++ b/lib/lsan/CMakeLists.txt @@ -11,6 +11,7 @@ set(LSAN_COMMON_SOURCES set(LSAN_SOURCES lsan.cc lsan_allocator.cc + lsan_linux.cc lsan_interceptors.cc lsan_preinit.cc lsan_thread.cc) diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc index 4284857962..640497d401 100644 --- a/lib/lsan/lsan_allocator.cc +++ b/lib/lsan/lsan_allocator.cc @@ -24,53 +24,18 @@ extern "C" void *memset(void *ptr, int value, uptr num); namespace __lsan { - -struct ChunkMetadata { - u8 allocated : 8; // Must be first. - ChunkTag tag : 2; -#if SANITIZER_WORDSIZE == 64 - uptr requested_size : 54; -#else - uptr requested_size : 32; - uptr padding : 22; -#endif - u32 stack_trace_id; -}; - -#if defined(__mips64) || defined(__aarch64__) || defined(__i386__) #if defined(__i386__) static const uptr kMaxAllowedMallocSize = 1UL << 30; -#else +#elif defined(__mips64) || defined(__aarch64__) static const uptr kMaxAllowedMallocSize = 4UL << 30; -#endif -static const uptr kRegionSizeLog = 20; -static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; -typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; -typedef CompactSizeClassMap SizeClassMap; -typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, - sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap> - PrimaryAllocator; #else static const uptr kMaxAllowedMallocSize = 8UL << 30; - -struct AP64 { // Allocator64 parameters. Deliberately using a short name. - static const uptr kSpaceBeg = 0x600000000000ULL; - static const uptr kSpaceSize = 0x40000000000ULL; // 4T. - static const uptr kMetadataSize = sizeof(ChunkMetadata); - typedef DefaultSizeClassMap SizeClassMap; - typedef NoOpMapUnmapCallback MapUnmapCallback; - static const uptr kFlags = 0; -}; - -typedef SizeClassAllocator64 PrimaryAllocator; #endif -typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocator Allocator; static Allocator allocator; -static THREADLOCAL AllocatorCache cache; void InitializeAllocator() { allocator.InitLinkerInitialized( @@ -79,7 +44,7 @@ void InitializeAllocator() { } void AllocatorThreadFinish() { - allocator.SwallowCache(&cache); + allocator.SwallowCache(GetAllocatorCache()); } static ChunkMetadata *Metadata(const void *p) { @@ -111,7 +76,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); return nullptr; } - void *p = allocator.Allocate(&cache, size, alignment, false); + void *p = allocator.Allocate(GetAllocatorCache(), size, alignment, false); // Do not rely on the allocator to clear the memory (it's slow). if (cleared && allocator.FromPrimary(p)) memset(p, 0, size); @@ -125,7 +90,7 @@ void Deallocate(void *p) { if (&__sanitizer_free_hook) __sanitizer_free_hook(p); RunFreeHooks(p); RegisterDeallocation(p); - allocator.Deallocate(&cache, p); + allocator.Deallocate(GetAllocatorCache(), p); } void *Reallocate(const StackTrace &stack, void *p, uptr new_size, @@ -133,17 +98,17 @@ void *Reallocate(const StackTrace &stack, void *p, uptr new_size, RegisterDeallocation(p); if (new_size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); - allocator.Deallocate(&cache, p); + allocator.Deallocate(GetAllocatorCache(), p); return nullptr; } - p = allocator.Reallocate(&cache, p, new_size, alignment); + p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment); RegisterAllocation(stack, p, new_size); return p; } void GetAllocatorCacheRange(uptr *begin, uptr *end) { - *begin = (uptr)&cache; - *end = *begin + sizeof(cache); + *begin = (uptr)GetAllocatorCache(); + *end = *begin + sizeof(AllocatorCache); } uptr GetMallocUsableSize(const void *p) { diff --git a/lib/lsan/lsan_allocator.h b/lib/lsan/lsan_allocator.h index f564601193..9c3dce97a0 100644 --- a/lib/lsan/lsan_allocator.h +++ b/lib/lsan/lsan_allocator.h @@ -15,8 +15,10 @@ #ifndef LSAN_ALLOCATOR_H #define LSAN_ALLOCATOR_H +#include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" +#include "lsan_common.h" namespace __lsan { @@ -34,6 +36,41 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end); void AllocatorThreadFinish(); void InitializeAllocator(); +struct ChunkMetadata { + u8 allocated : 8; // Must be first. + ChunkTag tag : 2; +#if SANITIZER_WORDSIZE == 64 + uptr requested_size : 54; +#else + uptr requested_size : 32; + uptr padding : 22; +#endif + u32 stack_trace_id; +}; + +#if defined(__mips64) || defined(__aarch64__) || defined(__i386__) +static const uptr kRegionSizeLog = 20; +static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; +typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; +typedef CompactSizeClassMap SizeClassMap; +typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, + sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap> + PrimaryAllocator; +#elif defined(__x86_64__) +struct AP64 { // Allocator64 parameters. Deliberately using a short name. + static const uptr kSpaceBeg = 0x600000000000ULL; + static const uptr kSpaceSize = 0x40000000000ULL; // 4T. + static const uptr kMetadataSize = sizeof(ChunkMetadata); + typedef DefaultSizeClassMap SizeClassMap; + typedef NoOpMapUnmapCallback MapUnmapCallback; + static const uptr kFlags = 0; +}; + +typedef SizeClassAllocator64 PrimaryAllocator; +#endif +typedef SizeClassAllocatorLocalCache AllocatorCache; + +AllocatorCache *GetAllocatorCache(); } // namespace __lsan #endif // LSAN_ALLOCATOR_H diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index c457d1b84a..cb8126aa57 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -30,12 +30,14 @@ // To enable LeakSanitizer on new architecture, one need to implement // internal_clone function as well as (probably) adjust TLS machinery for // new architecture inside sanitizer library. -#if (SANITIZER_LINUX && !SANITIZER_ANDROID || CAN_SANITIZE_LEAKS_MAC) \ - && (SANITIZER_WORDSIZE == 64) && (defined(__x86_64__) \ - || defined(__mips64) || defined(__aarch64__)) +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) && \ + (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__)) #define CAN_SANITIZE_LEAKS 1 #elif SANITIZER_LINUX && !SANITIZER_ANDROID && defined(__i386__) #define CAN_SANITIZE_LEAKS 1 +#elif CAN_SANITIZE_LEAKS_MAC && (defined(__x86_64__) || defined(__mips64) || \ + defined(__aarch64__) || defined(__i386__)) +#define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 #endif diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc index 0e10d41914..931b5112a8 100644 --- a/lib/lsan/lsan_common_linux.cc +++ b/lib/lsan/lsan_common_linux.cc @@ -34,10 +34,6 @@ static bool IsLinker(const char* full_name) { return LibraryNameIs(full_name, kLinkerName); } -static THREADLOCAL u32 current_thread_tid = kInvalidTid; -u32 GetCurrentThread() { return current_thread_tid; } -void SetCurrentThread(u32 tid) { current_thread_tid = tid; } - __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter; bool DisabledInThisThread() { return disable_counter > 0; } diff --git a/lib/lsan/lsan_common_mac.cc b/lib/lsan/lsan_common_mac.cc index 7f5e0550dc..5fd2b89afd 100644 --- a/lib/lsan/lsan_common_mac.cc +++ b/lib/lsan/lsan_common_mac.cc @@ -12,12 +12,14 @@ // //===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_platform.h" #include "lsan_common.h" #if CAN_SANITIZE_LEAKS && SANITIZER_MAC +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "lsan_allocator.h" + #include namespace __lsan { @@ -25,6 +27,7 @@ namespace __lsan { typedef struct { int disable_counter; u32 current_thread_id; + AllocatorCache cache; } thread_local_data_t; static pthread_key_t key; @@ -32,39 +35,45 @@ static pthread_once_t key_once = PTHREAD_ONCE_INIT; static void make_tls_key() { CHECK_EQ(pthread_key_create(&key, NULL), 0); } -static thread_local_data_t *get_tls_val() { +static thread_local_data_t *get_tls_val(bool alloc) { pthread_once(&key_once, make_tls_key); thread_local_data_t *ptr = (thread_local_data_t *)pthread_getspecific(key); - if (ptr == NULL) { + if (ptr == NULL && alloc) { ptr = (thread_local_data_t *)InternalAlloc(sizeof(*ptr)); ptr->disable_counter = 0; ptr->current_thread_id = kInvalidTid; + ptr->cache = AllocatorCache(); pthread_setspecific(key, ptr); } return ptr; } -bool DisabledInThisThread() { return get_tls_val()->disable_counter > 0; } +bool DisabledInThisThread() { + thread_local_data_t *data = get_tls_val(false); + return data ? data->disable_counter > 0 : false; +} -void DisableInThisThread() { ++get_tls_val()->disable_counter; } +void DisableInThisThread() { ++get_tls_val(true)->disable_counter; } void EnableInThisThread() { - int *disable_counter = &get_tls_val()->disable_counter; + int *disable_counter = &get_tls_val(true)->disable_counter; if (*disable_counter == 0) { DisableCounterUnderflow(); } --*disable_counter; } -u32 GetCurrentThread() { return get_tls_val()->current_thread_id; } +u32 GetCurrentThread() { return get_tls_val(true)->current_thread_id; } -void SetCurrentThread(u32 tid) { get_tls_val()->current_thread_id = tid; } +void SetCurrentThread(u32 tid) { get_tls_val(true)->current_thread_id = tid; } -void InitializePlatformSpecificModules() { - CHECK(0 && "unimplemented"); -} +AllocatorCache *GetAllocatorCache() { return &get_tls_val(true)->cache; } + +// Required on Linux for initialization of TLS behavior, but should not be +// required on Darwin. +void InitializePlatformSpecificModules() {} // Scans global variables for heap pointers. void ProcessGlobalRegions(Frontier *frontier) { diff --git a/lib/lsan/lsan_linux.cc b/lib/lsan/lsan_linux.cc new file mode 100644 index 0000000000..a60f7413a5 --- /dev/null +++ b/lib/lsan/lsan_linux.cc @@ -0,0 +1,31 @@ +//=-- lsan_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 a part of LeakSanitizer. Linux-specific code. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +#if SANITIZER_LINUX + +#include "lsan_allocator.h" + +namespace __lsan { + +static THREADLOCAL u32 current_thread_tid = kInvalidTid; +u32 GetCurrentThread() { return current_thread_tid; } +void SetCurrentThread(u32 tid) { current_thread_tid = tid; } + +static THREADLOCAL AllocatorCache allocator_cache; +AllocatorCache *GetAllocatorCache() { return &allocator_cache; } + +} // namespace __lsan + +#endif // SANITIZER_LINUX diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index 6447bb1b27..15543bd912 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -123,14 +123,6 @@ static void *AllocateFromLocalPool(uptr size_in_bytes) { #define CHECK_UNPOISONED_STRING(x, n) \ CHECK_UNPOISONED_STRING_OF_LEN((x), internal_strlen(x), (n)) -INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { - ENSURE_MSAN_INITED(); - SIZE_T res = REAL(fread)(ptr, size, nmemb, file); - if (res > 0) - __msan_unpoison(ptr, res *size); - return res; -} - #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { @@ -580,6 +572,13 @@ INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { return res; } +INTERCEPTOR(SIZE_T, wcsnlen, const wchar_t *s, SIZE_T n) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(wcsnlen)(s, n); + CHECK_UNPOISONED(s, sizeof(wchar_t) * Min(res + 1, n)); + return res; +} + // wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) { ENSURE_MSAN_INITED(); @@ -597,6 +596,18 @@ INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { return res; } +INTERCEPTOR(wchar_t *, wcsncpy, wchar_t *dest, const wchar_t *src, + SIZE_T n) { // NOLINT + ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; + SIZE_T copy_size = REAL(wcsnlen)(src, n); + if (copy_size < n) copy_size++; // trailing \0 + wchar_t *res = REAL(wcsncpy)(dest, src, n); // NOLINT + CopyShadowAndOrigin(dest, src, copy_size * sizeof(wchar_t), &stack); + __msan_unpoison(dest + copy_size, (n - copy_size) * sizeof(wchar_t)); + return res; +} + // wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, SIZE_T n); INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); @@ -1565,8 +1576,10 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(mbtowc); INTERCEPT_FUNCTION(mbrtowc); INTERCEPT_FUNCTION(wcslen); + INTERCEPT_FUNCTION(wcsnlen); INTERCEPT_FUNCTION(wcschr); INTERCEPT_FUNCTION(wcscpy); + INTERCEPT_FUNCTION(wcsncpy); INTERCEPT_FUNCTION(wcscmp); INTERCEPT_FUNCTION(getenv); INTERCEPT_FUNCTION(setenv); diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index 6f4dd99b33..59c40d3847 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -3502,6 +3502,21 @@ TEST(MemorySanitizer, getgroups) { EXPECT_NOT_POISONED(gids[i]); } +TEST(MemorySanitizer, getgroups_zero) { + gid_t group; + int n = getgroups(0, &group); + ASSERT_GE(n, 0); +} + +TEST(MemorySanitizer, getgroups_negative) { + gid_t group; + int n = getgroups(-1, 0); + ASSERT_EQ(-1, n); + + n = getgroups(-1, 0); + ASSERT_EQ(-1, n); +} + TEST(MemorySanitizer, wordexp) { wordexp_t w; int res = wordexp("a b c", &w, 0); diff --git a/lib/profile/InstrProfData.inc b/lib/profile/InstrProfData.inc index f7c22d1076..6ef1625d81 100644 --- a/lib/profile/InstrProfData.inc +++ b/lib/profile/InstrProfData.inc @@ -153,7 +153,17 @@ INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last) VALUE_PROF_FUNC_PARAM(uint64_t, TargetValue, Type::getInt64Ty(Ctx)) \ INSTR_PROF_COMMA VALUE_PROF_FUNC_PARAM(void *, Data, Type::getInt8PtrTy(Ctx)) INSTR_PROF_COMMA +#ifndef VALUE_RANGE_PROF VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx)) +#else /* VALUE_RANGE_PROF */ +VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx)) \ + INSTR_PROF_COMMA +VALUE_PROF_FUNC_PARAM(uint64_t, PreciseRangeStart, Type::getInt64Ty(Ctx)) \ + INSTR_PROF_COMMA +VALUE_PROF_FUNC_PARAM(uint64_t, PreciseRangeLast, Type::getInt64Ty(Ctx)) \ + INSTR_PROF_COMMA +VALUE_PROF_FUNC_PARAM(uint64_t, LargeValue, Type::getInt64Ty(Ctx)) +#endif /*VALUE_RANGE_PROF */ #undef VALUE_PROF_FUNC_PARAM #undef INSTR_PROF_COMMA /* VALUE_PROF_FUNC_PARAM end */ @@ -174,13 +184,15 @@ VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx)) * name hash and the function address. */ VALUE_PROF_KIND(IPVK_IndirectCallTarget, 0) +/* For memory intrinsic functions size profiling. */ +VALUE_PROF_KIND(IPVK_MemOPSize, 1) /* These two kinds must be the last to be * declared. This is to make sure the string * array created with the template can be * indexed with the kind value. */ VALUE_PROF_KIND(IPVK_First, IPVK_IndirectCallTarget) -VALUE_PROF_KIND(IPVK_Last, IPVK_IndirectCallTarget) +VALUE_PROF_KIND(IPVK_Last, IPVK_MemOPSize) #undef VALUE_PROF_KIND /* VALUE_PROF_KIND end */ @@ -649,6 +661,9 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure, #define INSTR_PROF_VALUE_PROF_FUNC __llvm_profile_instrument_target #define INSTR_PROF_VALUE_PROF_FUNC_STR \ INSTR_PROF_QUOTE(INSTR_PROF_VALUE_PROF_FUNC) +#define INSTR_PROF_VALUE_RANGE_PROF_FUNC __llvm_profile_instrument_range +#define INSTR_PROF_VALUE_RANGE_PROF_FUNC_STR \ + INSTR_PROF_QUOTE(INSTR_PROF_VALUE_RANGE_PROF_FUNC) /* InstrProfile per-function control data alignment. */ #define INSTR_PROF_DATA_ALIGNMENT 8 diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c index cd3590e12f..dfcbe52d7e 100644 --- a/lib/profile/InstrProfilingFile.c +++ b/lib/profile/InstrProfilingFile.c @@ -530,6 +530,7 @@ int __llvm_profile_write_file(void) { int rc, Length; const char *Filename; char *FilenameBuf; + int PDeathSig = 0; if (lprofProfileDumped()) { PROF_NOTE("Profile data not written to file: %s.\n", @@ -556,10 +557,18 @@ int __llvm_profile_write_file(void) { return -1; } + // Temporarily suspend getting SIGKILL when the parent exits. + PDeathSig = lprofSuspendSigKill(); + /* Write profile data to the file. */ rc = writeFile(Filename); if (rc) PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno)); + + // Restore SIGKILL. + if (PDeathSig == 1) + lprofRestoreSigKill(); + return rc; } diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c index 321c7192cc..fb68f30a5e 100644 --- a/lib/profile/InstrProfilingUtil.c +++ b/lib/profile/InstrProfilingUtil.c @@ -29,6 +29,11 @@ #include #include +#if defined(__linux__) +#include +#include +#endif + COMPILER_RT_VISIBILITY void __llvm_profile_recursive_mkdir(char *path) { int i; @@ -219,3 +224,21 @@ COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) { #endif return Sep; } + +COMPILER_RT_VISIBILITY int lprofSuspendSigKill() { +#if defined(__linux__) + int PDeachSig = 0; + /* Temporarily suspend getting SIGKILL upon exit of the parent process. */ + if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL) + prctl(PR_SET_PDEATHSIG, 0); + return (PDeachSig == SIGKILL); +#else + return 0; +#endif +} + +COMPILER_RT_VISIBILITY void lprofRestoreSigKill() { +#if defined(__linux__) + prctl(PR_SET_PDEATHSIG, SIGKILL); +#endif +} diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h index a80fde77e1..9698599606 100644 --- a/lib/profile/InstrProfilingUtil.h +++ b/lib/profile/InstrProfilingUtil.h @@ -51,4 +51,12 @@ int lprofGetHostName(char *Name, int Len); unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV); void *lprofPtrFetchAdd(void **Mem, long ByteIncr); +/* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed. + * Other return values mean no restore is needed. + */ +int lprofSuspendSigKill(); + +/* Restore previously suspended SIGKILL. */ +void lprofRestoreSigKill(); + #endif /* PROFILE_INSTRPROFILINGUTIL_H */ diff --git a/lib/profile/InstrProfilingValue.c b/lib/profile/InstrProfilingValue.c index 6648f89235..44263da800 100644 --- a/lib/profile/InstrProfilingValue.c +++ b/lib/profile/InstrProfilingValue.c @@ -219,6 +219,35 @@ __llvm_profile_instrument_target(uint64_t TargetValue, void *Data, } } +/* + * The target values are partitioned into multiple regions/ranges. There is one + * contiguous region which is precise -- every value in the range is tracked + * individually. A value outside the precise region will be collapsed into one + * value depending on the region it falls in. + * + * There are three regions: + * 1. (-inf, PreciseRangeStart) and (PreciseRangeLast, LargeRangeValue) belong + * to one region -- all values here should be mapped to one value of + * "PreciseRangeLast + 1". + * 2. [PreciseRangeStart, PreciseRangeLast] + * 3. Large values: [LargeValue, +inf) maps to one value of LargeValue. + * + * The range for large values is optional. The default value of INT64_MIN + * indicates it is not specified. + */ +COMPILER_RT_VISIBILITY void __llvm_profile_instrument_range( + uint64_t TargetValue, void *Data, uint32_t CounterIndex, + int64_t PreciseRangeStart, int64_t PreciseRangeLast, int64_t LargeValue) { + + if (LargeValue != INT64_MIN && (int64_t)TargetValue >= LargeValue) + TargetValue = LargeValue; + else if ((int64_t)TargetValue < PreciseRangeStart || + (int64_t)TargetValue > PreciseRangeLast) + TargetValue = PreciseRangeLast + 1; + + __llvm_profile_instrument_target(TargetValue, Data, CounterIndex); +} + /* * A wrapper struct that represents value profile runtime data. * Like InstrProfRecord class which is used by profiling host tools, diff --git a/lib/sanitizer_common/sanitizer_allocator_interface.h b/lib/sanitizer_common/sanitizer_allocator_interface.h index 74ee903c35..13910e719e 100644 --- a/lib/sanitizer_common/sanitizer_allocator_interface.h +++ b/lib/sanitizer_common/sanitizer_allocator_interface.h @@ -38,9 +38,8 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_free_hook(void *ptr); - -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE - void __sanitizer_print_memory_profile(int top_percent); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void +__sanitizer_print_memory_profile(uptr top_percent, uptr max_number_of_contexts); } // extern "C" #endif // SANITIZER_ALLOCATOR_INTERFACE_H diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc index 9078a900b5..36f391118f 100644 --- a/lib/sanitizer_common/sanitizer_common.cc +++ b/lib/sanitizer_common/sanitizer_common.cc @@ -505,11 +505,4 @@ int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *, void (*free_hook)(const void *)) { return InstallMallocFreeHooks(malloc_hook, free_hook); } - -#if !SANITIZER_GO -SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_print_memory_profile, - int top_percent) { - (void)top_percent; -} -#endif } // extern "C" diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc index fbffb56dbf..7ecde22943 100644 --- a/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -24,7 +24,8 @@ // COMMON_INTERCEPTOR_SET_THREAD_NAME // COMMON_INTERCEPTOR_ON_DLOPEN // COMMON_INTERCEPTOR_ON_EXIT -// COMMON_INTERCEPTOR_MUTEX_LOCK +// COMMON_INTERCEPTOR_MUTEX_PRE_LOCK +// COMMON_INTERCEPTOR_MUTEX_POST_LOCK // COMMON_INTERCEPTOR_MUTEX_UNLOCK // COMMON_INTERCEPTOR_MUTEX_REPAIR // COMMON_INTERCEPTOR_SET_PTHREAD_NAME @@ -89,8 +90,12 @@ bool PlatformHasDifferentMemcpyAndMemmove(); #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} #endif -#ifndef COMMON_INTERCEPTOR_MUTEX_LOCK -#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) {} +#ifndef COMMON_INTERCEPTOR_MUTEX_PRE_LOCK +#define COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_POST_LOCK +#define COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK @@ -495,6 +500,52 @@ INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) { #define INIT_STRCASESTR #endif +#if SANITIZER_INTERCEPT_STRTOK + +INTERCEPTOR(char*, strtok, char *str, const char *delimiters) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strtok, str, delimiters); + if (!common_flags()->intercept_strtok) { + return REAL(strtok)(str, delimiters); + } + if (common_flags()->strict_string_checks) { + // If strict_string_checks is enabled, we check the whole first argument + // string on the first call (strtok saves this string in a static buffer + // for subsequent calls). We do not need to check strtok's result. + // As the delimiters can change, we check them every call. + if (str != nullptr) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1); + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, delimiters, + REAL(strlen)(delimiters) + 1); + return REAL(strtok)(str, delimiters); + } else { + // However, when strict_string_checks is disabled we cannot check the + // whole string on the first call. Instead, we check the result string + // which is guaranteed to be a NULL-terminated substring of the first + // argument. We also conservatively check one character of str and the + // delimiters. + if (str != nullptr) { + COMMON_INTERCEPTOR_READ_STRING(ctx, str, 1); + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, delimiters, 1); + char *result = REAL(strtok)(str, delimiters); + if (result != nullptr) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, result, REAL(strlen)(result) + 1); + } else if (str != nullptr) { + // No delimiter were found, it's safe to assume that the entire str was + // scanned. + COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1); + } + return result; + } +} + +#define INIT_STRTOK COMMON_INTERCEPT_FUNCTION(strtok) +#else +#define INIT_STRTOK +#endif + #if SANITIZER_INTERCEPT_MEMMEM DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memmem, uptr called_pc, const void *s1, SIZE_T len1, const void *s2, @@ -837,6 +888,23 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { #define INIT_READ #endif +#if SANITIZER_INTERCEPT_FREAD +INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { + // libc file streams can call user-supplied functions, see fopencookie. + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fread, ptr, size, nmemb, file); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(fread)(ptr, size, nmemb, file); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res * size); + return res; +} +#define INIT_FREAD COMMON_INTERCEPT_FUNCTION(fread) +#else +#define INIT_FREAD +#endif + #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; @@ -937,6 +1005,20 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { #define INIT_WRITE #endif +#if SANITIZER_INTERCEPT_FWRITE +INTERCEPTOR(SIZE_T, fwrite, const void *p, uptr size, uptr nmemb, void *file) { + // libc file streams can call user-supplied functions, see fopencookie. + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fwrite, p, size, nmemb, file); + SIZE_T res = REAL(fwrite)(p, size, nmemb, file); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, p, res * size); + return res; +} +#define INIT_FWRITE COMMON_INTERCEPT_FUNCTION(fwrite) +#else +#define INIT_FWRITE +#endif + #if SANITIZER_INTERCEPT_PWRITE INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; @@ -3420,7 +3502,8 @@ INTERCEPTOR(int, getgroups, int size, u32 *lst) { // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgroups)(size, lst); - if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); + if (res >= 0 && lst && size > 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); return res; } #define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); @@ -3675,11 +3758,12 @@ INTERCEPTOR(void, _exit, int status) { INTERCEPTOR(int, pthread_mutex_lock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); + COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m); int res = REAL(pthread_mutex_lock)(m); if (res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); if (res == 0 || res == errno_EOWNERDEAD) - COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m); if (res == errno_EINVAL) COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m); return res; @@ -6047,6 +6131,21 @@ INTERCEPTOR(void *, getutxline, void *ut) { #define INIT_UTMPX #endif +#if SANITIZER_INTERCEPT_GETLOADAVG +INTERCEPTOR(int, getloadavg, double *loadavg, int nelem) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getloadavg, loadavg, nelem); + int res = REAL(getloadavg)(loadavg, nelem); + if (res > 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, loadavg, res * sizeof(*loadavg)); + return res; +} +#define INIT_GETLOADAVG \ + COMMON_INTERCEPT_FUNCTION(getloadavg); +#else +#define INIT_GETLOADAVG +#endif + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); @@ -6064,6 +6163,7 @@ static void InitializeCommonInterceptors() { INIT_STRCHRNUL; INIT_STRRCHR; INIT_STRSPN; + INIT_STRTOK; INIT_STRPBRK; INIT_MEMSET; INIT_MEMMOVE; @@ -6073,12 +6173,14 @@ static void InitializeCommonInterceptors() { INIT_MEMRCHR; INIT_MEMMEM; INIT_READ; + INIT_FREAD; INIT_PREAD; INIT_PREAD64; INIT_READV; INIT_PREADV; INIT_PREADV64; INIT_WRITE; + INIT_FWRITE; INIT_PWRITE; INIT_PWRITE64; INIT_WRITEV; @@ -6245,4 +6347,5 @@ static void InitializeCommonInterceptors() { // FIXME: add other *stat interceptors. INIT_UTMP; INIT_UTMPX; + INIT_GETLOADAVG; } diff --git a/lib/sanitizer_common/sanitizer_common_interface.inc b/lib/sanitizer_common/sanitizer_common_interface.inc index 4f0e940a13..550427c906 100644 --- a/lib/sanitizer_common/sanitizer_common_interface.inc +++ b/lib/sanitizer_common/sanitizer_common_interface.inc @@ -34,6 +34,6 @@ INTERFACE_FUNCTION(__sanitizer_get_heap_size) INTERFACE_FUNCTION(__sanitizer_get_ownership) INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks) +INTERFACE_FUNCTION(__sanitizer_print_memory_profile) INTERFACE_WEAK_FUNCTION(__sanitizer_free_hook) INTERFACE_WEAK_FUNCTION(__sanitizer_malloc_hook) -INTERFACE_WEAK_FUNCTION(__sanitizer_print_memory_profile) diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc index e96db6dd61..430318863f 100644 --- a/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -123,7 +123,7 @@ void BackgroundThread(void *arg) { if (heap_profile && current_rss_mb > rss_during_last_reported_profile * 1.1) { Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb); - __sanitizer_print_memory_profile(90); + __sanitizer_print_memory_profile(90, 20); rss_during_last_reported_profile = current_rss_mb; } } diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc index d799f6ef7e..e61d1c3b4f 100644 --- a/lib/sanitizer_common/sanitizer_flags.inc +++ b/lib/sanitizer_common/sanitizer_flags.inc @@ -62,8 +62,7 @@ COMMON_FLAG( COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") -COMMON_FLAG(bool, detect_leaks, SANITIZER_WORDSIZE == 64, - "Enable memory leak detection.") +COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.") COMMON_FLAG( bool, leak_check_at_exit, true, "Invoke leak checking in an atexit handler. Has no effect if " @@ -80,7 +79,9 @@ COMMON_FLAG(int, print_module_map, 0, "exits, 2 = print after each report.") COMMON_FLAG(bool, check_printf, true, "Check printf arguments.") COMMON_FLAG(bool, handle_segv, true, - "If set, registers the tool's custom SIGSEGV/SIGBUS handler.") + "If set, registers the tool's custom SIGSEGV handler.") +COMMON_FLAG(bool, handle_sigbus, true, + "If set, registers the tool's custom SIGBUS handler.") COMMON_FLAG(bool, handle_abort, false, "If set, registers the tool's custom SIGABRT handler.") COMMON_FLAG(bool, handle_sigill, false, @@ -191,6 +192,9 @@ COMMON_FLAG(bool, intercept_strstr, true, COMMON_FLAG(bool, intercept_strspn, true, "If set, uses custom wrappers for strspn and strcspn function " "to find more errors.") +COMMON_FLAG(bool, intercept_strtok, true, + "If set, uses a custom wrapper for the strtok function " + "to find more errors.") COMMON_FLAG(bool, intercept_strpbrk, true, "If set, uses custom wrappers for strpbrk function " "to find more errors.") diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc index 5f84dc5110..ab65e2db57 100644 --- a/lib/sanitizer_common/sanitizer_linux.cc +++ b/lib/sanitizer_common/sanitizer_linux.cc @@ -77,6 +77,20 @@ extern char **environ; // provided by crt1 #include #endif +#ifndef __GLIBC_PREREQ +#define __GLIBC_PREREQ(x, y) 0 +#endif + +#if SANITIZER_LINUX && __GLIBC_PREREQ(2, 16) +# define SANITIZER_USE_GETAUXVAL 1 +#else +# define SANITIZER_USE_GETAUXVAL 0 +#endif + +#if SANITIZER_USE_GETAUXVAL +#include +#endif + #if SANITIZER_LINUX // struct kernel_timeval { @@ -805,6 +819,8 @@ uptr GetPageSize() { return 4096; #elif SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) return EXEC_PAGESIZE; +#elif SANITIZER_USE_GETAUXVAL + return getauxval(AT_PAGESZ); #else return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy. #endif @@ -1292,7 +1308,9 @@ bool IsHandledDeadlySignal(int signum) { return true; if (common_flags()->handle_sigfpe && signum == SIGFPE) return true; - return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; + if (common_flags()->handle_segv && signum == SIGSEGV) + return true; + return common_flags()->handle_sigbus && signum == SIGBUS; } #if !SANITIZER_GO @@ -1461,7 +1479,7 @@ void MaybeReexec() { void PrintModuleMap() { } void CheckNoDeepBind(const char *filename, int flag) { -#if !SANITIZER_ANDROID +#ifdef RTLD_DEEPBIND if (flag & RTLD_DEEPBIND) { Report( "You are trying to dlopen a %s shared library with RTLD_DEEPBIND flag" diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc index 8101d95158..34af4e9187 100644 --- a/lib/sanitizer_common/sanitizer_mac.cc +++ b/lib/sanitizer_common/sanitizer_mac.cc @@ -102,12 +102,12 @@ extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE; uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd, u64 offset) { if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL); - if (__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset); + if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset); return (uptr)mmap(addr, length, prot, flags, fd, offset); } uptr internal_munmap(void *addr, uptr length) { - if (__munmap) return __munmap(addr, length); + if (&__munmap) return __munmap(addr, length); return munmap(addr, length); } @@ -198,7 +198,7 @@ uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE; int internal_fork() { - if (__fork) + if (&__fork) return __fork(); return fork(); } @@ -404,7 +404,9 @@ bool IsHandledDeadlySignal(int signum) { return true; if (common_flags()->handle_sigfpe && signum == SIGFPE) return true; - return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; + if (common_flags()->handle_segv && signum == SIGSEGV) + return true; + return common_flags()->handle_sigbus && signum == SIGBUS; } MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h index 480e63a14b..a583e989c3 100644 --- a/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -74,6 +74,7 @@ #define SANITIZER_INTERCEPT_STRCMP 1 #define SANITIZER_INTERCEPT_STRSTR 1 #define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_STRTOK 1 #define SANITIZER_INTERCEPT_STRCHR 1 #define SANITIZER_INTERCEPT_STRCHRNUL SI_UNIX_NOT_MAC #define SANITIZER_INTERCEPT_STRRCHR 1 @@ -103,6 +104,9 @@ #define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_FREAD SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_FWRITE SI_NOT_WINDOWS + #define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID @@ -326,6 +330,9 @@ #define SANITIZER_INTERCEPT_UTMP SI_NOT_WINDOWS && !SI_MAC && !SI_FREEBSD #define SANITIZER_INTERCEPT_UTMPX SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD +#define SANITIZER_INTERCEPT_GETLOADAVG \ + SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD + #define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO (!SI_FREEBSD && !SI_MAC) #define SANITIZER_INTERCEPT_MEMALIGN (!SI_FREEBSD && !SI_MAC) #define SANITIZER_INTERCEPT_PVALLOC (!SI_FREEBSD && !SI_MAC) diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc index b9cfb06bbf..9916f4d385 100644 --- a/lib/sanitizer_common/sanitizer_posix.cc +++ b/lib/sanitizer_common/sanitizer_posix.cc @@ -366,8 +366,10 @@ const char *DescribeSignalOrException(int signo) { return "ILL"; case SIGABRT: return "ABRT"; - default: + case SIGSEGV: return "SEGV"; + case SIGBUS: + return "BUS"; } return "UNKNOWN SIGNAL"; } diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc b/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc index 4cccc295ed..a7aa2f06c9 100644 --- a/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc +++ b/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc @@ -13,7 +13,8 @@ #include "sanitizer_platform.h" -#if SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) +#if SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__) || \ + defined(__mips64) || defined(__i386)) #include "sanitizer_stoptheworld.h" @@ -35,4 +36,5 @@ uptr SuspendedThreadsList::RegisterCount() { } } // namespace __sanitizer -#endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) +#endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) || + // defined(__mips64) || defined(__i386)) diff --git a/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt b/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt index 033acf7f20..737f9459d2 100644 --- a/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt +++ b/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt @@ -37,6 +37,7 @@ clock_gettime U cfgetospeed U dl_iterate_phdr U dlsym U +dup U dup2 U environ U execv U diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc index 7fb4eb2a5f..d7a9e0b679 100644 --- a/lib/tsan/go/tsan_go.cc +++ b/lib/tsan/go/tsan_go.cc @@ -247,13 +247,17 @@ void __tsan_finalizer_goroutine(ThreadState *thr) { } void __tsan_mutex_before_lock(ThreadState *thr, uptr addr, uptr write) { + if (write) + MutexPreLock(thr, 0, addr); + else + MutexPreReadLock(thr, 0, addr); } void __tsan_mutex_after_lock(ThreadState *thr, uptr addr, uptr write) { if (write) - MutexLock(thr, 0, addr); + MutexPostLock(thr, 0, addr); else - MutexReadLock(thr, 0, addr); + MutexPostReadLock(thr, 0, addr); } void __tsan_mutex_before_unlock(ThreadState *thr, uptr addr, uptr write) { diff --git a/lib/tsan/rtl/tsan.syms.extra b/lib/tsan/rtl/tsan.syms.extra index 22dfde9141..ab5b5a4fcb 100644 --- a/lib/tsan/rtl/tsan.syms.extra +++ b/lib/tsan/rtl/tsan.syms.extra @@ -9,6 +9,16 @@ __tsan_java* __tsan_unaligned* __tsan_release __tsan_acquire +__tsan_mutex_create +__tsan_mutex_destroy +__tsan_mutex_pre_lock +__tsan_mutex_post_lock +__tsan_mutex_pre_unlock +__tsan_mutex_post_unlock +__tsan_mutex_pre_signal +__tsan_mutex_post_signal +__tsan_mutex_pre_divert +__tsan_mutex_post_divert __ubsan_* Annotate* WTFAnnotate* diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index d8d4746ab5..89e22a1327 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -21,10 +21,6 @@ namespace __tsan { -Flags *flags() { - return &ctx->flags; -} - // Can be overriden in frontend. #ifdef TSAN_EXTERNAL_HOOKS extern "C" const char* __tsan_default_options(); diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h index e2f6b3c9f0..66740def52 100644 --- a/lib/tsan/rtl/tsan_flags.h +++ b/lib/tsan/rtl/tsan_flags.h @@ -28,7 +28,6 @@ struct Flags : DDFlags { void ParseFromString(const char *str); }; -Flags *flags(); void InitializeFlags(Flags *flags, const char *env); } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index 9bf1b28b93..bb2badbff2 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -277,7 +277,7 @@ ScopedInterceptor::~ScopedInterceptor() { void ScopedInterceptor::EnableIgnores() { if (ignoring_) { - ThreadIgnoreBegin(thr_, pc_); + ThreadIgnoreBegin(thr_, pc_, false); if (in_ignored_lib_) { DCHECK(!thr_->in_ignored_lib); thr_->in_ignored_lib = true; @@ -1025,7 +1025,7 @@ static void cond_mutex_unlock(CondMutexUnlockCtx *arg) { ThreadSignalContext *ctx = SigCtx(arg->thr); CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1); atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed); - MutexLock(arg->thr, arg->pc, (uptr)arg->m); + MutexPostLock(arg->thr, arg->pc, (uptr)arg->m, MutexFlagDoPreLockOnPostLock); // Undo BlockingCall ctor effects. arg->thr->ignore_interceptors--; arg->si->~ScopedInterceptor(); @@ -1054,7 +1054,7 @@ static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, fn, c, m, t, (void (*)(void *arg))cond_mutex_unlock, &arg); } if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); - MutexLock(thr, pc, (uptr)m); + MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock); return res; } @@ -1114,14 +1114,15 @@ TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a); int res = REAL(pthread_mutex_init)(m, a); if (res == 0) { - bool recursive = false; + u32 flagz = 0; if (a) { int type = 0; if (REAL(pthread_mutexattr_gettype)(a, &type) == 0) - recursive = (type == PTHREAD_MUTEX_RECURSIVE - || type == PTHREAD_MUTEX_RECURSIVE_NP); + if (type == PTHREAD_MUTEX_RECURSIVE || + type == PTHREAD_MUTEX_RECURSIVE_NP) + flagz |= MutexFlagWriteReentrant; } - MutexCreate(thr, pc, (uptr)m, false, recursive, false); + MutexCreate(thr, pc, (uptr)m, flagz); } return res; } @@ -1141,7 +1142,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { if (res == EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); if (res == 0 || res == EOWNERDEAD) - MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true); + MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock); return res; } @@ -1150,7 +1151,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime); int res = REAL(pthread_mutex_timedlock)(m, abstime); if (res == 0) { - MutexLock(thr, pc, (uptr)m); + MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock); } return res; } @@ -1161,7 +1162,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); if (res == 0) { - MutexCreate(thr, pc, (uptr)m, false, false, false); + MutexCreate(thr, pc, (uptr)m); } return res; } @@ -1177,9 +1178,10 @@ TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) { TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m); + MutexPreLock(thr, pc, (uptr)m); int res = REAL(pthread_spin_lock)(m); if (res == 0) { - MutexLock(thr, pc, (uptr)m); + MutexPostLock(thr, pc, (uptr)m); } return res; } @@ -1188,7 +1190,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m); int res = REAL(pthread_spin_trylock)(m); if (res == 0) { - MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true); + MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock); } return res; } @@ -1205,7 +1207,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a); int res = REAL(pthread_rwlock_init)(m, a); if (res == 0) { - MutexCreate(thr, pc, (uptr)m, true, false, false); + MutexCreate(thr, pc, (uptr)m); } return res; } @@ -1221,9 +1223,10 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) { TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m); + MutexPreReadLock(thr, pc, (uptr)m); int res = REAL(pthread_rwlock_rdlock)(m); if (res == 0) { - MutexReadLock(thr, pc, (uptr)m); + MutexPostReadLock(thr, pc, (uptr)m); } return res; } @@ -1232,7 +1235,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m); int res = REAL(pthread_rwlock_tryrdlock)(m); if (res == 0) { - MutexReadLock(thr, pc, (uptr)m, /*try_lock=*/true); + MutexPostReadLock(thr, pc, (uptr)m, MutexFlagTryLock); } return res; } @@ -1242,7 +1245,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime); int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); if (res == 0) { - MutexReadLock(thr, pc, (uptr)m); + MutexPostReadLock(thr, pc, (uptr)m); } return res; } @@ -1250,9 +1253,10 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m); + MutexPreLock(thr, pc, (uptr)m); int res = REAL(pthread_rwlock_wrlock)(m); if (res == 0) { - MutexLock(thr, pc, (uptr)m); + MutexPostLock(thr, pc, (uptr)m); } return res; } @@ -1261,7 +1265,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m); int res = REAL(pthread_rwlock_trywrlock)(m); if (res == 0) { - MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true); + MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock); } return res; } @@ -1271,7 +1275,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime); int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); if (res == 0) { - MutexLock(thr, pc, (uptr)m); + MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock); } return res; } @@ -1644,24 +1648,6 @@ TSAN_INTERCEPTOR(void*, tmpfile64, int fake) { #define TSAN_MAYBE_INTERCEPT_TMPFILE64 #endif -TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { - // libc file streams can call user-supplied functions, see fopencookie. - { - SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); - MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); - } - return REAL(fread)(ptr, size, nmemb, f); -} - -TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { - // libc file streams can call user-supplied functions, see fopencookie. - { - SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); - MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); - } - return REAL(fwrite)(p, size, nmemb, f); -} - static void FlushStreams() { // Flushing all the streams here may freeze the process if a child thread is // performing file stream operations at the same time. @@ -2251,8 +2237,12 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, #define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ OnExit(((TsanInterceptorContext *) ctx)->thr) -#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ - MutexLock(((TsanInterceptorContext *)ctx)->thr, \ +#define COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m) \ + MutexPreLock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m) \ + MutexPostLock(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)m) #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ diff --git a/lib/tsan/rtl/tsan_interceptors_mac.cc b/lib/tsan/rtl/tsan_interceptors_mac.cc index fc5eb04990..f6bf8a0e58 100644 --- a/lib/tsan/rtl/tsan_interceptors_mac.cc +++ b/lib/tsan/rtl/tsan_interceptors_mac.cc @@ -281,6 +281,12 @@ TSAN_INTERCEPTOR(void, xpc_connection_send_message_with_reply, (connection, message, replyq, new_handler); } +TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) { + SCOPED_TSAN_INTERCEPTOR(xpc_connection_cancel, connection); + Release(thr, pc, (uptr)connection); + REAL(xpc_connection_cancel)(connection); +} + // On macOS, libc++ is always linked dynamically, so intercepting works the // usual way. #define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc index 62db796616..810c84025f 100644 --- a/lib/tsan/rtl/tsan_interface_ann.cc +++ b/lib/tsan/rtl/tsan_interface_ann.cc @@ -31,11 +31,10 @@ namespace __tsan { class ScopedAnnotation { public: - ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l, - uptr pc) + ScopedAnnotation(ThreadState *thr, const char *aname, uptr pc) : thr_(thr) { FuncEntry(thr_, pc); - DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l); + DPrintf("#%d: annotation %s()\n", thr_->tid, aname); } ~ScopedAnnotation() { @@ -46,18 +45,20 @@ class ScopedAnnotation { ThreadState *const thr_; }; -#define SCOPED_ANNOTATION(typ) \ +#define SCOPED_ANNOTATION_RET(typ, ret) \ if (!flags()->enable_annotations) \ - return; \ + return ret; \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = (uptr)__builtin_return_address(0); \ StatInc(thr, StatAnnotation); \ StatInc(thr, Stat##typ); \ - ScopedAnnotation sa(thr, __func__, f, l, caller_pc); \ + ScopedAnnotation sa(thr, __func__, caller_pc); \ const uptr pc = StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ +#define SCOPED_ANNOTATION(typ) SCOPED_ANNOTATION_RET(typ, ) + static const int kMaxDescLen = 128; struct ExpectRace { @@ -252,12 +253,12 @@ void INTERFACE_ATTRIBUTE AnnotateCondVarWait(char *f, int l, uptr cv, void INTERFACE_ATTRIBUTE AnnotateRWLockCreate(char *f, int l, uptr m) { SCOPED_ANNOTATION(AnnotateRWLockCreate); - MutexCreate(thr, pc, m, true, true, false); + MutexCreate(thr, pc, m, MutexFlagWriteReentrant); } void INTERFACE_ATTRIBUTE AnnotateRWLockCreateStatic(char *f, int l, uptr m) { SCOPED_ANNOTATION(AnnotateRWLockCreateStatic); - MutexCreate(thr, pc, m, true, true, true); + MutexCreate(thr, pc, m, MutexFlagWriteReentrant | MutexFlagLinkerInit); } void INTERFACE_ATTRIBUTE AnnotateRWLockDestroy(char *f, int l, uptr m) { @@ -269,9 +270,9 @@ void INTERFACE_ATTRIBUTE AnnotateRWLockAcquired(char *f, int l, uptr m, uptr is_w) { SCOPED_ANNOTATION(AnnotateRWLockAcquired); if (is_w) - MutexLock(thr, pc, m); + MutexPostLock(thr, pc, m, MutexFlagDoPreLockOnPostLock); else - MutexReadLock(thr, pc, m); + MutexPostReadLock(thr, pc, m, MutexFlagDoPreLockOnPostLock); } void INTERFACE_ATTRIBUTE AnnotateRWLockReleased(char *f, int l, uptr m, @@ -458,4 +459,95 @@ void INTERFACE_ATTRIBUTE AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {} void INTERFACE_ATTRIBUTE AnnotateMemoryIsUninitialized(char *f, int l, uptr mem, uptr sz) {} + +// Note: the parameter is called flagz, because flags is already taken +// by the global function that returns flags. +INTERFACE_ATTRIBUTE +void __tsan_mutex_create(void *m, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_create); + MutexCreate(thr, pc, (uptr)m, flagz & MutexCreationFlagMask); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_destroy(void *m, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_destroy); + MutexDestroy(thr, pc, (uptr)m); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_pre_lock(void *m, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_pre_lock); + if (!(flagz & MutexFlagTryLock)) { + if (flagz & MutexFlagReadLock) + MutexPreReadLock(thr, pc, (uptr)m); + else + MutexPreLock(thr, pc, (uptr)m); + } + ThreadIgnoreBegin(thr, pc, false); + ThreadIgnoreSyncBegin(thr, pc, false); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_post_lock(void *m, unsigned flagz, int rec) { + SCOPED_ANNOTATION(__tsan_mutex_post_lock); + ThreadIgnoreSyncEnd(thr, pc); + ThreadIgnoreEnd(thr, pc); + if (!(flagz & MutexFlagTryLockFailed)) { + if (flagz & MutexFlagReadLock) + MutexPostReadLock(thr, pc, (uptr)m, flagz); + else + MutexPostLock(thr, pc, (uptr)m, flagz, rec); + } +} + +INTERFACE_ATTRIBUTE +int __tsan_mutex_pre_unlock(void *m, unsigned flagz) { + SCOPED_ANNOTATION_RET(__tsan_mutex_pre_unlock, 0); + int ret = 0; + if (flagz & MutexFlagReadLock) { + CHECK(!(flagz & MutexFlagRecursiveUnlock)); + MutexReadUnlock(thr, pc, (uptr)m); + } else { + ret = MutexUnlock(thr, pc, (uptr)m, flagz); + } + ThreadIgnoreBegin(thr, pc, false); + ThreadIgnoreSyncBegin(thr, pc, false); + return ret; +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_post_unlock(void *m, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_post_unlock); + ThreadIgnoreSyncEnd(thr, pc); + ThreadIgnoreEnd(thr, pc); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_pre_signal(void *addr, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_pre_signal); + ThreadIgnoreBegin(thr, pc, false); + ThreadIgnoreSyncBegin(thr, pc, false); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_post_signal(void *addr, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_post_signal); + ThreadIgnoreSyncEnd(thr, pc); + ThreadIgnoreEnd(thr, pc); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_pre_divert(void *addr, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_pre_divert); + // Exit from ignore region started in __tsan_mutex_pre_lock/unlock/signal. + ThreadIgnoreSyncEnd(thr, pc); + ThreadIgnoreEnd(thr, pc); +} + +INTERFACE_ATTRIBUTE +void __tsan_mutex_post_divert(void *addr, unsigned flagz) { + SCOPED_ANNOTATION(__tsan_mutex_post_divert); + ThreadIgnoreBegin(thr, pc, false); + ThreadIgnoreSyncBegin(thr, pc, false); +} } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc index 5238b66a2e..b22d5c1ece 100644 --- a/lib/tsan/rtl/tsan_interface_atomic.cc +++ b/lib/tsan/rtl/tsan_interface_atomic.cc @@ -450,13 +450,32 @@ static void AtomicFence(ThreadState *thr, uptr pc, morder mo) { // C/C++ +static morder convert_morder(morder mo) { + if (flags()->force_seq_cst_atomics) + return (morder)mo_seq_cst; + + // Filter out additional memory order flags: + // MEMMODEL_SYNC = 1 << 15 + // __ATOMIC_HLE_ACQUIRE = 1 << 16 + // __ATOMIC_HLE_RELEASE = 1 << 17 + // + // HLE is an optimization, and we pretend that elision always fails. + // MEMMODEL_SYNC is used when lowering __sync_ atomics, + // since we use __sync_ atomics for actual atomic operations, + // we can safely ignore it as well. It also subtly affects semantics, + // but we don't model the difference. + return (morder)(mo & 0x7fff); +} + #define SCOPED_ATOMIC(func, ...) \ - const uptr callpc = (uptr)__builtin_return_address(0); \ - uptr pc = StackTrace::GetCurrentPc(); \ - mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ ThreadState *const thr = cur_thread(); \ - if (thr->ignore_interceptors) \ + if (thr->ignore_sync || thr->ignore_interceptors) { \ + ProcessPendingSignals(thr); \ return NoTsanAtomic##func(__VA_ARGS__); \ + } \ + const uptr callpc = (uptr)__builtin_return_address(0); \ + uptr pc = StackTrace::GetCurrentPc(); \ + mo = convert_morder(mo); \ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ ScopedAtomic sa(thr, callpc, a, mo, __func__); \ return Atomic##func(thr, pc, __VA_ARGS__); \ diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc index 5bdc04f075..75e960e629 100644 --- a/lib/tsan/rtl/tsan_interface_java.cc +++ b/lib/tsan/rtl/tsan_interface_java.cc @@ -180,8 +180,8 @@ void __tsan_java_mutex_lock(jptr addr) { CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); - MutexCreate(thr, pc, addr, true, true, true); - MutexLock(thr, pc, addr); + MutexPostLock(thr, pc, addr, MutexFlagLinkerInit | MutexFlagWriteReentrant | + MutexFlagDoPreLockOnPostLock); } void __tsan_java_mutex_unlock(jptr addr) { @@ -201,8 +201,8 @@ void __tsan_java_mutex_read_lock(jptr addr) { CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); - MutexCreate(thr, pc, addr, true, true, true); - MutexReadLock(thr, pc, addr); + MutexPostReadLock(thr, pc, addr, MutexFlagLinkerInit | + MutexFlagWriteReentrant | MutexFlagDoPreLockOnPostLock); } void __tsan_java_mutex_read_unlock(jptr addr) { @@ -223,8 +223,8 @@ void __tsan_java_mutex_lock_rec(jptr addr, int rec) { CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); CHECK_GT(rec, 0); - MutexCreate(thr, pc, addr, true, true, true); - MutexLock(thr, pc, addr, rec); + MutexPostLock(thr, pc, addr, MutexFlagLinkerInit | MutexFlagWriteReentrant | + MutexFlagDoPreLockOnPostLock | MutexFlagRecursiveLock, rec); } int __tsan_java_mutex_unlock_rec(jptr addr) { @@ -234,7 +234,7 @@ int __tsan_java_mutex_unlock_rec(jptr addr) { CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); - return MutexUnlock(thr, pc, addr, true); + return MutexUnlock(thr, pc, addr, MutexFlagRecursiveUnlock); } void __tsan_java_acquire(jptr addr) { diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc index d8c689ebb5..8c759a3be4 100644 --- a/lib/tsan/rtl/tsan_libdispatch_mac.cc +++ b/lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -93,14 +93,15 @@ static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc, new_context->free_context_in_callback = true; new_context->submitted_synchronously = false; new_context->is_barrier_block = false; + new_context->non_queue_sync_object = 0; return new_context; } -#define GET_QUEUE_SYNC_VARS(context, q) \ - bool is_queue_serial = q && IsQueueSerial(q); \ - uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \ - uptr serial_sync = (uptr)sync_ptr; \ - uptr concurrent_sync = ((uptr)sync_ptr) + sizeof(uptr); \ +#define GET_QUEUE_SYNC_VARS(context, q) \ + bool is_queue_serial = q && IsQueueSerial(q); \ + uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \ + uptr serial_sync = (uptr)sync_ptr; \ + uptr concurrent_sync = sync_ptr ? ((uptr)sync_ptr) + sizeof(uptr) : 0; \ bool serial_task = context->is_barrier_block || is_queue_serial static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc, @@ -111,8 +112,8 @@ static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc, dispatch_queue_t q = context->queue; do { GET_QUEUE_SYNC_VARS(context, q); - Acquire(thr, pc, serial_sync); - if (serial_task) Acquire(thr, pc, concurrent_sync); + if (serial_sync) Acquire(thr, pc, serial_sync); + if (serial_task && concurrent_sync) Acquire(thr, pc, concurrent_sync); if (q) q = GetTargetQueueFromQueue(q); } while (q); @@ -126,7 +127,8 @@ static void dispatch_sync_post_execute(ThreadState *thr, uptr pc, dispatch_queue_t q = context->queue; do { GET_QUEUE_SYNC_VARS(context, q); - Release(thr, pc, serial_task ? serial_sync : concurrent_sync); + if (serial_task && serial_sync) Release(thr, pc, serial_sync); + if (!serial_task && concurrent_sync) Release(thr, pc, concurrent_sync); if (q) q = GetTargetQueueFromQueue(q); } while (q); diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index bc5991c6e0..70393037e7 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -980,21 +980,21 @@ void FuncExit(ThreadState *thr) { thr->shadow_stack_pos--; } -void ThreadIgnoreBegin(ThreadState *thr, uptr pc) { +void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack) { DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); thr->ignore_reads_and_writes++; CHECK_GT(thr->ignore_reads_and_writes, 0); thr->fast_state.SetIgnoreBit(); #if !SANITIZER_GO - if (!ctx->after_multithreaded_fork) + if (save_stack && !ctx->after_multithreaded_fork) thr->mop_ignore_set.Add(CurrentStackId(thr, pc)); #endif } void ThreadIgnoreEnd(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); + CHECK_GT(thr->ignore_reads_and_writes, 0); thr->ignore_reads_and_writes--; - CHECK_GE(thr->ignore_reads_and_writes, 0); if (thr->ignore_reads_and_writes == 0) { thr->fast_state.ClearIgnoreBit(); #if !SANITIZER_GO @@ -1011,20 +1011,20 @@ uptr __tsan_testonly_shadow_stack_current_size() { } #endif -void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) { +void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc, bool save_stack) { DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); thr->ignore_sync++; CHECK_GT(thr->ignore_sync, 0); #if !SANITIZER_GO - if (!ctx->after_multithreaded_fork) + if (save_stack && !ctx->after_multithreaded_fork) thr->sync_ignore_set.Add(CurrentStackId(thr, pc)); #endif } void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); + CHECK_GT(thr->ignore_sync, 0); thr->ignore_sync--; - CHECK_GE(thr->ignore_sync, 0); #if !SANITIZER_GO if (thr->ignore_sync == 0) thr->sync_ignore_set.Reset(); diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index 88539414c9..0d62af00a0 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -546,6 +546,10 @@ struct Context { extern Context *ctx; // The one and the only global runtime context. +ALWAYS_INLINE Flags *flags() { + return &ctx->flags; +} + struct ScopedIgnoreInterceptors { ScopedIgnoreInterceptors() { #if !SANITIZER_GO @@ -707,9 +711,9 @@ void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); -void ThreadIgnoreBegin(ThreadState *thr, uptr pc); +void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack = true); void ThreadIgnoreEnd(ThreadState *thr, uptr pc); -void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc); +void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc, bool save_stack = true); void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc); void FuncEntry(ThreadState *thr, uptr pc); @@ -731,13 +735,16 @@ void ProcDestroy(Processor *proc); void ProcWire(Processor *proc, ThreadState *thr); void ProcUnwire(Processor *proc, ThreadState *thr); -void MutexCreate(ThreadState *thr, uptr pc, uptr addr, - bool rw, bool recursive, bool linker_init); +// Note: the parameter is called flagz, because flags is already taken +// by the global function that returns flags. +void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexDestroy(ThreadState *thr, uptr pc, uptr addr); -void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1, - bool try_lock = false); -int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); -void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool try_lock = false); +void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); +void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0, + int rec = 1); +int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); +void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); +void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index f3b51c30fa..086b289279 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -62,20 +62,17 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ, OutputReport(thr, rep); } -void MutexCreate(ThreadState *thr, uptr pc, uptr addr, - bool rw, bool recursive, bool linker_init) { - DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr); +void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { + DPrintf("#%d: MutexCreate %zx flagz=0x%x\n", thr->tid, addr, flagz); StatInc(thr, StatMutexCreate); - if (!linker_init && IsAppMem(addr)) { + if (!(flagz & MutexFlagLinkerInit) && IsAppMem(addr)) { CHECK(!thr->is_freeing); thr->is_freeing = true; MemoryWrite(thr, pc, addr, kSizeLog1); thr->is_freeing = false; } SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); - s->is_rw = rw; - s->is_recursive = recursive; - s->is_linker_init = linker_init; + s->SetFlags(flagz & MutexCreationFlagMask); if (!SANITIZER_GO && s->creation_stack_id == 0) s->creation_stack_id = CurrentStackId(thr, pc); s->mtx.Unlock(); @@ -87,7 +84,7 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true); if (s == 0) return; - if (s->is_linker_init) { + if (s->IsFlagSet(MutexFlagLinkerInit)) { // Destroy is no-op for linker-initialized mutexes. s->mtx.Unlock(); return; @@ -100,8 +97,8 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { bool unlock_locked = false; if (flags()->report_destroy_locked && s->owner_tid != SyncVar::kInvalidTid - && !s->is_broken) { - s->is_broken = true; + && !s->IsFlagSet(MutexFlagBroken)) { + s->SetFlags(MutexFlagBroken); unlock_locked = true; } u64 mid = s->GetId(); @@ -141,12 +138,33 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { // s will be destroyed and freed in MetaMap::FreeBlock. } -void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec, bool try_lock) { - DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec); - CHECK_GT(rec, 0); +void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { + DPrintf("#%d: MutexPreLock %zx flagz=0x%x\n", thr->tid, addr, flagz); + if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) { + SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false); + s->UpdateFlags(flagz); + if (s->owner_tid != thr->tid) { + Callback cb(thr, pc); + ctx->dd->MutexBeforeLock(&cb, &s->dd, true); + s->mtx.ReadUnlock(); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } else { + s->mtx.ReadUnlock(); + } + } +} + +void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) { + DPrintf("#%d: MutexPostLock %zx flag=0x%x rec=%d\n", + thr->tid, addr, flagz, rec); + if (flagz & MutexFlagRecursiveLock) + CHECK_GT(rec, 0); + else + rec = 1; if (IsAppMem(addr)) MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); + s->UpdateFlags(flagz); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId()); bool report_double_lock = false; @@ -156,38 +174,43 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec, bool try_lock) { s->last_lock = thr->fast_state.raw(); } else if (s->owner_tid == thr->tid) { CHECK_GT(s->recursion, 0); - } else if (flags()->report_mutex_bugs && !s->is_broken) { - s->is_broken = true; + } else if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { + s->SetFlags(MutexFlagBroken); report_double_lock = true; } - if (s->recursion == 0) { + const bool first = s->recursion == 0; + s->recursion += rec; + if (first) { StatInc(thr, StatMutexLock); AcquireImpl(thr, pc, &s->clock); AcquireImpl(thr, pc, &s->read_clock); - } else if (!s->is_recursive) { + } else if (!s->IsFlagSet(MutexFlagWriteReentrant)) { StatInc(thr, StatMutexRecLock); } - s->recursion += rec; thr->mset.Add(s->GetId(), true, thr->fast_state.epoch()); - if (common_flags()->detect_deadlocks && (s->recursion - rec) == 0) { + bool pre_lock = false; + if (first && common_flags()->detect_deadlocks) { + pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) && + !(flagz & MutexFlagTryLock); Callback cb(thr, pc); - if (!try_lock) + if (pre_lock) ctx->dd->MutexBeforeLock(&cb, &s->dd, true); - ctx->dd->MutexAfterLock(&cb, &s->dd, true, try_lock); + ctx->dd->MutexAfterLock(&cb, &s->dd, true, flagz & MutexFlagTryLock); } u64 mid = s->GetId(); s->mtx.Unlock(); // Can't touch s after this point. + s = 0; if (report_double_lock) ReportMutexMisuse(thr, pc, ReportTypeMutexDoubleLock, addr, mid); - if (common_flags()->detect_deadlocks) { + if (first && pre_lock && common_flags()->detect_deadlocks) { Callback cb(thr, pc); ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); } } -int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { - DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all); +int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { + DPrintf("#%d: MutexUnlock %zx flagz=0x%x\n", thr->tid, addr, flagz); if (IsAppMem(addr)) MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); @@ -196,12 +219,12 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { int rec = 0; bool report_bad_unlock = false; if (!SANITIZER_GO && (s->recursion == 0 || s->owner_tid != thr->tid)) { - if (flags()->report_mutex_bugs && !s->is_broken) { - s->is_broken = true; + if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { + s->SetFlags(MutexFlagBroken); report_bad_unlock = true; } } else { - rec = all ? s->recursion : 1; + rec = (flagz & MutexFlagRecursiveUnlock) ? s->recursion : 1; s->recursion -= rec; if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); @@ -229,36 +252,53 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { return rec; } -void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool trylock) { - DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr); +void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { + DPrintf("#%d: MutexPreReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz); + if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) { + SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false); + s->UpdateFlags(flagz); + Callback cb(thr, pc); + ctx->dd->MutexBeforeLock(&cb, &s->dd, false); + s->mtx.ReadUnlock(); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } +} + +void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { + DPrintf("#%d: MutexPostReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz); StatInc(thr, StatMutexReadLock); if (IsAppMem(addr)) MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false); + s->UpdateFlags(flagz); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); bool report_bad_lock = false; if (s->owner_tid != SyncVar::kInvalidTid) { - if (flags()->report_mutex_bugs && !s->is_broken) { - s->is_broken = true; + if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { + s->SetFlags(MutexFlagBroken); report_bad_lock = true; } } AcquireImpl(thr, pc, &s->clock); s->last_lock = thr->fast_state.raw(); thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); - if (common_flags()->detect_deadlocks && s->recursion == 0) { + bool pre_lock = false; + if (common_flags()->detect_deadlocks) { + pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) && + !(flagz & MutexFlagTryLock); Callback cb(thr, pc); - if (!trylock) + if (pre_lock) ctx->dd->MutexBeforeLock(&cb, &s->dd, false); - ctx->dd->MutexAfterLock(&cb, &s->dd, false, trylock); + ctx->dd->MutexAfterLock(&cb, &s->dd, false, flagz & MutexFlagTryLock); } u64 mid = s->GetId(); s->mtx.ReadUnlock(); // Can't touch s after this point. + s = 0; if (report_bad_lock) ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadLock, addr, mid); - if (common_flags()->detect_deadlocks) { + if (pre_lock && common_flags()->detect_deadlocks) { Callback cb(thr, pc); ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); } @@ -274,8 +314,8 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); bool report_bad_unlock = false; if (s->owner_tid != SyncVar::kInvalidTid) { - if (flags()->report_mutex_bugs && !s->is_broken) { - s->is_broken = true; + if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { + s->SetFlags(MutexFlagBroken); report_bad_unlock = true; } } @@ -323,8 +363,8 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { } else { StatInc(thr, StatMutexRecUnlock); } - } else if (!s->is_broken) { - s->is_broken = true; + } else if (!s->IsFlagSet(MutexFlagBroken)) { + s->SetFlags(MutexFlagBroken); report_bad_unlock = true; } thr->mset.Del(s->GetId(), write); diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc index d1d6ed24d9..2ee688bf57 100644 --- a/lib/tsan/rtl/tsan_stat.cc +++ b/lib/tsan/rtl/tsan_stat.cc @@ -153,6 +153,16 @@ void StatOutput(u64 *stat) { name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange "; name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange "; name[StatAnnotateThreadName] = " ThreadName "; + name[Stat__tsan_mutex_create] = " __tsan_mutex_create "; + name[Stat__tsan_mutex_destroy] = " __tsan_mutex_destroy "; + name[Stat__tsan_mutex_pre_lock] = " __tsan_mutex_pre_lock "; + name[Stat__tsan_mutex_post_lock] = " __tsan_mutex_post_lock "; + name[Stat__tsan_mutex_pre_unlock] = " __tsan_mutex_pre_unlock "; + name[Stat__tsan_mutex_post_unlock] = " __tsan_mutex_post_unlock "; + name[Stat__tsan_mutex_pre_signal] = " __tsan_mutex_pre_signal "; + name[Stat__tsan_mutex_post_signal] = " __tsan_mutex_post_signal "; + name[Stat__tsan_mutex_pre_divert] = " __tsan_mutex_pre_divert "; + name[Stat__tsan_mutex_post_divert] = " __tsan_mutex_post_divert "; name[StatMtxTotal] = "Contentionz "; name[StatMtxTrace] = " Trace "; diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h index 8447dd84fc..7d2791ebbf 100644 --- a/lib/tsan/rtl/tsan_stat.h +++ b/lib/tsan/rtl/tsan_stat.h @@ -157,6 +157,16 @@ enum StatType { StatAnnotatePublishMemoryRange, StatAnnotateUnpublishMemoryRange, StatAnnotateThreadName, + Stat__tsan_mutex_create, + Stat__tsan_mutex_destroy, + Stat__tsan_mutex_pre_lock, + Stat__tsan_mutex_post_lock, + Stat__tsan_mutex_pre_unlock, + Stat__tsan_mutex_post_unlock, + Stat__tsan_mutex_pre_signal, + Stat__tsan_mutex_post_signal, + Stat__tsan_mutex_pre_divert, + Stat__tsan_mutex_post_divert, // Internal mutex contentionz. StatMtxTotal, diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc index 2be047462f..4cc3cb89c3 100644 --- a/lib/tsan/rtl/tsan_sync.cc +++ b/lib/tsan/rtl/tsan_sync.cc @@ -42,10 +42,7 @@ void SyncVar::Reset(Processor *proc) { owner_tid = kInvalidTid; last_lock = 0; recursion = 0; - is_rw = 0; - is_recursive = 0; - is_broken = 0; - is_linker_init = 0; + atomic_store_relaxed(&flags, 0); if (proc == 0) { CHECK_EQ(clock.size(), 0); diff --git a/lib/tsan/rtl/tsan_sync.h b/lib/tsan/rtl/tsan_sync.h index 86e6bbd55b..d24d697621 100644 --- a/lib/tsan/rtl/tsan_sync.h +++ b/lib/tsan/rtl/tsan_sync.h @@ -23,6 +23,29 @@ namespace __tsan { +// These need to match __tsan_mutex_* flags defined in tsan_interface.h. +// See documentation there as well. +enum MutexFlags { + MutexFlagLinkerInit = 1 << 0, // __tsan_mutex_linker_init + MutexFlagWriteReentrant = 1 << 1, // __tsan_mutex_write_reentrant + MutexFlagReadReentrant = 1 << 2, // __tsan_mutex_read_reentrant + MutexFlagReadLock = 1 << 3, // __tsan_mutex_read_lock + MutexFlagTryLock = 1 << 4, // __tsan_mutex_try_lock + MutexFlagTryLockFailed = 1 << 5, // __tsan_mutex_try_lock_failed + MutexFlagRecursiveLock = 1 << 6, // __tsan_mutex_recursive_lock + MutexFlagRecursiveUnlock = 1 << 7, // __tsan_mutex_recursive_unlock + + // The following flags are runtime private. + // Mutex API misuse was detected, so don't report any more. + MutexFlagBroken = 1 << 30, + // We did not intercept pre lock event, so handle it on post lock. + MutexFlagDoPreLockOnPostLock = 1 << 29, + // Must list all mutex creation flags. + MutexCreationFlagMask = MutexFlagLinkerInit | + MutexFlagWriteReentrant | + MutexFlagReadReentrant, +}; + struct SyncVar { SyncVar(); @@ -35,10 +58,7 @@ struct SyncVar { int owner_tid; // Set only by exclusive owners. u64 last_lock; int recursion; - bool is_rw; - bool is_recursive; - bool is_broken; - bool is_linker_init; + atomic_uint32_t flags; u32 next; // in MetaMap DDMutex dd; SyncClock read_clock; // Used for rw mutexes only. @@ -61,6 +81,26 @@ struct SyncVar { *uid = id >> 48; return (uptr)GetLsb(id, 48); } + + bool IsFlagSet(u32 f) const { + return atomic_load_relaxed(&flags); + } + + void SetFlags(u32 f) { + atomic_store_relaxed(&flags, atomic_load_relaxed(&flags) | f); + } + + void UpdateFlags(u32 flagz) { + // Filter out operation flags. + if (!(flagz & MutexCreationFlagMask)) + return; + u32 current = atomic_load_relaxed(&flags); + if (current & MutexCreationFlagMask) + return; + // Note: this can be called from MutexPostReadLock which holds only read + // lock on the SyncVar. + atomic_store_relaxed(&flags, current | (flagz & MutexCreationFlagMask)); + } }; /* MetaMap allows to map arbitrary user pointers onto various descriptors. diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index 6ffffae7bd..4e025a8ddd 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -38,7 +38,7 @@ bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) { const char *TypeCheckKinds[] = { "load of", "store to", "reference binding to", "member access within", "member call on", "constructor call on", "downcast of", "downcast of", - "upcast of", "cast to virtual base of"}; + "upcast of", "cast to virtual base of", "_Nonnull binding to"}; } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, @@ -472,7 +472,8 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort( Die(); } -static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) { +static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts, + bool IsAttr) { SourceLocation Loc = Data->Loc.acquire(); ErrorType ET = ErrorType::InvalidNullReturn; @@ -484,21 +485,35 @@ static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) { Diag(Loc, DL_Error, "null pointer returned from function declared to never " "return null"); if (!Data->AttrLoc.isInvalid()) - Diag(Data->AttrLoc, DL_Note, "returns_nonnull attribute specified here"); + Diag(Data->AttrLoc, DL_Note, "%0 specified here") + << (IsAttr ? "returns_nonnull attribute" + : "_Nonnull return type annotation"); } void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) { GET_REPORT_OPTIONS(false); - handleNonNullReturn(Data, Opts); + handleNonNullReturn(Data, Opts, true); } void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) { GET_REPORT_OPTIONS(true); - handleNonNullReturn(Data, Opts); + handleNonNullReturn(Data, Opts, true); Die(); } -static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) { +void __ubsan::__ubsan_handle_nullability_return(NonNullReturnData *Data) { + GET_REPORT_OPTIONS(false); + handleNonNullReturn(Data, Opts, false); +} + +void __ubsan::__ubsan_handle_nullability_return_abort(NonNullReturnData *Data) { + GET_REPORT_OPTIONS(true); + handleNonNullReturn(Data, Opts, false); + Die(); +} + +static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts, + bool IsAttr) { SourceLocation Loc = Data->Loc.acquire(); ErrorType ET = ErrorType::InvalidNullArgument; @@ -507,20 +522,34 @@ static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) { ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to " - "never be null") << Data->ArgIndex; + Diag(Loc, DL_Error, + "null pointer passed as argument %0, which is declared to " + "never be null") + << Data->ArgIndex; if (!Data->AttrLoc.isInvalid()) - Diag(Data->AttrLoc, DL_Note, "nonnull attribute specified here"); + Diag(Data->AttrLoc, DL_Note, "%0 specified here") + << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation"); } void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) { GET_REPORT_OPTIONS(false); - handleNonNullArg(Data, Opts); + handleNonNullArg(Data, Opts, true); } void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { GET_REPORT_OPTIONS(true); - handleNonNullArg(Data, Opts); + handleNonNullArg(Data, Opts, true); + Die(); +} + +void __ubsan::__ubsan_handle_nullability_arg(NonNullArgData *Data) { + GET_REPORT_OPTIONS(false); + handleNonNullArg(Data, Opts, false); +} + +void __ubsan::__ubsan_handle_nullability_arg_abort(NonNullArgData *Data) { + GET_REPORT_OPTIONS(true); + handleNonNullArg(Data, Opts, false); Die(); } diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h index 350eb91d1b..5857bc2495 100644 --- a/lib/ubsan/ubsan_handlers.h +++ b/lib/ubsan/ubsan_handlers.h @@ -136,8 +136,10 @@ struct NonNullReturnData { SourceLocation AttrLoc; }; -/// \brief Handle returning null from function with returns_nonnull attribute. +/// \brief Handle returning null from function with the returns_nonnull +/// attribute, or a return type annotated with _Nonnull. RECOVERABLE(nonnull_return, NonNullReturnData *Data) +RECOVERABLE(nullability_return, NonNullReturnData *Data) struct NonNullArgData { SourceLocation Loc; @@ -145,8 +147,10 @@ struct NonNullArgData { int ArgIndex; }; -/// \brief Handle passing null pointer to function with nonnull attribute. +/// \brief Handle passing null pointer to a function parameter with the nonnull +/// attribute, or a _Nonnull type annotation. RECOVERABLE(nonnull_arg, NonNullArgData *Data) +RECOVERABLE(nullability_arg, NonNullArgData *Data) /// \brief Known CFI check kinds. /// Keep in sync with the enum of the same name in CodeGenFunction.h diff --git a/lib/ubsan/ubsan_interface.inc b/lib/ubsan/ubsan_interface.inc index 75f080d156..0e43ebc682 100644 --- a/lib/ubsan/ubsan_interface.inc +++ b/lib/ubsan/ubsan_interface.inc @@ -30,6 +30,10 @@ INTERFACE_FUNCTION(__ubsan_handle_nonnull_arg) INTERFACE_FUNCTION(__ubsan_handle_nonnull_arg_abort) INTERFACE_FUNCTION(__ubsan_handle_nonnull_return) INTERFACE_FUNCTION(__ubsan_handle_nonnull_return_abort) +INTERFACE_FUNCTION(__ubsan_handle_nullability_arg) +INTERFACE_FUNCTION(__ubsan_handle_nullability_arg_abort) +INTERFACE_FUNCTION(__ubsan_handle_nullability_return) +INTERFACE_FUNCTION(__ubsan_handle_nullability_return_abort) INTERFACE_FUNCTION(__ubsan_handle_out_of_bounds) INTERFACE_FUNCTION(__ubsan_handle_out_of_bounds_abort) INTERFACE_FUNCTION(__ubsan_handle_shift_out_of_bounds) diff --git a/lib/xray/tests/CMakeLists.txt b/lib/xray/tests/CMakeLists.txt index 43878cb15e..a1eb4a030c 100644 --- a/lib/xray/tests/CMakeLists.txt +++ b/lib/xray/tests/CMakeLists.txt @@ -49,7 +49,7 @@ macro(add_xray_unittest testname) -lstdc++ -lm ${CMAKE_THREAD_LIBS_INIT} -lpthread -L${COMPILER_RT_LIBRARY_OUTPUT_DIR} -lclang_rt.xray-${arch} - -latomic -ldl -lrt) + -ldl -lrt) endif() # FIXME: Figure out how to run even just the unit tests on APPLE. endforeach() diff --git a/lib/xray/tests/unit/CMakeLists.txt b/lib/xray/tests/unit/CMakeLists.txt index 62d01f2395..4ed9195189 100644 --- a/lib/xray/tests/unit/CMakeLists.txt +++ b/lib/xray/tests/unit/CMakeLists.txt @@ -2,3 +2,5 @@ add_xray_unittest(XRayBufferQueueTest SOURCES buffer_queue_test.cc xray_unit_test_main.cc) add_xray_unittest(XRayFDRLoggingTest SOURCES fdr_logging_test.cc xray_unit_test_main.cc) + +add_executable(xray_fdr_log_printer xray_fdr_log_printer_tool.cc) diff --git a/lib/xray/tests/unit/buffer_queue_test.cc b/lib/xray/tests/unit/buffer_queue_test.cc index 4db762dc72..ac89a8dbc5 100644 --- a/lib/xray/tests/unit/buffer_queue_test.cc +++ b/lib/xray/tests/unit/buffer_queue_test.cc @@ -14,7 +14,6 @@ #include "gtest/gtest.h" #include -#include #include namespace __xray { @@ -32,9 +31,9 @@ TEST(BufferQueueTest, GetAndRelease) { BufferQueue Buffers(kSize, 1, Success); ASSERT_TRUE(Success); BufferQueue::Buffer Buf; - ASSERT_EQ(Buffers.getBuffer(Buf), std::error_code()); + ASSERT_EQ(Buffers.getBuffer(Buf), BufferQueue::ErrorCode::Ok); ASSERT_NE(nullptr, Buf.Buffer); - ASSERT_EQ(Buffers.releaseBuffer(Buf), std::error_code()); + ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok); ASSERT_EQ(nullptr, Buf.Buffer); } @@ -43,11 +42,10 @@ TEST(BufferQueueTest, GetUntilFailed) { BufferQueue Buffers(kSize, 1, Success); ASSERT_TRUE(Success); BufferQueue::Buffer Buf0; - EXPECT_EQ(Buffers.getBuffer(Buf0), std::error_code()); + EXPECT_EQ(Buffers.getBuffer(Buf0), BufferQueue::ErrorCode::Ok); BufferQueue::Buffer Buf1; - EXPECT_EQ(std::make_error_code(std::errc::not_enough_memory), - Buffers.getBuffer(Buf1)); - EXPECT_EQ(Buffers.releaseBuffer(Buf0), std::error_code()); + EXPECT_EQ(BufferQueue::ErrorCode::NotEnoughMemory, Buffers.getBuffer(Buf1)); + EXPECT_EQ(Buffers.releaseBuffer(Buf0), BufferQueue::ErrorCode::Ok); } TEST(BufferQueueTest, ReleaseUnknown) { @@ -57,7 +55,7 @@ TEST(BufferQueueTest, ReleaseUnknown) { BufferQueue::Buffer Buf; Buf.Buffer = reinterpret_cast(0xdeadbeef); Buf.Size = kSize; - EXPECT_EQ(std::make_error_code(std::errc::argument_out_of_domain), + EXPECT_EQ(BufferQueue::ErrorCode::UnrecognizedBuffer, Buffers.releaseBuffer(Buf)); } @@ -66,15 +64,15 @@ TEST(BufferQueueTest, ErrorsWhenFinalising) { BufferQueue Buffers(kSize, 2, Success); ASSERT_TRUE(Success); BufferQueue::Buffer Buf; - ASSERT_EQ(Buffers.getBuffer(Buf), std::error_code()); + ASSERT_EQ(Buffers.getBuffer(Buf), BufferQueue::ErrorCode::Ok); ASSERT_NE(nullptr, Buf.Buffer); - ASSERT_EQ(Buffers.finalize(), std::error_code()); + ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok); BufferQueue::Buffer OtherBuf; - ASSERT_EQ(std::make_error_code(std::errc::state_not_recoverable), + ASSERT_EQ(BufferQueue::ErrorCode::AlreadyFinalized, Buffers.getBuffer(OtherBuf)); - ASSERT_EQ(std::make_error_code(std::errc::state_not_recoverable), + ASSERT_EQ(BufferQueue::ErrorCode::AlreadyFinalized, Buffers.finalize()); - ASSERT_EQ(Buffers.releaseBuffer(Buf), std::error_code()); + ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok); } TEST(BufferQueueTest, MultiThreaded) { @@ -83,14 +81,17 @@ TEST(BufferQueueTest, MultiThreaded) { ASSERT_TRUE(Success); auto F = [&] { BufferQueue::Buffer B; - while (!Buffers.getBuffer(B)) { + while (true) { + auto EC = Buffers.getBuffer(B); + if (EC != BufferQueue::ErrorCode::Ok) + return; Buffers.releaseBuffer(B); } }; auto T0 = std::async(std::launch::async, F); auto T1 = std::async(std::launch::async, F); auto T2 = std::async(std::launch::async, [&] { - while (!Buffers.finalize()) + while (Buffers.finalize() != BufferQueue::ErrorCode::Ok) ; }); F(); @@ -103,8 +104,8 @@ TEST(BufferQueueTest, Apply) { auto Count = 0; BufferQueue::Buffer B; for (int I = 0; I < 10; ++I) { - ASSERT_FALSE(Buffers.getBuffer(B)); - ASSERT_FALSE(Buffers.releaseBuffer(B)); + ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); + ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); } Buffers.apply([&](const BufferQueue::Buffer &B) { ++Count; }); ASSERT_EQ(Count, 10); diff --git a/lib/xray/tests/unit/xray_fdr_log_printer_tool.cc b/lib/xray/tests/unit/xray_fdr_log_printer_tool.cc new file mode 100644 index 0000000000..6e209809e3 --- /dev/null +++ b/lib/xray/tests/unit/xray_fdr_log_printer_tool.cc @@ -0,0 +1,330 @@ +//===-- xray_fdr_log_printer_tool.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 XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// + +#include "xray_fdr_logging.h" +#include "xray_fdr_logging_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xray/xray_records.h" + +// Writes out xray fdr mode log records to stdout based on a sequence of +// formatted data read from stdin. +// +// Interprets an adhoc format of Top Level Types and parameter maps of the form: +// +// RecordType : { Parameter1 = Value, Parameter2 = value , Parameter3 = value} +// OtherRecordType : { ParameterOne = Value } +// +// Each line corresponds to a type of record written by the Xray Flight Data +// Recorder mode to a buffer. This program synthesizes records in the FDR binary +// format and writes them to std::cout. + +namespace { + +/// A crude lexer to read tokens and skip over whitespace. +class TokenReader { +public: + TokenReader() : LastDelimPresent(false), FoundEof(false), LastDelim(0) {} + std::string readToken(std::istream &Stream); + bool hasLastDelim() const { return LastDelimPresent; } + char getLastDelim() const { return LastDelim; } + void setLastDelim(char Delim) { + LastDelimPresent = true; + LastDelim = Delim; + } + void clearLastDelim() { + LastDelimPresent = false; + LastDelim = 0; + } + bool isEof() { return FoundEof; } + void setFoundEof(bool eof) { FoundEof = eof; } + +private: + bool LastDelimPresent; + bool FoundEof; + char LastDelim; +}; + +// Globally tracks whether we reached EOF and caches delimiters that qualify as +// tokens. +static TokenReader TokenReader{}; + +bool isWhitespace(char c) { + // Hardcode the whitespace characters we will not return as tokens even though + // they are token delimiters. + static const std::vector Whitespace{' ', '\n', '\t'}; + return std::find(Whitespace.begin(), Whitespace.end(), c) != Whitespace.end(); +} + +bool isDelimiter(char c) { + // Hardcode a set of token delimiters. + static const std::vector Delimiters{' ', ':', ',', '\n', + '\t', '{', '}', '='}; + return std::find(Delimiters.begin(), Delimiters.end(), c) != Delimiters.end(); +} + +std::string TokenReader::readToken(std::istream &Stream) { + // If on the last call we read a trailing delimiter that also qualifies as a + // token, return it now. + if (hasLastDelim()) { + char Token = getLastDelim(); + clearLastDelim(); + return std::string{Token}; + } + + std::stringstream Builder{}; + char c; + c = Stream.get(); + while (!isDelimiter(c) && !Stream.eof()) { + Builder << c; + c = Stream.get(); + } + + setFoundEof(Stream.eof()); + + std::string Token = Builder.str(); + + if (Token.empty()) { + // We read a whitespace delimiter only. Skip over it. + if (!isEof() && isWhitespace(c)) { + return readToken(Stream); + } else if (isWhitespace(c)) { + // We only read a whitespace delimiter. + return ""; + } else { + // We read a delimiter that matters as a token. + return std::string{c}; + } + } + + // If we found a delimiter that's a valid token. Store it to return as the + // next token. + if (!isWhitespace(c)) + setLastDelim(c); + + return Token; +} + +// Reads an expected token or dies a gruesome death. +void eatExpectedToken(std::istream &Stream, const std::string &Expected) { + std::string Token = TokenReader.readToken(Stream); + if (Token.compare(Expected) != 0) { + std::cerr << "Expecting token '" << Expected << "'. Found '" << Token + << "'.\n"; + std::exit(1); + } +} + +// Constructs a map of key value pairs from a token stream. +// Expects to read an expression of the form: +// +// { a = b, c = d, e = f} +// +// If not, the driver will crash. +std::map readMap(std::istream &Stream) { + using StrMap = std::map; + using StrVector = std::vector; + + eatExpectedToken(Stream, "{"); + StrVector TokenList{}; + + while (!TokenReader.isEof()) { + std::string CurrentToken = TokenReader.readToken(Stream); + if (CurrentToken.compare("}") == 0) { + break; + } + TokenList.push_back(CurrentToken); + if (TokenReader.isEof()) { + std::cerr << "Got EOF while building a param map.\n"; + std::exit(1); + } + } + + if (TokenList.size() == 0) { + StrMap EmptyMap{}; + return EmptyMap; + } + if (TokenList.size() % 4 != 3) { + std::cerr << "Error while building token map. Expected triples of tokens " + "in the form 'a = b' separated by commas.\n"; + std::exit(1); + } + + StrMap TokenMap{}; + std::size_t ElementIndex = 0; + for (; ElementIndex < TokenList.size(); ElementIndex += 4) { + if (TokenList[ElementIndex + 1].compare("=") != 0) { + std::cerr << "Expected an assignment when building a param map.\n"; + std::exit(1); + } + TokenMap[TokenList[ElementIndex]] = TokenList[ElementIndex + 2]; + if (ElementIndex + 3 < TokenList.size()) { + if (TokenList[ElementIndex + 3].compare(",") != 0) { + std::cerr << "Expected assignment statements to be separated by commas." + << "\n"; + std::exit(1); + } + } + } + return TokenMap; +} + +std::string getOrDie(const std::map &Lookup, + const std::string &Key) { + auto MapIter = Lookup.find(Key); + if (MapIter == Lookup.end()) { + std::cerr << "Expected key '" << Key << "'. Was not found.\n"; + std::exit(1); + } + return MapIter->second; +} + +// Reads a numeric type from a string token through the magic of +// std::stringstream. +template struct NumberParser { + static NT parse(const std::string &Input) { + NT Number = 0; + std::stringstream Stream(Input); + Stream >> Number; + return Number; + } +}; + +void writeNewBufferOrDie(std::istream &Input) { + auto TokenMap = readMap(Input); + pid_t Tid = NumberParser::parse(getOrDie(TokenMap, "Tid")); + time_t Time = NumberParser::parse(getOrDie(TokenMap, "time")); + timespec TimeSpec = {Time, 0}; + constexpr const size_t OutputSize = 32; + std::array Buffer{}; + char *MemPtr = Buffer.data(); + __xray::__xray_fdr_internal::writeNewBufferPreamble(Tid, TimeSpec, MemPtr); + std::cout.write(Buffer.data(), OutputSize); +} + +void writeNewCPUIdOrDie(std::istream &Input) { + auto TokenMap = readMap(Input); + uint16_t CPU = NumberParser::parse(getOrDie(TokenMap, "CPU")); + uint64_t TSC = NumberParser::parse(getOrDie(TokenMap, "TSC")); + constexpr const size_t OutputSize = 16; + std::array Buffer{}; + char *MemPtr = Buffer.data(); + __xray::__xray_fdr_internal::writeNewCPUIdMetadata(CPU, TSC, MemPtr); + std::cout.write(Buffer.data(), OutputSize); +} + +void writeEOBOrDie(std::istream &Input) { + auto TokenMap = readMap(Input); + constexpr const size_t OutputSize = 16; + std::array Buffer{}; + char *MemPtr = Buffer.data(); + __xray::__xray_fdr_internal::writeEOBMetadata(MemPtr); + std::cout.write(Buffer.data(), OutputSize); +} + +void writeTSCWrapOrDie(std::istream &Input) { + auto TokenMap = readMap(Input); + uint64_t TSC = NumberParser::parse(getOrDie(TokenMap, "TSC")); + constexpr const size_t OutputSize = 16; + std::array Buffer{}; + char *MemPtr = Buffer.data(); + __xray::__xray_fdr_internal::writeTSCWrapMetadata(TSC, MemPtr); + std::cout.write(Buffer.data(), OutputSize); +} + +XRayEntryType decodeEntryType(const std::string &EntryTypeStr) { + if (EntryTypeStr.compare("Entry") == 0) { + return XRayEntryType::ENTRY; + } else if (EntryTypeStr.compare("LogArgsEntry") == 0) { + return XRayEntryType::LOG_ARGS_ENTRY; + } else if (EntryTypeStr.compare("Exit") == 0) { + return XRayEntryType::EXIT; + } else if (EntryTypeStr.compare("Tail") == 0) { + return XRayEntryType::TAIL; + } + std::cerr << "Illegal entry type " << EntryTypeStr << ".\n"; + std::exit(1); +} + +void writeFunctionOrDie(std::istream &Input) { + auto TokenMap = readMap(std::cin); + int FuncId = NumberParser::parse(getOrDie(TokenMap, "FuncId")); + uint32_t TSCDelta = + NumberParser::parse(getOrDie(TokenMap, "TSCDelta")); + std::string EntryType = getOrDie(TokenMap, "EntryType"); + XRayEntryType XrayEntryType = decodeEntryType(EntryType); + constexpr const size_t OutputSize = 8; + std::array Buffer{}; + char *MemPtr = Buffer.data(); + __xray::__xray_fdr_internal::writeFunctionRecord(FuncId, TSCDelta, + XrayEntryType, MemPtr); + std::cout.write(Buffer.data(), OutputSize); +} + +} // namespace + +int main(int argc, char **argv) { + std::map> TopLevelRecordMap; + TopLevelRecordMap["NewBuffer"] = writeNewBufferOrDie; + TopLevelRecordMap["NewCPU"] = writeNewCPUIdOrDie; + TopLevelRecordMap["EOB"] = writeEOBOrDie; + TopLevelRecordMap["TSCWrap"] = writeTSCWrapOrDie; + TopLevelRecordMap["Function"] = writeFunctionOrDie; + + // Write file header + // + // (2) uint16 : version + // (2) uint16 : type + // (4) uint32 : bitfield + // (8) uint64 : cycle frequency + // (16) - : padding + uint16_t HeaderVersion = 1; + uint16_t HeaderType = 1; + uint32_t Bitfield = 3; + uint64_t CycleFreq = 42; + constexpr const size_t HeaderSize = 32; + std::array Header{}; + std::memcpy(Header.data(), &HeaderVersion, sizeof(HeaderVersion)); + std::memcpy(Header.data() + 2, &HeaderType, sizeof(HeaderType)); + std::memcpy(Header.data() + 4, &Bitfield, sizeof(Bitfield)); + std::memcpy(Header.data() + 8, &CycleFreq, sizeof(CycleFreq)); + std::cout.write(Header.data(), HeaderSize); + + std::string CurrentToken; + while (true) { + CurrentToken = TokenReader.readToken(std::cin); + if (TokenReader.isEof()) + break; + auto MapIter = TopLevelRecordMap.find(CurrentToken); + if (MapIter != TopLevelRecordMap.end()) { + eatExpectedToken(std::cin, ":"); + if (TokenReader.isEof()) { + std::cerr << "Got eof when expecting to read a map.\n"; + std::exit(1); + } + MapIter->second(std::cin); + } else { + std::cerr << "Got bad top level instruction '" << CurrentToken << "'.\n"; + std::exit(1); + } + } + return 0; +} diff --git a/lib/xray/xray_buffer_queue.cc b/lib/xray/xray_buffer_queue.cc index bd8f4961e7..7ba755ac30 100644 --- a/lib/xray/xray_buffer_queue.cc +++ b/lib/xray/xray_buffer_queue.cc @@ -13,13 +13,17 @@ // //===----------------------------------------------------------------------===// #include "xray_buffer_queue.h" -#include +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" + #include +#include using namespace __xray; +using namespace __sanitizer; BufferQueue::BufferQueue(std::size_t B, std::size_t N, bool &Success) - : BufferSize(B), Buffers(N), Mutex(), OwnedBuffers(), Finalizing(false) { + : BufferSize(B), Buffers(N), Mutex(), OwnedBuffers(), Finalizing{0} { for (auto &T : Buffers) { void *Tmp = malloc(BufferSize); if (Tmp == nullptr) { @@ -35,37 +39,38 @@ BufferQueue::BufferQueue(std::size_t B, std::size_t N, bool &Success) Success = true; } -std::error_code BufferQueue::getBuffer(Buffer &Buf) { - if (Finalizing.load(std::memory_order_acquire)) - return std::make_error_code(std::errc::state_not_recoverable); - std::lock_guard Guard(Mutex); +BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) { + if (__sanitizer::atomic_load(&Finalizing, __sanitizer::memory_order_acquire)) + return ErrorCode::QueueFinalizing; + __sanitizer::BlockingMutexLock Guard(&Mutex); if (Buffers.empty()) - return std::make_error_code(std::errc::not_enough_memory); + return ErrorCode::NotEnoughMemory; auto &T = Buffers.front(); auto &B = std::get<0>(T); Buf = B; B.Buffer = nullptr; B.Size = 0; Buffers.pop_front(); - return {}; + return ErrorCode::Ok; } -std::error_code BufferQueue::releaseBuffer(Buffer &Buf) { +BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) { if (OwnedBuffers.count(Buf.Buffer) == 0) - return std::make_error_code(std::errc::argument_out_of_domain); - std::lock_guard Guard(Mutex); + return ErrorCode::UnrecognizedBuffer; + __sanitizer::BlockingMutexLock Guard(&Mutex); // Now that the buffer has been released, we mark it as "used". Buffers.emplace(Buffers.end(), Buf, true /* used */); Buf.Buffer = nullptr; Buf.Size = 0; - return {}; + return ErrorCode::Ok; } -std::error_code BufferQueue::finalize() { - if (Finalizing.exchange(true, std::memory_order_acq_rel)) - return std::make_error_code(std::errc::state_not_recoverable); - return {}; +BufferQueue::ErrorCode BufferQueue::finalize() { + if (__sanitizer::atomic_exchange(&Finalizing, 1, + __sanitizer::memory_order_acq_rel)) + return ErrorCode::QueueFinalizing; + return ErrorCode::Ok; } BufferQueue::~BufferQueue() { diff --git a/lib/xray/xray_buffer_queue.h b/lib/xray/xray_buffer_queue.h index 3898437e4b..e051695a29 100644 --- a/lib/xray/xray_buffer_queue.h +++ b/lib/xray/xray_buffer_queue.h @@ -15,11 +15,9 @@ #ifndef XRAY_BUFFER_QUEUE_H #define XRAY_BUFFER_QUEUE_H -#include -#include +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_mutex.h" #include -#include -#include #include #include @@ -34,23 +32,47 @@ class BufferQueue { public: struct Buffer { void *Buffer = nullptr; - std::size_t Size = 0; + size_t Size = 0; }; private: - std::size_t BufferSize; + size_t BufferSize; // We use a bool to indicate whether the Buffer has been used in this // freelist implementation. std::deque> Buffers; - std::mutex Mutex; + __sanitizer::BlockingMutex Mutex; std::unordered_set OwnedBuffers; - std::atomic Finalizing; + __sanitizer::atomic_uint8_t Finalizing; public: + enum class ErrorCode : unsigned { + Ok, + NotEnoughMemory, + QueueFinalizing, + UnrecognizedBuffer, + AlreadyFinalized, + }; + + static const char *getErrorString(ErrorCode E) { + switch (E) { + case ErrorCode::Ok: + return "(none)"; + case ErrorCode::NotEnoughMemory: + return "no available buffers in the queue"; + case ErrorCode::QueueFinalizing: + return "queue already finalizing"; + case ErrorCode::UnrecognizedBuffer: + return "buffer being returned not owned by buffer queue"; + case ErrorCode::AlreadyFinalized: + return "queue already finalized"; + } + return "unknown error"; + } + /// Initialise a queue of size |N| with buffers of size |B|. We report success /// through |Success|. - BufferQueue(std::size_t B, std::size_t N, bool &Success); + BufferQueue(size_t B, size_t N, bool &Success); /// Updates |Buf| to contain the pointer to an appropriate buffer. Returns an /// error in case there are no available buffers to return when we will run @@ -63,15 +85,21 @@ class BufferQueue { /// - std::errc::not_enough_memory on exceeding MaxSize. /// - no error when we find a Buffer. /// - std::errc::state_not_recoverable on finalising BufferQueue. - std::error_code getBuffer(Buffer &Buf); + ErrorCode getBuffer(Buffer &Buf); /// Updates |Buf| to point to nullptr, with size 0. /// /// Returns: /// - ... - std::error_code releaseBuffer(Buffer &Buf); + ErrorCode releaseBuffer(Buffer &Buf); + + bool finalizing() const { + return __sanitizer::atomic_load(&Finalizing, + __sanitizer::memory_order_acquire); + } - bool finalizing() const { return Finalizing.load(std::memory_order_acquire); } + /// Returns the configured size of the buffers in the buffer queue. + size_t ConfiguredBufferSize() const { return BufferSize; } /// Sets the state of the BufferQueue to finalizing, which ensures that: /// @@ -80,13 +108,13 @@ class BufferQueue { /// /// After a call to finalize succeeds, all subsequent calls to finalize will /// fail with std::errc::state_not_recoverable. - std::error_code finalize(); + ErrorCode finalize(); /// Applies the provided function F to each Buffer in the queue, only if the /// Buffer is marked 'used' (i.e. has been the result of getBuffer(...) and a /// releaseBuffer(...) operation. template void apply(F Fn) { - std::lock_guard G(Mutex); + __sanitizer::BlockingMutexLock G(&Mutex); for (const auto &T : Buffers) { if (std::get<1>(T)) Fn(std::get<0>(T)); diff --git a/lib/xray/xray_fdr_log_records.h b/lib/xray/xray_fdr_log_records.h new file mode 100644 index 0000000000..36d9410d16 --- /dev/null +++ b/lib/xray/xray_fdr_log_records.h @@ -0,0 +1,65 @@ +//===-- xray_fdr_log_records.h -------------------------------------------===// +// +// 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 XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#ifndef XRAY_XRAY_FDR_LOG_RECORDS_H +#define XRAY_XRAY_FDR_LOG_RECORDS_H + +enum class RecordType : uint8_t { Function, Metadata }; + +// A MetadataRecord encodes the kind of record in its first byte, and have 15 +// additional bytes in the end to hold free-form data. +struct alignas(16) MetadataRecord { + // A MetadataRecord must always have a type of 1. + /* RecordType */ uint8_t Type : 1; + + // Each kind of record is represented as a 7-bit value (even though we use an + // unsigned 8-bit enum class to do so). + enum class RecordKinds : uint8_t { + NewBuffer, + EndOfBuffer, + NewCPUId, + TSCWrap, + WalltimeMarker, + }; + // Use 7 bits to identify this record type. + /* RecordKinds */ uint8_t RecordKind : 7; + char Data[15]; +} __attribute__((packed)); + +static_assert(sizeof(MetadataRecord) == 16, "Wrong size for MetadataRecord."); + +struct alignas(8) FunctionRecord { + // A FunctionRecord must always have a type of 0. + /* RecordType */ uint8_t Type : 1; + enum class RecordKinds { + FunctionEnter = 0x00, + FunctionExit = 0x01, + FunctionTailExit = 0x02, + }; + /* RecordKinds */ uint8_t RecordKind : 3; + + // We only use 28 bits of the function ID, so that we can use as few bytes as + // possible. This means we only support 2^28 (268,435,456) unique function ids + // in a single binary. + int FuncId : 28; + + // We use another 4 bytes to hold the delta between the previous entry's TSC. + // In case we've found that the distance is greater than the allowable 32 bits + // (either because we are running in a different CPU and the TSC might be + // different then), we should use a MetadataRecord before this FunctionRecord + // that will contain the full TSC for that CPU, and keep this to 0. + uint32_t TSCDelta; +} __attribute__((packed)); + +static_assert(sizeof(FunctionRecord) == 8, "Wrong size for FunctionRecord."); + +#endif // XRAY_XRAY_FDR_LOG_RECORDS_H diff --git a/lib/xray/xray_fdr_logging.cc b/lib/xray/xray_fdr_logging.cc index bae7d4c4d0..ba1a6ab2c5 100644 --- a/lib/xray/xray_fdr_logging.cc +++ b/lib/xray/xray_fdr_logging.cc @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file is a part of XRay, a dynamic runtime instruementation system. +// This file is a part of XRay, a dynamic runtime instrumentation system. // // Here we implement the Flight Data Recorder mode for XRay, where we use // compact structures to store records in memory as well as when writing out the @@ -17,20 +17,20 @@ #include "xray_fdr_logging.h" #include #include -#include #include -#include #include #include #include #include #include +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "xray/xray_interface.h" #include "xray/xray_records.h" #include "xray_buffer_queue.h" #include "xray_defs.h" +#include "xray_fdr_logging_impl.h" #include "xray_flags.h" #include "xray_tsc.h" #include "xray_utils.h" @@ -40,29 +40,29 @@ namespace __xray { // Global BufferQueue. std::shared_ptr BQ; -std::atomic LoggingStatus{ +__sanitizer::atomic_sint32_t LoggingStatus = { XRayLogInitStatus::XRAY_LOG_UNINITIALIZED}; -std::atomic LogFlushStatus{ +__sanitizer::atomic_sint32_t LogFlushStatus = { XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING}; std::unique_ptr FDROptions; XRayLogInitStatus fdrLoggingInit(std::size_t BufferSize, std::size_t BufferMax, - void *Options, - size_t OptionsSize) XRAY_NEVER_INSTRUMENT { - assert(OptionsSize == sizeof(FDRLoggingOptions)); - XRayLogInitStatus CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; - if (!LoggingStatus.compare_exchange_strong( - CurrentStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZING, - std::memory_order_release, std::memory_order_relaxed)) - return CurrentStatus; + void *Options, + size_t OptionsSize) XRAY_NEVER_INSTRUMENT { + if (OptionsSize != sizeof(FDRLoggingOptions)) + return static_cast(__sanitizer::atomic_load( + &LoggingStatus, __sanitizer::memory_order_acquire)); + s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; + if (!__sanitizer::atomic_compare_exchange_strong( + &LoggingStatus, &CurrentStatus, + XRayLogInitStatus::XRAY_LOG_INITIALIZING, + __sanitizer::memory_order_release)) + return static_cast(CurrentStatus); FDROptions.reset(new FDRLoggingOptions()); - *FDROptions = *reinterpret_cast(Options); - if (FDROptions->ReportErrors) - SetPrintfAndReportCallback(printToStdErr); - + memcpy(FDROptions.get(), Options, OptionsSize); bool Success = false; BQ = std::make_shared(BufferSize, BufferMax, Success); if (!Success) { @@ -73,22 +73,25 @@ XRayLogInitStatus fdrLoggingInit(std::size_t BufferSize, std::size_t BufferMax, // Install the actual handleArg0 handler after initialising the buffers. __xray_set_handler(fdrLoggingHandleArg0); - LoggingStatus.store(XRayLogInitStatus::XRAY_LOG_INITIALIZED, - std::memory_order_release); + __sanitizer::atomic_store(&LoggingStatus, + XRayLogInitStatus::XRAY_LOG_INITIALIZED, + __sanitizer::memory_order_release); + Report("XRay FDR init successful.\n"); return XRayLogInitStatus::XRAY_LOG_INITIALIZED; } // Must finalize before flushing. XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT { - if (LoggingStatus.load(std::memory_order_acquire) != + if (__sanitizer::atomic_load(&LoggingStatus, + __sanitizer::memory_order_acquire) != XRayLogInitStatus::XRAY_LOG_FINALIZED) return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; - XRayLogFlushStatus Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; - if (!LogFlushStatus.compare_exchange_strong( - Result, XRayLogFlushStatus::XRAY_LOG_FLUSHING, - std::memory_order_release, std::memory_order_relaxed)) - return Result; + s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; + if (!__sanitizer::atomic_compare_exchange_strong( + &LogFlushStatus, &Result, XRayLogFlushStatus::XRAY_LOG_FLUSHING, + __sanitizer::memory_order_release)) + return static_cast(Result); // Make a copy of the BufferQueue pointer to prevent other threads that may be // resetting it from blowing away the queue prematurely while we're dealing @@ -109,7 +112,8 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT { Fd = getLogFD(); if (Fd == -1) { auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; - LogFlushStatus.store(Result, std::memory_order_release); + __sanitizer::atomic_store(&LogFlushStatus, Result, + __sanitizer::memory_order_release); return Result; } @@ -121,50 +125,58 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT { // before setting the values in the header. Header.ConstantTSC = 1; Header.NonstopTSC = 1; - clock_gettime(CLOCK_REALTIME, &Header.TS); + Header.FdrData = FdrAdditionalHeaderData{LocalBQ->ConfiguredBufferSize()}; retryingWriteAll(Fd, reinterpret_cast(&Header), reinterpret_cast(&Header) + sizeof(Header)); + LocalBQ->apply([&](const BufferQueue::Buffer &B) { - retryingWriteAll(Fd, reinterpret_cast(B.Buffer), - reinterpret_cast(B.Buffer) + B.Size); + uint64_t BufferSize = B.Size; + if (BufferSize > 0) { + retryingWriteAll(Fd, reinterpret_cast(B.Buffer), + reinterpret_cast(B.Buffer) + B.Size); + } }); - LogFlushStatus.store(XRayLogFlushStatus::XRAY_LOG_FLUSHED, - std::memory_order_release); + __sanitizer::atomic_store(&LogFlushStatus, + XRayLogFlushStatus::XRAY_LOG_FLUSHED, + __sanitizer::memory_order_release); return XRayLogFlushStatus::XRAY_LOG_FLUSHED; } XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT { - XRayLogInitStatus CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED; - if (!LoggingStatus.compare_exchange_strong( - CurrentStatus, XRayLogInitStatus::XRAY_LOG_FINALIZING, - std::memory_order_release, std::memory_order_relaxed)) - return CurrentStatus; + s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED; + if (!__sanitizer::atomic_compare_exchange_strong( + &LoggingStatus, &CurrentStatus, + XRayLogInitStatus::XRAY_LOG_FINALIZING, + __sanitizer::memory_order_release)) + return static_cast(CurrentStatus); // Do special things to make the log finalize itself, and not allow any more // operations to be performed until re-initialized. BQ->finalize(); - LoggingStatus.store(XRayLogInitStatus::XRAY_LOG_FINALIZED, - std::memory_order_release); + __sanitizer::atomic_store(&LoggingStatus, + XRayLogInitStatus::XRAY_LOG_FINALIZED, + __sanitizer::memory_order_release); return XRayLogInitStatus::XRAY_LOG_FINALIZED; } XRayLogInitStatus fdrLoggingReset() XRAY_NEVER_INSTRUMENT { - XRayLogInitStatus CurrentStatus = XRayLogInitStatus::XRAY_LOG_FINALIZED; - if (!LoggingStatus.compare_exchange_strong( - CurrentStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, - std::memory_order_release, std::memory_order_relaxed)) - return CurrentStatus; + s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_FINALIZED; + if (__sanitizer::atomic_compare_exchange_strong( + &LoggingStatus, &CurrentStatus, + XRayLogInitStatus::XRAY_LOG_INITIALIZED, + __sanitizer::memory_order_release)) + return static_cast(CurrentStatus); // Release the in-memory buffer queue. BQ.reset(); // Spin until the flushing status is flushed. - XRayLogFlushStatus CurrentFlushingStatus = - XRayLogFlushStatus::XRAY_LOG_FLUSHED; - while (!LogFlushStatus.compare_exchange_weak( - CurrentFlushingStatus, XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING, - std::memory_order_release, std::memory_order_relaxed)) { + s32 CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED; + while (__sanitizer::atomic_compare_exchange_weak( + &LogFlushStatus, &CurrentFlushingStatus, + XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING, + __sanitizer::memory_order_release)) { if (CurrentFlushingStatus == XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING) break; CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED; @@ -174,352 +186,16 @@ XRayLogInitStatus fdrLoggingReset() XRAY_NEVER_INSTRUMENT { return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; } -namespace { -thread_local BufferQueue::Buffer Buffer; -thread_local char *RecordPtr = nullptr; - -void setupNewBuffer(const BufferQueue::Buffer &Buffer) XRAY_NEVER_INSTRUMENT { - RecordPtr = static_cast(Buffer.Buffer); - - static constexpr int InitRecordsCount = 2; - std::aligned_storage::type Records[InitRecordsCount]; - { - // Write out a MetadataRecord to signify that this is the start of a new - // buffer, associated with a particular thread, with a new CPU. For the - // data, we have 15 bytes to squeeze as much information as we can. At this - // point we only write down the following bytes: - // - Thread ID (pid_t, 4 bytes) - auto &NewBuffer = *reinterpret_cast(&Records[0]); - NewBuffer.Type = uint8_t(RecordType::Metadata); - NewBuffer.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewBuffer); - pid_t Tid = syscall(SYS_gettid); - std::memcpy(&NewBuffer.Data, &Tid, sizeof(pid_t)); - } - - // Also write the WalltimeMarker record. - { - static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes"); - auto &WalltimeMarker = *reinterpret_cast(&Records[1]); - WalltimeMarker.Type = uint8_t(RecordType::Metadata); - WalltimeMarker.RecordKind = - uint8_t(MetadataRecord::RecordKinds::WalltimeMarker); - timespec TS{0, 0}; - clock_gettime(CLOCK_MONOTONIC, &TS); - - // We only really need microsecond precision here, and enforce across - // platforms that we need 64-bit seconds and 32-bit microseconds encoded in - // the Metadata record. - int32_t Micros = TS.tv_nsec / 1000; - int64_t Seconds = TS.tv_sec; - std::memcpy(WalltimeMarker.Data, &Seconds, sizeof(Seconds)); - std::memcpy(WalltimeMarker.Data + sizeof(Seconds), &Micros, sizeof(Micros)); - } - std::memcpy(RecordPtr, Records, sizeof(MetadataRecord) * InitRecordsCount); - RecordPtr += sizeof(MetadataRecord) * InitRecordsCount; -} - -void writeNewCPUIdMetadata(uint16_t CPU, uint64_t TSC) XRAY_NEVER_INSTRUMENT { - MetadataRecord NewCPUId; - NewCPUId.Type = uint8_t(RecordType::Metadata); - NewCPUId.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewCPUId); - - // The data for the New CPU will contain the following bytes: - // - CPU ID (uint16_t, 2 bytes) - // - Full TSC (uint64_t, 8 bytes) - // Total = 12 bytes. - std::memcpy(&NewCPUId.Data, &CPU, sizeof(CPU)); - std::memcpy(&NewCPUId.Data[sizeof(CPU)], &TSC, sizeof(TSC)); - std::memcpy(RecordPtr, &NewCPUId, sizeof(MetadataRecord)); - RecordPtr += sizeof(MetadataRecord); -} - -void writeEOBMetadata() XRAY_NEVER_INSTRUMENT { - MetadataRecord EOBMeta; - EOBMeta.Type = uint8_t(RecordType::Metadata); - EOBMeta.RecordKind = uint8_t(MetadataRecord::RecordKinds::EndOfBuffer); - // For now we don't write any bytes into the Data field. - std::memcpy(RecordPtr, &EOBMeta, sizeof(MetadataRecord)); - RecordPtr += sizeof(MetadataRecord); -} - -void writeTSCWrapMetadata(uint64_t TSC) XRAY_NEVER_INSTRUMENT { - MetadataRecord TSCWrap; - TSCWrap.Type = uint8_t(RecordType::Metadata); - TSCWrap.RecordKind = uint8_t(MetadataRecord::RecordKinds::TSCWrap); - - // The data for the TSCWrap record contains the following bytes: - // - Full TSC (uint64_t, 8 bytes) - // Total = 8 bytes. - std::memcpy(&TSCWrap.Data, &TSC, sizeof(TSC)); - std::memcpy(RecordPtr, &TSCWrap, sizeof(MetadataRecord)); - RecordPtr += sizeof(MetadataRecord); -} - -constexpr auto MetadataRecSize = sizeof(MetadataRecord); -constexpr auto FunctionRecSize = sizeof(FunctionRecord); - -class ThreadExitBufferCleanup { - std::weak_ptr Buffers; - BufferQueue::Buffer &Buffer; - -public: - explicit ThreadExitBufferCleanup(std::weak_ptr BQ, - BufferQueue::Buffer &Buffer) - XRAY_NEVER_INSTRUMENT : Buffers(BQ), - Buffer(Buffer) {} - - ~ThreadExitBufferCleanup() noexcept XRAY_NEVER_INSTRUMENT { - if (RecordPtr == nullptr) - return; - - // We make sure that upon exit, a thread will write out the EOB - // MetadataRecord in the thread-local log, and also release the buffer to - // the queue. - assert((RecordPtr + MetadataRecSize) - static_cast(Buffer.Buffer) >= - static_cast(MetadataRecSize)); - if (auto BQ = Buffers.lock()) { - writeEOBMetadata(); - if (auto EC = BQ->releaseBuffer(Buffer)) - Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, - EC.message().c_str()); - return; - } - } -}; - -class RecursionGuard { - bool &Running; - const bool Valid; - -public: - explicit RecursionGuard(bool &R) : Running(R), Valid(!R) { - if (Valid) - Running = true; - } - - RecursionGuard(const RecursionGuard &) = delete; - RecursionGuard(RecursionGuard &&) = delete; - RecursionGuard &operator=(const RecursionGuard &) = delete; - RecursionGuard &operator=(RecursionGuard &&) = delete; - - explicit operator bool() const { return Valid; } - - ~RecursionGuard() noexcept { - if (Valid) - Running = false; - } -}; - -inline bool loggingInitialized() { - return LoggingStatus.load(std::memory_order_acquire) == - XRayLogInitStatus::XRAY_LOG_INITIALIZED; -} - -} // namespace - void fdrLoggingHandleArg0(int32_t FuncId, - XRayEntryType Entry) XRAY_NEVER_INSTRUMENT { + XRayEntryType Entry) XRAY_NEVER_INSTRUMENT { // We want to get the TSC as early as possible, so that we can check whether // we've seen this CPU before. We also do it before we load anything else, to // allow for forward progress with the scheduling. unsigned char CPU; uint64_t TSC = __xray::readTSC(CPU); - // Bail out right away if logging is not initialized yet. - if (LoggingStatus.load(std::memory_order_acquire) != - XRayLogInitStatus::XRAY_LOG_INITIALIZED) - return; - - // We use a thread_local variable to keep track of which CPUs we've already - // run, and the TSC times for these CPUs. This allows us to stop repeating the - // CPU field in the function records. - // - // We assume that we'll support only 65536 CPUs for x86_64. - thread_local uint16_t CurrentCPU = std::numeric_limits::max(); - thread_local uint64_t LastTSC = 0; - - // Make sure a thread that's ever called handleArg0 has a thread-local - // live reference to the buffer queue for this particular instance of - // FDRLogging, and that we're going to clean it up when the thread exits. - thread_local auto LocalBQ = BQ; - thread_local ThreadExitBufferCleanup Cleanup(LocalBQ, Buffer); - - // Prevent signal handler recursion, so in case we're already in a log writing - // mode and the signal handler comes in (and is also instrumented) then we - // don't want to be clobbering potentially partial writes already happening in - // the thread. We use a simple thread_local latch to only allow one on-going - // handleArg0 to happen at any given time. - thread_local bool Running = false; - RecursionGuard Guard{Running}; - if (!Guard) { - assert(Running == true && "RecursionGuard is buggy!"); - return; - } - - if (!loggingInitialized() || LocalBQ->finalizing()) { - writeEOBMetadata(); - if (auto EC = BQ->releaseBuffer(Buffer)) { - Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, - EC.message().c_str()); - return; - } - RecordPtr = nullptr; - } - - if (Buffer.Buffer == nullptr) { - if (auto EC = LocalBQ->getBuffer(Buffer)) { - auto LS = LoggingStatus.load(std::memory_order_acquire); - if (LS != XRayLogInitStatus::XRAY_LOG_FINALIZING && - LS != XRayLogInitStatus::XRAY_LOG_FINALIZED) - Report("Failed to acquire a buffer; error=%s\n", EC.message().c_str()); - return; - } - - setupNewBuffer(Buffer); - } - - if (CurrentCPU == std::numeric_limits::max()) { - // This means this is the first CPU this thread has ever run on. We set the - // current CPU and record this as the first TSC we've seen. - CurrentCPU = CPU; - writeNewCPUIdMetadata(CPU, TSC); - } - - // Before we go setting up writing new function entries, we need to be really - // careful about the pointer math we're doing. This means we need to ensure - // that the record we are about to write is going to fit into the buffer, - // without overflowing the buffer. - // - // To do this properly, we use the following assumptions: - // - // - The least number of bytes we will ever write is 8 - // (sizeof(FunctionRecord)) only if the delta between the previous entry - // and this entry is within 32 bits. - // - The most number of bytes we will ever write is 8 + 16 = 24. This is - // computed by: - // - // sizeof(FunctionRecord) + sizeof(MetadataRecord) - // - // These arise in the following cases: - // - // 1. When the delta between the TSC we get and the previous TSC for the - // same CPU is outside of the uint32_t range, we end up having to - // write a MetadataRecord to indicate a "tsc wrap" before the actual - // FunctionRecord. - // 2. When we learn that we've moved CPUs, we need to write a - // MetadataRecord to indicate a "cpu change", and thus write out the - // current TSC for that CPU before writing out the actual - // FunctionRecord. - // 3. When we learn about a new CPU ID, we need to write down a "new cpu - // id" MetadataRecord before writing out the actual FunctionRecord. - // - // - An End-of-Buffer (EOB) MetadataRecord is 16 bytes. - // - // So the math we need to do is to determine whether writing 24 bytes past the - // current pointer leaves us with enough bytes to write the EOB - // MetadataRecord. If we don't have enough space after writing as much as 24 - // bytes in the end of the buffer, we need to write out the EOB, get a new - // Buffer, set it up properly before doing any further writing. - // - char *BufferStart = static_cast(Buffer.Buffer); - if ((RecordPtr + (MetadataRecSize + FunctionRecSize)) - BufferStart < - static_cast(MetadataRecSize)) { - writeEOBMetadata(); - if (auto EC = LocalBQ->releaseBuffer(Buffer)) { - Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, - EC.message().c_str()); - return; - } - if (auto EC = LocalBQ->getBuffer(Buffer)) { - Report("Failed to acquire a buffer; error=%s\n", EC.message().c_str()); - return; - } - setupNewBuffer(Buffer); - } - - // By this point, we are now ready to write at most 24 bytes (one metadata - // record and one function record). - BufferStart = static_cast(Buffer.Buffer); - assert((RecordPtr + (MetadataRecSize + FunctionRecSize)) - BufferStart >= - static_cast(MetadataRecSize) && - "Misconfigured BufferQueue provided; Buffer size not large enough."); - - std::aligned_storage::type - AlignedFuncRecordBuffer; - auto &FuncRecord = - *reinterpret_cast(&AlignedFuncRecordBuffer); - FuncRecord.Type = uint8_t(RecordType::Function); - - // Only get the lower 28 bits of the function id. - FuncRecord.FuncId = FuncId & ~(0x0F << 28); - - // Here we compute the TSC Delta. There are a few interesting situations we - // need to account for: - // - // - The thread has migrated to a different CPU. If this is the case, then - // we write down the following records: - // - // 1. A 'NewCPUId' Metadata record. - // 2. A FunctionRecord with a 0 for the TSCDelta field. - // - // - The TSC delta is greater than the 32 bits we can store in a - // FunctionRecord. In this case we write down the following records: - // - // 1. A 'TSCWrap' Metadata record. - // 2. A FunctionRecord with a 0 for the TSCDelta field. - // - // - The TSC delta is representable within the 32 bits we can store in a - // FunctionRecord. In this case we write down just a FunctionRecord with - // the correct TSC delta. - // - FuncRecord.TSCDelta = 0; - if (CPU != CurrentCPU) { - // We've moved to a new CPU. - writeNewCPUIdMetadata(CPU, TSC); - } else { - // If the delta is greater than the range for a uint32_t, then we write out - // the TSC wrap metadata entry with the full TSC, and the TSC for the - // function record be 0. - auto Delta = LastTSC - TSC; - if (Delta > (1ULL << 32) - 1) - writeTSCWrapMetadata(TSC); - else - FuncRecord.TSCDelta = Delta; - } - - // We then update our "LastTSC" and "CurrentCPU" thread-local variables to aid - // us in future computations of this TSC delta value. - LastTSC = TSC; - CurrentCPU = CPU; - - switch (Entry) { - case XRayEntryType::ENTRY: - case XRayEntryType::LOG_ARGS_ENTRY: - FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter); - break; - case XRayEntryType::EXIT: - FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionExit); - break; - case XRayEntryType::TAIL: - FuncRecord.RecordKind = - uint8_t(FunctionRecord::RecordKinds::FunctionTailExit); - break; - } - - std::memcpy(RecordPtr, &AlignedFuncRecordBuffer, sizeof(FunctionRecord)); - RecordPtr += sizeof(FunctionRecord); - - // If we've exhausted the buffer by this time, we then release the buffer to - // make sure that other threads may start using this buffer. - if ((RecordPtr + MetadataRecSize) - BufferStart == MetadataRecSize) { - writeEOBMetadata(); - if (auto EC = LocalBQ->releaseBuffer(Buffer)) { - Report("Failed releasing buffer at %p; error=%s\n", Buffer.Buffer, - EC.message().c_str()); - return; - } - RecordPtr = nullptr; - } + __xray_fdr_internal::processFunctionHook(FuncId, Entry, TSC, CPU, + clock_gettime, LoggingStatus, BQ); } } // namespace __xray diff --git a/lib/xray/xray_fdr_logging.h b/lib/xray/xray_fdr_logging.h index 28c829625b..426b54dc78 100644 --- a/lib/xray/xray_fdr_logging.h +++ b/lib/xray/xray_fdr_logging.h @@ -14,6 +14,7 @@ #define XRAY_XRAY_FDR_LOGGING_H #include "xray/xray_log_interface.h" +#include "xray_fdr_log_records.h" // FDR (Flight Data Recorder) Mode // =============================== @@ -25,67 +26,8 @@ // default mode of always writing fixed-size records. namespace __xray { - -enum class RecordType : uint8_t { - Function, Metadata -}; - -// A MetadataRecord encodes the kind of record in its first byte, and have 15 -// additional bytes in the end to hold free-form data. -struct alignas(16) MetadataRecord { - // A MetadataRecord must always have a type of 1. - /* RecordType */ uint8_t Type : 1; - - // Each kind of record is represented as a 7-bit value (even though we use an - // unsigned 8-bit enum class to do so). - enum class RecordKinds : uint8_t { - NewBuffer, - EndOfBuffer, - NewCPUId, - TSCWrap, - WalltimeMarker, - }; - // Use 7 bits to identify this record type. - /* RecordKinds */ uint8_t RecordKind : 7; - char Data[15]; -} __attribute__((packed)); - -static_assert(sizeof(MetadataRecord) == 16, "Wrong size for MetadataRecord."); - -struct alignas(8) FunctionRecord { - // A FunctionRecord must always have a type of 0. - /* RecordType */ uint8_t Type : 1; - enum class RecordKinds { - FunctionEnter = 0x00, - FunctionExit = 0x01, - FunctionTailExit = 0x02, - }; - /* RecordKinds */ uint8_t RecordKind : 3; - - // We only use 28 bits of the function ID, so that we can use as few bytes as - // possible. This means we only support 2^28 (268,435,456) unique function ids - // in a single binary. - int FuncId : 28; - - // We use another 4 bytes to hold the delta between the previous entry's TSC. - // In case we've found that the distance is greater than the allowable 32 bits - // (either because we are running in a different CPU and the TSC might be - // different then), we should use a MetadataRecord before this FunctionRecord - // that will contain the full TSC for that CPU, and keep this to 0. - uint32_t TSCDelta; -} __attribute__((packed)); - -static_assert(sizeof(FunctionRecord) == 8, "Wrong size for FunctionRecord."); - -// Options used by the FDR implementation. -struct FDRLoggingOptions { - bool ReportErrors = false; - int Fd = -1; -}; - -// Flight Data Recorder mode implementation interfaces. XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax, - void *Options, size_t OptionsSize); + void *Options, size_t OptionsSize); XRayLogInitStatus fdrLoggingFinalize(); void fdrLoggingHandleArg0(int32_t FuncId, XRayEntryType Entry); XRayLogFlushStatus fdrLoggingFlush(); diff --git a/lib/xray/xray_fdr_logging_impl.h b/lib/xray/xray_fdr_logging_impl.h new file mode 100644 index 0000000000..b0b2a73404 --- /dev/null +++ b/lib/xray/xray_fdr_logging_impl.h @@ -0,0 +1,514 @@ +//===-- xray_fdr_logging_impl.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a dynamic runtime instrumentation system. +// +// Here we implement the thread local state management and record i/o for Flight +// Data Recorder mode for XRay, where we use compact structures to store records +// in memory as well as when writing out the data to files. +// +//===----------------------------------------------------------------------===// +#ifndef XRAY_XRAY_FDR_LOGGING_IMPL_H +#define XRAY_XRAY_FDR_LOGGING_IMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sanitizer_common/sanitizer_common.h" +#include "xray/xray_log_interface.h" +#include "xray_buffer_queue.h" +#include "xray_defs.h" +#include "xray_fdr_log_records.h" + +namespace __xray { + +/// We expose some of the state transitions when FDR logging mode is operating +/// such that we can simulate a series of log events that may occur without +/// and test with determinism without worrying about the real CPU time. +/// +/// Because the code uses thread_local allocation extensively as part of its +/// design, callers that wish to test events occuring on different threads +/// will actually have to run them on different threads. +/// +/// This also means that it is possible to break invariants maintained by +/// cooperation with xray_fdr_logging class, so be careful and think twice. +namespace __xray_fdr_internal { + +/// Writes the new buffer record and wallclock time that begin a buffer for a +/// thread to MemPtr and increments MemPtr. Bypasses the thread local state +// machine and writes directly to memory without checks. +static void writeNewBufferPreamble(pid_t Tid, timespec TS, char *&MemPtr); + +/// Write a metadata record to switch to a new CPU to MemPtr and increments +/// MemPtr. Bypasses the thread local state machine and writes directly to +/// memory without checks. +static void writeNewCPUIdMetadata(uint16_t CPU, uint64_t TSC, char *&MemPtr); + +/// Writes an EOB metadata record to MemPtr and increments MemPtr. Bypasses the +/// thread local state machine and writes directly to memory without checks. +static void writeEOBMetadata(char *&MemPtr); + +/// Writes a TSC Wrap metadata record to MemPtr and increments MemPtr. Bypasses +/// the thread local state machine and directly writes to memory without checks. +static void writeTSCWrapMetadata(uint64_t TSC, char *&MemPtr); + +/// Writes a Function Record to MemPtr and increments MemPtr. Bypasses the +/// thread local state machine and writes the function record directly to +/// memory. +static void writeFunctionRecord(int FuncId, uint32_t TSCDelta, + XRayEntryType EntryType, char *&MemPtr); + +/// Sets up a new buffer in thread_local storage and writes a preamble. The +/// wall_clock_reader function is used to populate the WallTimeRecord entry. +static void setupNewBuffer(const BufferQueue::Buffer &Buffer, + int (*wall_clock_reader)(clockid_t, + struct timespec *)); + +/// Called to record CPU time for a new CPU within the current thread. +static void writeNewCPUIdMetadata(uint16_t CPU, uint64_t TSC); + +/// Called to close the buffer when the thread exhausts the buffer or when the +/// thread exits (via a thread local variable destructor). +static void writeEOBMetadata(); + +/// TSC Wrap records are written when a TSC delta encoding scheme overflows. +static void writeTSCWrapMetadata(uint64_t TSC); + +/// Here's where the meat of the processing happens. The writer captures +/// function entry, exit and tail exit points with a time and will create +/// TSCWrap, NewCPUId and Function records as necessary. The writer might +/// walk backward through its buffer and erase trivial functions to avoid +/// polluting the log and may use the buffer queue to obtain or release a +/// buffer. +static void processFunctionHook(int32_t FuncId, XRayEntryType Entry, + uint64_t TSC, unsigned char CPU, + int (*wall_clock_reader)(clockid_t, + struct timespec *), + __sanitizer::atomic_sint32_t &LoggingStatus, + const std::shared_ptr &BQ); + +//-----------------------------------------------------------------------------| +// The rest of the file is implementation. | +//-----------------------------------------------------------------------------| +// Functions are implemented in the header for inlining since we don't want | +// to grow the stack when we've hijacked the binary for logging. | +//-----------------------------------------------------------------------------| + +namespace { +thread_local BufferQueue::Buffer Buffer; +thread_local char *RecordPtr = nullptr; + +constexpr auto MetadataRecSize = sizeof(MetadataRecord); +constexpr auto FunctionRecSize = sizeof(FunctionRecord); + +class ThreadExitBufferCleanup { + std::weak_ptr Buffers; + BufferQueue::Buffer &Buffer; + +public: + explicit ThreadExitBufferCleanup(std::weak_ptr BQ, + BufferQueue::Buffer &Buffer) + XRAY_NEVER_INSTRUMENT : Buffers(BQ), + Buffer(Buffer) {} + + ~ThreadExitBufferCleanup() noexcept XRAY_NEVER_INSTRUMENT { + if (RecordPtr == nullptr) + return; + + // We make sure that upon exit, a thread will write out the EOB + // MetadataRecord in the thread-local log, and also release the buffer to + // the queue. + assert((RecordPtr + MetadataRecSize) - static_cast(Buffer.Buffer) >= + static_cast(MetadataRecSize)); + if (auto BQ = Buffers.lock()) { + writeEOBMetadata(); + auto EC = BQ->releaseBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) + Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, + BufferQueue::getErrorString(EC)); + return; + } + } +}; + +class RecursionGuard { + bool &Running; + const bool Valid; + +public: + explicit RecursionGuard(bool &R) : Running(R), Valid(!R) { + if (Valid) + Running = true; + } + + RecursionGuard(const RecursionGuard &) = delete; + RecursionGuard(RecursionGuard &&) = delete; + RecursionGuard &operator=(const RecursionGuard &) = delete; + RecursionGuard &operator=(RecursionGuard &&) = delete; + + explicit operator bool() const { return Valid; } + + ~RecursionGuard() noexcept { + if (Valid) + Running = false; + } +}; + +static inline bool loggingInitialized( + const __sanitizer::atomic_sint32_t &LoggingStatus) XRAY_NEVER_INSTRUMENT { + return __sanitizer::atomic_load(&LoggingStatus, + __sanitizer::memory_order_acquire) == + XRayLogInitStatus::XRAY_LOG_INITIALIZED; +} + +} // namespace + +static inline void writeNewBufferPreamble(pid_t Tid, timespec TS, + char *&MemPtr) XRAY_NEVER_INSTRUMENT { + static constexpr int InitRecordsCount = 2; + std::aligned_storage::type Records[InitRecordsCount]; + { + // Write out a MetadataRecord to signify that this is the start of a new + // buffer, associated with a particular thread, with a new CPU. For the + // data, we have 15 bytes to squeeze as much information as we can. At this + // point we only write down the following bytes: + // - Thread ID (pid_t, 4 bytes) + auto &NewBuffer = *reinterpret_cast(&Records[0]); + NewBuffer.Type = uint8_t(RecordType::Metadata); + NewBuffer.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewBuffer); + std::memcpy(&NewBuffer.Data, &Tid, sizeof(pid_t)); + } + // Also write the WalltimeMarker record. + { + static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes"); + auto &WalltimeMarker = *reinterpret_cast(&Records[1]); + WalltimeMarker.Type = uint8_t(RecordType::Metadata); + WalltimeMarker.RecordKind = + uint8_t(MetadataRecord::RecordKinds::WalltimeMarker); + + // We only really need microsecond precision here, and enforce across + // platforms that we need 64-bit seconds and 32-bit microseconds encoded in + // the Metadata record. + int32_t Micros = TS.tv_nsec / 1000; + int64_t Seconds = TS.tv_sec; + std::memcpy(WalltimeMarker.Data, &Seconds, sizeof(Seconds)); + std::memcpy(WalltimeMarker.Data + sizeof(Seconds), &Micros, sizeof(Micros)); + } + std::memcpy(MemPtr, Records, sizeof(MetadataRecord) * InitRecordsCount); + MemPtr += sizeof(MetadataRecord) * InitRecordsCount; +} + +static inline void setupNewBuffer(const BufferQueue::Buffer &Buffer, + int (*wall_clock_reader)(clockid_t, + struct timespec *)) + XRAY_NEVER_INSTRUMENT { + RecordPtr = static_cast(Buffer.Buffer); + pid_t Tid = syscall(SYS_gettid); + timespec TS{0, 0}; + // This is typically clock_gettime, but callers have injection ability. + wall_clock_reader(CLOCK_MONOTONIC, &TS); + writeNewBufferPreamble(Tid, TS, RecordPtr); +} + +static inline void writeNewCPUIdMetadata(uint16_t CPU, uint64_t TSC, + char *&MemPtr) XRAY_NEVER_INSTRUMENT { + MetadataRecord NewCPUId; + NewCPUId.Type = uint8_t(RecordType::Metadata); + NewCPUId.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewCPUId); + + // The data for the New CPU will contain the following bytes: + // - CPU ID (uint16_t, 2 bytes) + // - Full TSC (uint64_t, 8 bytes) + // Total = 12 bytes. + std::memcpy(&NewCPUId.Data, &CPU, sizeof(CPU)); + std::memcpy(&NewCPUId.Data[sizeof(CPU)], &TSC, sizeof(TSC)); + std::memcpy(MemPtr, &NewCPUId, sizeof(MetadataRecord)); + MemPtr += sizeof(MetadataRecord); +} + +static inline void writeNewCPUIdMetadata(uint16_t CPU, + uint64_t TSC) XRAY_NEVER_INSTRUMENT { + writeNewCPUIdMetadata(CPU, TSC, RecordPtr); +} + +static inline void writeEOBMetadata(char *&MemPtr) XRAY_NEVER_INSTRUMENT { + MetadataRecord EOBMeta; + EOBMeta.Type = uint8_t(RecordType::Metadata); + EOBMeta.RecordKind = uint8_t(MetadataRecord::RecordKinds::EndOfBuffer); + // For now we don't write any bytes into the Data field. + std::memcpy(MemPtr, &EOBMeta, sizeof(MetadataRecord)); + MemPtr += sizeof(MetadataRecord); +} + +static inline void writeEOBMetadata() XRAY_NEVER_INSTRUMENT { + writeEOBMetadata(RecordPtr); +} + +static inline void writeTSCWrapMetadata(uint64_t TSC, + char *&MemPtr) XRAY_NEVER_INSTRUMENT { + MetadataRecord TSCWrap; + TSCWrap.Type = uint8_t(RecordType::Metadata); + TSCWrap.RecordKind = uint8_t(MetadataRecord::RecordKinds::TSCWrap); + + // The data for the TSCWrap record contains the following bytes: + // - Full TSC (uint64_t, 8 bytes) + // Total = 8 bytes. + std::memcpy(&TSCWrap.Data, &TSC, sizeof(TSC)); + std::memcpy(MemPtr, &TSCWrap, sizeof(MetadataRecord)); + MemPtr += sizeof(MetadataRecord); +} + +static inline void writeTSCWrapMetadata(uint64_t TSC) XRAY_NEVER_INSTRUMENT { + writeTSCWrapMetadata(TSC, RecordPtr); +} + +static inline void writeFunctionRecord(int FuncId, uint32_t TSCDelta, + XRayEntryType EntryType, + char *&MemPtr) XRAY_NEVER_INSTRUMENT { + std::aligned_storage::type + AlignedFuncRecordBuffer; + auto &FuncRecord = + *reinterpret_cast(&AlignedFuncRecordBuffer); + FuncRecord.Type = uint8_t(RecordType::Function); + // Only take 28 bits of the function id. + FuncRecord.FuncId = FuncId & ~(0x0F << 28); + FuncRecord.TSCDelta = TSCDelta; + + switch (EntryType) { + case XRayEntryType::ENTRY: + case XRayEntryType::LOG_ARGS_ENTRY: + FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter); + break; + case XRayEntryType::EXIT: + FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionExit); + break; + case XRayEntryType::TAIL: + FuncRecord.RecordKind = + uint8_t(FunctionRecord::RecordKinds::FunctionTailExit); + break; + } + + std::memcpy(MemPtr, &AlignedFuncRecordBuffer, sizeof(FunctionRecord)); + MemPtr += sizeof(FunctionRecord); +} + +static inline void processFunctionHook( + int32_t FuncId, XRayEntryType Entry, uint64_t TSC, unsigned char CPU, + int (*wall_clock_reader)(clockid_t, struct timespec *), + __sanitizer::atomic_sint32_t &LoggingStatus, + const std::shared_ptr &BQ) XRAY_NEVER_INSTRUMENT { + // Bail out right away if logging is not initialized yet. + // We should take the opportunity to release the buffer though. + auto Status = __sanitizer::atomic_load(&LoggingStatus, + __sanitizer::memory_order_acquire); + if (Status != XRayLogInitStatus::XRAY_LOG_INITIALIZED) { + if (RecordPtr != nullptr && + (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING || + Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)) { + writeEOBMetadata(); + auto EC = BQ->releaseBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) { + Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, + BufferQueue::getErrorString(EC)); + return; + } + RecordPtr = nullptr; + } + return; + } + + // We use a thread_local variable to keep track of which CPUs we've already + // run, and the TSC times for these CPUs. This allows us to stop repeating the + // CPU field in the function records. + // + // We assume that we'll support only 65536 CPUs for x86_64. + thread_local uint16_t CurrentCPU = std::numeric_limits::max(); + thread_local uint64_t LastTSC = 0; + + // Make sure a thread that's ever called handleArg0 has a thread-local + // live reference to the buffer queue for this particular instance of + // FDRLogging, and that we're going to clean it up when the thread exits. + thread_local auto LocalBQ = BQ; + thread_local ThreadExitBufferCleanup Cleanup(LocalBQ, Buffer); + + // Prevent signal handler recursion, so in case we're already in a log writing + // mode and the signal handler comes in (and is also instrumented) then we + // don't want to be clobbering potentially partial writes already happening in + // the thread. We use a simple thread_local latch to only allow one on-going + // handleArg0 to happen at any given time. + thread_local bool Running = false; + RecursionGuard Guard{Running}; + if (!Guard) { + assert(Running == true && "RecursionGuard is buggy!"); + return; + } + + if (!loggingInitialized(LoggingStatus) || LocalBQ->finalizing()) { + writeEOBMetadata(); + auto EC = BQ->releaseBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) { + Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, + BufferQueue::getErrorString(EC)); + return; + } + RecordPtr = nullptr; + } + + if (Buffer.Buffer == nullptr) { + auto EC = LocalBQ->getBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) { + auto LS = __sanitizer::atomic_load(&LoggingStatus, + __sanitizer::memory_order_acquire); + if (LS != XRayLogInitStatus::XRAY_LOG_FINALIZING && + LS != XRayLogInitStatus::XRAY_LOG_FINALIZED) + Report("Failed to acquire a buffer; error=%s\n", + BufferQueue::getErrorString(EC)); + return; + } + + setupNewBuffer(Buffer, wall_clock_reader); + } + + if (CurrentCPU == std::numeric_limits::max()) { + // This means this is the first CPU this thread has ever run on. We set the + // current CPU and record this as the first TSC we've seen. + CurrentCPU = CPU; + writeNewCPUIdMetadata(CPU, TSC); + } + + // Before we go setting up writing new function entries, we need to be really + // careful about the pointer math we're doing. This means we need to ensure + // that the record we are about to write is going to fit into the buffer, + // without overflowing the buffer. + // + // To do this properly, we use the following assumptions: + // + // - The least number of bytes we will ever write is 8 + // (sizeof(FunctionRecord)) only if the delta between the previous entry + // and this entry is within 32 bits. + // - The most number of bytes we will ever write is 8 + 16 = 24. This is + // computed by: + // + // sizeof(FunctionRecord) + sizeof(MetadataRecord) + // + // These arise in the following cases: + // + // 1. When the delta between the TSC we get and the previous TSC for the + // same CPU is outside of the uint32_t range, we end up having to + // write a MetadataRecord to indicate a "tsc wrap" before the actual + // FunctionRecord. + // 2. When we learn that we've moved CPUs, we need to write a + // MetadataRecord to indicate a "cpu change", and thus write out the + // current TSC for that CPU before writing out the actual + // FunctionRecord. + // 3. When we learn about a new CPU ID, we need to write down a "new cpu + // id" MetadataRecord before writing out the actual FunctionRecord. + // + // - An End-of-Buffer (EOB) MetadataRecord is 16 bytes. + // + // So the math we need to do is to determine whether writing 24 bytes past the + // current pointer leaves us with enough bytes to write the EOB + // MetadataRecord. If we don't have enough space after writing as much as 24 + // bytes in the end of the buffer, we need to write out the EOB, get a new + // Buffer, set it up properly before doing any further writing. + // + char *BufferStart = static_cast(Buffer.Buffer); + if ((RecordPtr + (MetadataRecSize + FunctionRecSize)) - BufferStart < + static_cast(MetadataRecSize)) { + writeEOBMetadata(); + auto EC = LocalBQ->releaseBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) { + Report("Failed to release buffer at %p; error=%s\n", Buffer.Buffer, + BufferQueue::getErrorString(EC)); + return; + } + EC = LocalBQ->getBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) { + Report("Failed to acquire a buffer; error=%s\n", + BufferQueue::getErrorString(EC)); + return; + } + setupNewBuffer(Buffer, wall_clock_reader); + } + + // By this point, we are now ready to write at most 24 bytes (one metadata + // record and one function record). + BufferStart = static_cast(Buffer.Buffer); + assert((RecordPtr + (MetadataRecSize + FunctionRecSize)) - BufferStart >= + static_cast(MetadataRecSize) && + "Misconfigured BufferQueue provided; Buffer size not large enough."); + + // Here we compute the TSC Delta. There are a few interesting situations we + // need to account for: + // + // - The thread has migrated to a different CPU. If this is the case, then + // we write down the following records: + // + // 1. A 'NewCPUId' Metadata record. + // 2. A FunctionRecord with a 0 for the TSCDelta field. + // + // - The TSC delta is greater than the 32 bits we can store in a + // FunctionRecord. In this case we write down the following records: + // + // 1. A 'TSCWrap' Metadata record. + // 2. A FunctionRecord with a 0 for the TSCDelta field. + // + // - The TSC delta is representable within the 32 bits we can store in a + // FunctionRecord. In this case we write down just a FunctionRecord with + // the correct TSC delta. + // + + uint32_t RecordTSCDelta = 0; + if (CPU != CurrentCPU) { + // We've moved to a new CPU. + writeNewCPUIdMetadata(CPU, TSC); + } else { + // If the delta is greater than the range for a uint32_t, then we write out + // the TSC wrap metadata entry with the full TSC, and the TSC for the + // function record be 0. + auto Delta = TSC - LastTSC; + if (Delta > (1ULL << 32) - 1) + writeTSCWrapMetadata(TSC); + else + RecordTSCDelta = Delta; + } + + // We then update our "LastTSC" and "CurrentCPU" thread-local variables to aid + // us in future computations of this TSC delta value. + LastTSC = TSC; + CurrentCPU = CPU; + + writeFunctionRecord(FuncId, RecordTSCDelta, Entry, RecordPtr); + + // If we've exhausted the buffer by this time, we then release the buffer to + // make sure that other threads may start using this buffer. + if ((RecordPtr + MetadataRecSize) - BufferStart == MetadataRecSize) { + writeEOBMetadata(); + auto EC = LocalBQ->releaseBuffer(Buffer); + if (EC != BufferQueue::ErrorCode::Ok) { + Report("Failed releasing buffer at %p; error=%s\n", Buffer.Buffer, + BufferQueue::getErrorString(EC)); + return; + } + RecordPtr = nullptr; + } +} + +} // namespace __xray_fdr_internal + +} // namespace __xray + +#endif // XRAY_XRAY_FDR_LOGGING_IMPL_H diff --git a/lib/xray/xray_init.cc b/lib/xray/xray_init.cc index 9d012e9a60..6f558d6561 100644 --- a/lib/xray/xray_init.cc +++ b/lib/xray/xray_init.cc @@ -12,7 +12,6 @@ // XRay initialisation logic. //===----------------------------------------------------------------------===// -#include #include #include #include @@ -28,7 +27,6 @@ extern const XRaySledEntry __start_xray_instr_map[] __attribute__((weak)); extern const XRaySledEntry __stop_xray_instr_map[] __attribute__((weak)); } -using namespace __sanitizer; using namespace __xray; // When set to 'true' this means the XRay runtime has been initialised. We use @@ -38,10 +36,11 @@ using namespace __xray; // // FIXME: Support DSO instrumentation maps too. The current solution only works // for statically linked executables. -std::atomic XRayInitialized{false}; +__sanitizer::atomic_uint8_t XRayInitialized{0}; // This should always be updated before XRayInitialized is updated. -std::atomic<__xray::XRaySledMap> XRayInstrMap{}; +__sanitizer::SpinMutex XRayInstrMapMutex; +XRaySledMap XRayInstrMap; // __xray_init() will do the actual loading of the current process' memory map // and then proceed to look for the .xray_instr_map section/segment. @@ -52,15 +51,13 @@ void __xray_init() XRAY_NEVER_INSTRUMENT { return; } - // Now initialize the XRayInstrMap global struct with the address of the - // entries, reinterpreted as an array of XRaySledEntry objects. We use the - // virtual pointer we have from the section to provide us the correct - // information. - __xray::XRaySledMap SledMap{}; - SledMap.Sleds = __start_xray_instr_map; - SledMap.Entries = __stop_xray_instr_map - __start_xray_instr_map; - XRayInstrMap.store(SledMap, std::memory_order_release); - XRayInitialized.store(true, std::memory_order_release); + { + __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex); + XRayInstrMap.Sleds = __start_xray_instr_map; + XRayInstrMap.Entries = __stop_xray_instr_map - __start_xray_instr_map; + } + __sanitizer::atomic_store(&XRayInitialized, true, + __sanitizer::memory_order_release); if (flags()->patch_premain) __xray_patch(); diff --git a/lib/xray/xray_inmemory_log.cc b/lib/xray/xray_inmemory_log.cc index fd4f549310..cdaa6d1b5c 100644 --- a/lib/xray/xray_inmemory_log.cc +++ b/lib/xray/xray_inmemory_log.cc @@ -77,7 +77,6 @@ using namespace __xray; static int __xray_OpenLogFile() XRAY_NEVER_INSTRUMENT { int F = getLogFD(); - auto TSCFrequency = getTSCFrequency(); if (F == -1) return -1; // Since we're here, we get to write the header. We set it up so that the @@ -86,7 +85,9 @@ static int __xray_OpenLogFile() XRAY_NEVER_INSTRUMENT { XRayFileHeader Header; Header.Version = 1; Header.Type = FileTypes::NAIVE_LOG; - Header.CycleFrequency = TSCFrequency; + Header.CycleFrequency = probeRequiredCPUFeatures() + ? getTSCFrequency() + : __xray::NanosecondsPerSecond; // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc' // before setting the values in the header. @@ -97,8 +98,9 @@ static int __xray_OpenLogFile() XRAY_NEVER_INSTRUMENT { return F; } -void __xray_InMemoryRawLog(int32_t FuncId, - XRayEntryType Type) XRAY_NEVER_INSTRUMENT { +template +void __xray_InMemoryRawLog(int32_t FuncId, XRayEntryType Type, + RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT { using Buffer = std::aligned_storage::type; static constexpr size_t BuffLen = 1024; @@ -115,7 +117,7 @@ void __xray_InMemoryRawLog(int32_t FuncId, // through a pointer offset. auto &R = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer)[Offset]; R.RecordType = RecordTypes::NORMAL; - R.TSC = __xray::readTSC(R.CPU); + R.TSC = ReadTSC(R.CPU); R.TId = TId; R.Type = Type; R.FuncId = FuncId; @@ -129,13 +131,32 @@ void __xray_InMemoryRawLog(int32_t FuncId, } } +void __xray_InMemoryRawLogRealTSC(int32_t FuncId, + XRayEntryType Type) XRAY_NEVER_INSTRUMENT { + __xray_InMemoryRawLog(FuncId, Type, __xray::readTSC); +} + +void __xray_InMemoryEmulateTSC(int32_t FuncId, + XRayEntryType Type) XRAY_NEVER_INSTRUMENT { + __xray_InMemoryRawLog(FuncId, Type, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT { + timespec TS; + int result = clock_gettime(CLOCK_REALTIME, &TS); + if (result != 0) { + Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno)); + TS = {0, 0}; + } + CPU = 0; + return TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec; + }); +} + static auto UNUSED Unused = [] { - if (!probeRequiredCPUFeatures()) { - Report("Required CPU features missing for XRay instrumentation, not " - "installing instrumentation hooks.\n"); - return false; - } + auto UseRealTSC = probeRequiredCPUFeatures(); + if (!UseRealTSC) + Report("WARNING: Required CPU features missing for XRay instrumentation, " + "using emulation instead.\n"); if (flags()->xray_naive_log) - __xray_set_handler(__xray_InMemoryRawLog); + __xray_set_handler(UseRealTSC ? __xray_InMemoryRawLogRealTSC + : __xray_InMemoryEmulateTSC); return true; }(); diff --git a/lib/xray/xray_interface.cc b/lib/xray/xray_interface.cc index 39cf8efb34..26ec161fe8 100644 --- a/lib/xray/xray_interface.cc +++ b/lib/xray/xray_interface.cc @@ -15,7 +15,6 @@ #include "xray_interface_internal.h" -#include #include #include #include @@ -46,10 +45,10 @@ static const int16_t cSledLength = 8; #endif /* CPU architecture */ // This is the function to call when we encounter the entry or exit sleds. -std::atomic XRayPatchedFunction{nullptr}; +__sanitizer::atomic_uintptr_t XRayPatchedFunction{0}; // This is the function to call from the arg1-enabled sleds/trampolines. -std::atomic XRayArgLogger{nullptr}; +__sanitizer::atomic_uintptr_t XRayArgLogger{0}; // MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo // any successful mprotect(...) changes. This is used to make a page writeable @@ -88,13 +87,18 @@ class MProtectHelper { } // namespace __xray -extern std::atomic XRayInitialized; -extern std::atomic<__xray::XRaySledMap> XRayInstrMap; +extern __sanitizer::SpinMutex XRayInstrMapMutex; +extern __sanitizer::atomic_uint8_t XRayInitialized; +extern __xray::XRaySledMap XRayInstrMap; int __xray_set_handler(void (*entry)(int32_t, XRayEntryType)) XRAY_NEVER_INSTRUMENT { - if (XRayInitialized.load(std::memory_order_acquire)) { - __xray::XRayPatchedFunction.store(entry, std::memory_order_release); + if (__sanitizer::atomic_load(&XRayInitialized, + __sanitizer::memory_order_acquire)) { + + __sanitizer::atomic_store(&__xray::XRayPatchedFunction, + reinterpret_cast(entry), + __sanitizer::memory_order_release); return 1; } return 0; @@ -104,7 +108,7 @@ int __xray_remove_handler() XRAY_NEVER_INSTRUMENT { return __xray_set_handler(nullptr); } -std::atomic XRayPatching{false}; +__sanitizer::atomic_uint8_t XRayPatching{0}; using namespace __xray; @@ -132,26 +136,29 @@ CleanupInvoker scopeCleanup(Function Fn) XRAY_NEVER_INSTRUMENT { // implementation. |Enable| defines whether we're enabling or disabling the // runtime XRay instrumentation. XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT { - if (!XRayInitialized.load(std::memory_order_acquire)) + if (!__sanitizer::atomic_load(&XRayInitialized, + __sanitizer::memory_order_acquire)) return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. - static bool NotPatching = false; - if (!XRayPatching.compare_exchange_strong(NotPatching, true, - std::memory_order_acq_rel, - std::memory_order_acquire)) { + uint8_t NotPatching = false; + if (!__sanitizer::atomic_compare_exchange_strong( + &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel)) return XRayPatchingStatus::ONGOING; // Already patching. - } - bool PatchingSuccess = false; + uint8_t PatchingSuccess = false; auto XRayPatchingStatusResetter = scopeCleanup([&PatchingSuccess] { - if (!PatchingSuccess) { - XRayPatching.store(false, std::memory_order_release); - } + if (!PatchingSuccess) + __sanitizer::atomic_store(&XRayPatching, false, + __sanitizer::memory_order_release); }); // Step 1: Compute the function id, as a unique identifier per function in the // instrumentation map. - XRaySledMap InstrMap = XRayInstrMap.load(std::memory_order_acquire); + XRaySledMap InstrMap; + { + __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex); + InstrMap = XRayInstrMap; + } if (InstrMap.Entries == 0) return XRayPatchingStatus::NOT_INITIALIZED; @@ -205,7 +212,8 @@ XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT { } (void)Success; } - XRayPatching.store(false, std::memory_order_release); + __sanitizer::atomic_store(&XRayPatching, false, + __sanitizer::memory_order_release); PatchingSuccess = true; return XRayPatchingStatus::SUCCESS; } @@ -218,15 +226,16 @@ XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT { return controlPatching(false); } -int __xray_set_handler_arg1(void (*Handler)(int32_t, XRayEntryType, uint64_t)) -{ - if (!XRayInitialized.load(std::memory_order_acquire)) { +int __xray_set_handler_arg1(void (*Handler)(int32_t, XRayEntryType, uint64_t)) { + if (!__sanitizer::atomic_load(&XRayInitialized, + __sanitizer::memory_order_acquire)) return 0; - } + // A relaxed write might not be visible even if the current thread gets // scheduled on a different CPU/NUMA node. We need to wait for everyone to // have this handler installed for consistency of collected data across CPUs. - XRayArgLogger.store(Handler, std::memory_order_release); + __sanitizer::atomic_store(&XRayArgLogger, reinterpret_cast(Handler), + __sanitizer::memory_order_release); return 1; } int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); } diff --git a/lib/xray/xray_log_interface.cc b/lib/xray/xray_log_interface.cc index c7cfe5080b..ffed601c05 100644 --- a/lib/xray/xray_log_interface.cc +++ b/lib/xray/xray_log_interface.cc @@ -12,45 +12,47 @@ //===----------------------------------------------------------------------===// #include "xray/xray_log_interface.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_mutex.h" #include "xray/xray_interface.h" #include "xray_defs.h" #include -#include -std::mutex XRayImplMutex; +__sanitizer::SpinMutex XRayImplMutex; std::unique_ptr GlobalXRayImpl; void __xray_set_log_impl(XRayLogImpl Impl) XRAY_NEVER_INSTRUMENT { if (Impl.log_init == nullptr || Impl.log_finalize == nullptr || Impl.handle_arg0 == nullptr || Impl.flush_log == nullptr) { - std::lock_guard Guard(XRayImplMutex); + __sanitizer::SpinMutexLock Guard(&XRayImplMutex); GlobalXRayImpl.reset(); return; } - std::lock_guard Guard(XRayImplMutex); + __sanitizer::SpinMutexLock Guard(&XRayImplMutex); GlobalXRayImpl.reset(new XRayLogImpl); *GlobalXRayImpl = Impl; } -XRayLogInitStatus __xray_init(size_t BufferSize, size_t MaxBuffers, void *Args, - size_t ArgsSize) XRAY_NEVER_INSTRUMENT { - std::lock_guard Guard(XRayImplMutex); +XRayLogInitStatus __xray_log_init(size_t BufferSize, size_t MaxBuffers, + void *Args, + size_t ArgsSize) XRAY_NEVER_INSTRUMENT { + __sanitizer::SpinMutexLock Guard(&XRayImplMutex); if (!GlobalXRayImpl) return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; return GlobalXRayImpl->log_init(BufferSize, MaxBuffers, Args, ArgsSize); } XRayLogInitStatus __xray_log_finalize() XRAY_NEVER_INSTRUMENT { - std::lock_guard Guard(XRayImplMutex); + __sanitizer::SpinMutexLock Guard(&XRayImplMutex); if (!GlobalXRayImpl) return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; return GlobalXRayImpl->log_finalize(); } XRayLogFlushStatus __xray_log_flushLog() XRAY_NEVER_INSTRUMENT { - std::lock_guard Guard(XRayImplMutex); + __sanitizer::SpinMutexLock Guard(&XRayImplMutex); if (!GlobalXRayImpl) return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; return GlobalXRayImpl->flush_log(); diff --git a/lib/xray/xray_trampoline_AArch64.S b/lib/xray/xray_trampoline_AArch64.S index b8c89e4bcc..4d1b04fb7d 100644 --- a/lib/xray/xray_trampoline_AArch64.S +++ b/lib/xray/xray_trampoline_AArch64.S @@ -1,3 +1,5 @@ +#include "../builtins/assembly.h" + .text /* The variable containing the handler function pointer */ .global _ZN6__xray19XRayPatchedFunctionE @@ -138,3 +140,5 @@ FunctionTailExit_restore: LDP X3, X4, [SP], #16 LDP X1, X2, [SP], #16 RET + +NO_EXEC_STACK_DIRECTIVE diff --git a/lib/xray/xray_trampoline_arm.S b/lib/xray/xray_trampoline_arm.S index ee6763e0e3..71dbee65d8 100644 --- a/lib/xray/xray_trampoline_arm.S +++ b/lib/xray/xray_trampoline_arm.S @@ -1,3 +1,5 @@ +#include "../builtins/assembly.h" + .syntax unified .arch armv6t2 .fpu vfpv2 @@ -96,3 +98,5 @@ FunctionTailExit_restore: @ Restore floating-point parameters of the instrumented function VPOP {d0-d7} POP {r1-r3,pc} + +NO_EXEC_STACK_DIRECTIVE diff --git a/lib/xray/xray_trampoline_x86_64.S b/lib/xray/xray_trampoline_x86_64.S index b9fef6dad9..da0aae326b 100644 --- a/lib/xray/xray_trampoline_x86_64.S +++ b/lib/xray/xray_trampoline_x86_64.S @@ -13,6 +13,8 @@ // //===----------------------------------------------------------------------===// +#include "../builtins/assembly.h" + .macro SAVE_REGISTERS subq $200, %rsp movupd %xmm0, 184(%rsp) @@ -188,3 +190,5 @@ __xray_ArgLoggerEntry: .Larg1entryEnd: .size __xray_ArgLoggerEntry, .Larg1entryEnd-__xray_ArgLoggerEntry .cfi_endproc + +NO_EXEC_STACK_DIRECTIVE diff --git a/lib/xray/xray_tsc.h b/lib/xray/xray_tsc.h index e9ed7c7350..4507564e7c 100644 --- a/lib/xray/xray_tsc.h +++ b/lib/xray/xray_tsc.h @@ -13,6 +13,10 @@ #ifndef XRAY_EMULATE_TSC_H #define XRAY_EMULATE_TSC_H +namespace __xray { +static constexpr uint64_t NanosecondsPerSecond = 1000ULL * 1000 * 1000; +} + #if defined(__x86_64__) #include "xray_x86_64.inc" #elif defined(__powerpc64__) @@ -37,8 +41,6 @@ namespace __xray { -static constexpr uint64_t NanosecondsPerSecond = 1000ULL * 1000 * 1000; - inline bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; } ALWAYS_INLINE uint64_t readTSC(uint8_t &CPU) XRAY_NEVER_INSTRUMENT { @@ -60,7 +62,7 @@ inline uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT { } // namespace __xray #else -"Unsupported CPU Architecture" +#error Target architecture is not supported. #endif // CPU architecture #endif // XRAY_EMULATE_TSC_H diff --git a/lib/xray/xray_utils.cc b/lib/xray/xray_utils.cc index 69e7b98fe3..b9a38d1b98 100644 --- a/lib/xray/xray_utils.cc +++ b/lib/xray/xray_utils.cc @@ -15,6 +15,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "xray_defs.h" #include "xray_flags.h" +#include #include #include #include @@ -92,8 +93,6 @@ bool readValueFromFile(const char *Filename, } int getLogFD() XRAY_NEVER_INSTRUMENT { - // FIXME: Figure out how to make this less stderr-dependent. - SetPrintfAndReportCallback(printToStdErr); // Open a temporary file once for the log. static char TmpFilename[256] = {}; static char TmpWildcardPattern[] = "XXXXXX"; @@ -118,8 +117,7 @@ int getLogFD() XRAY_NEVER_INSTRUMENT { TmpFilename); return -1; } - if (Verbosity()) - fprintf(stderr, "XRay: Log file in '%s'\n", TmpFilename); + Report("XRay: Log file in '%s'\n", TmpFilename); return Fd; } diff --git a/lib/xray/xray_x86_64.cc b/lib/xray/xray_x86_64.cc index 1b9131316d..8c2a4e313e 100644 --- a/lib/xray/xray_x86_64.cc +++ b/lib/xray/xray_x86_64.cc @@ -44,9 +44,9 @@ static bool readValueFromFile(const char *Filename, ssize_t BytesRead; bool Success; std::tie(BytesRead, Success) = retryingReadSome(Fd, Line, Line + BufSize); + close(Fd); if (!Success) return false; - close(Fd); char *End = nullptr; long long Tmp = internal_simple_strtoll(Line, &End, 10); bool Result = false; diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt index 893276767a..4b4fdf19d1 100644 --- a/test/asan/CMakeLists.txt +++ b/test/asan/CMakeLists.txt @@ -3,6 +3,12 @@ set(ASAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(ASAN_TESTSUITES) set(ASAN_DYNAMIC_TESTSUITES) +# FIXME: Shadow memory for 64-bit asan easily exhausts swap on most machines. +# Find a way to make these tests pass reliably, and re-enable them. +if(OS_NAME MATCHES "Windows" AND CMAKE_SIZEOF_VOID_P EQUAL 8) + set(EXCLUDE_FROM_ALL TRUE) +endif() + macro(get_bits_for_arch arch bits) if (${arch} MATCHES "i386|i686|arm|mips|mipsel") set(${bits} 32) @@ -16,7 +22,7 @@ endmacro() set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND ASAN_TEST_DEPS asan) - if(WIN32 AND COMPILER_RT_HAS_LLD_SOURCES) + if(WIN32 AND COMPILER_RT_HAS_LLD) list(APPEND ASAN_TEST_DEPS lld ) @@ -108,3 +114,9 @@ if(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME) set(EXCLUDE_FROM_ALL FALSE) endif() endif() + +# Reset EXCLUDE_FROM_ALL to its initial value. +# FIXME: Remove when we run Win64 asan tests. +if(OS_NAME MATCHES "Windows" AND CMAKE_SIZEOF_VOID_P EQUAL 8) + set(EXCLUDE_FROM_ALL FALSE) +endif() diff --git a/test/asan/TestCases/Darwin/scribble.cc b/test/asan/TestCases/Darwin/scribble.cc new file mode 100644 index 0000000000..33f64e19ca --- /dev/null +++ b/test/asan/TestCases/Darwin/scribble.cc @@ -0,0 +1,57 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NOSCRIBBLE %s +// RUN: env MallocScribble=1 MallocPreScribble=1 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s +// RUN: %env_asan_opts=max_free_fill_size=4096 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s + +#include +#include +#include +#include + +struct Isa { + const char *class_name; +}; + +struct MyClass { + long padding; + Isa *isa; + long data; + + void print_my_class_name(); +}; + +__attribute__((no_sanitize("address"))) +void MyClass::print_my_class_name() { + fprintf(stderr, "this = %p\n", this); + fprintf(stderr, "padding = 0x%lx\n", this->padding); + fprintf(stderr, "isa = %p\n", this->isa); + + if ((uint32_t)(uintptr_t)this->isa != 0x55555555) { + fprintf(stderr, "class name: %s\n", this->isa->class_name); + } +} + +int main() { + Isa *my_class_isa = (Isa *)malloc(sizeof(Isa)); + memset(my_class_isa, 0x77, sizeof(Isa)); + my_class_isa->class_name = "MyClass"; + + MyClass *my_object = (MyClass *)malloc(sizeof(MyClass)); + memset(my_object, 0x88, sizeof(MyClass)); + my_object->isa = my_class_isa; + my_object->data = 42; + + my_object->print_my_class_name(); + // CHECK-SCRIBBLE: class name: MyClass + // CHECK-NOSCRIBBLE: class name: MyClass + + free(my_object); + + my_object->print_my_class_name(); + // CHECK-NOSCRIBBLE: class name: MyClass + // CHECK-SCRIBBLE: isa = {{(0x)?}}{{5555555555555555|55555555}} + + fprintf(stderr, "okthxbai!\n"); + // CHECK-SCRIBBLE: okthxbai! + // CHECK-NOSCRIBBLE: okthxbai! +} diff --git a/test/asan/TestCases/Linux/asan_dlopen_test.cc b/test/asan/TestCases/Linux/asan_dlopen_test.cc index f1e31b0a05..5081b77532 100644 --- a/test/asan/TestCases/Linux/asan_dlopen_test.cc +++ b/test/asan/TestCases/Linux/asan_dlopen_test.cc @@ -2,6 +2,8 @@ // // RUN: %clangxx %s -DRT=\"%shared_libasan\" -o %t -ldl // RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=verify_asan_link_order=true not %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=verify_asan_link_order=false %run %t 2>&1 // REQUIRES: asan-dynamic-runtime // XFAIL: android diff --git a/test/asan/TestCases/Linux/coverage-missing.cc b/test/asan/TestCases/Linux/coverage-missing.cc index 49487d39a1..cb5d71a276 100644 --- a/test/asan/TestCases/Linux/coverage-missing.cc +++ b/test/asan/TestCases/Linux/coverage-missing.cc @@ -8,15 +8,15 @@ // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t // RUN: %sancov print *.sancov > main.txt // RUN: rm *.sancov -// RUN: [ $(cat main.txt | wc -l) == 1 ] +// RUN: count 1 < main.txt // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x // RUN: %sancov print *.sancov > foo.txt // RUN: rm *.sancov -// RUN: [ $(cat foo.txt | wc -l) == 3 ] +// RUN: count 3 < foo.txt // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x x // RUN: %sancov print *.sancov > bar.txt // RUN: rm *.sancov -// RUN: [ $(cat bar.txt | wc -l) == 4 ] +// RUN: count 4 < bar.txt // RUN: %sancov missing %t < foo.txt > foo-missing.txt // RUN: sort main.txt foo-missing.txt -o foo-missing-with-main.txt // The "missing from foo" set may contain a few bogus PCs from the sanitizer @@ -35,11 +35,11 @@ // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x // RUN: %sancov print $LIBNAME.*.sancov > foo.txt // RUN: rm *.sancov -// RUN: [ $(cat foo.txt | wc -l) == 2 ] +// RUN: count 2 < foo.txt // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x x // RUN: %sancov print $LIBNAME.*.sancov > bar.txt // RUN: rm *.sancov -// RUN: [ $(cat bar.txt | wc -l) == 3 ] +// RUN: count 3 < bar.txt // RUN: %sancov missing %dynamiclib < foo.txt > foo-missing.txt // RUN: ( diff bar.txt foo-missing.txt || true ) | not grep "^<" diff --git a/test/asan/TestCases/Linux/memmem_test.cc b/test/asan/TestCases/Linux/memmem_test.cc index 661381cdd7..a838cb56af 100644 --- a/test/asan/TestCases/Linux/memmem_test.cc +++ b/test/asan/TestCases/Linux/memmem_test.cc @@ -15,10 +15,10 @@ int main(int argc, char **argv) { // A1: AddressSanitizer: stack-buffer-overflow // A1: {{#0.*memmem}} // A1-NEXT: {{#1.*main}} - // A1: 'a1' <== Memory access at offset + // A1: 'a1'{{.*}} <== Memory access at offset // // A2: AddressSanitizer: stack-buffer-overflow // A2: {{#0.*memmem}} - // A2: 'a2' <== Memory access at offset + // A2: 'a2'{{.*}} <== Memory access at offset return res == NULL; } diff --git a/test/asan/TestCases/Linux/print_memory_profile_test.cc b/test/asan/TestCases/Linux/print_memory_profile_test.cc index 8909ccad08..e7896be405 100644 --- a/test/asan/TestCases/Linux/print_memory_profile_test.cc +++ b/test/asan/TestCases/Linux/print_memory_profile_test.cc @@ -3,8 +3,9 @@ // REQUIRES: leak-detection // // RUN: %clangxx_asan %s -o %t -// RUN: %run %t 100 2>&1 | FileCheck %s --check-prefix=CHECK-100 -// RUN: %run %t 50 2>&1 | FileCheck %s --check-prefix=CHECK-50 +// RUN: %run %t 100 10 2>&1 | FileCheck %s --check-prefix=CHECK-100-10 +// RUN: %run %t 100 1 2>&1 | FileCheck %s --check-prefix=CHECK-100-1 +// RUN: %run %t 50 10 2>&1 | FileCheck %s --check-prefix=CHECK-50-10 #include #include @@ -13,7 +14,7 @@ char *sink[1000]; int main(int argc, char **argv) { - if (argc < 2) + if (argc < 3) return 1; int idx = 0; @@ -22,12 +23,17 @@ int main(int argc, char **argv) { for (int i = 0; i < 28; i++) sink[idx++] = new char[24000]; - __sanitizer_print_memory_profile(atoi(argv[1])); + __sanitizer_print_memory_profile(atoi(argv[1]), atoi(argv[2])); } -// CHECK-100: Live Heap Allocations: {{.*}}; showing top 100% -// CHECK-100: 2227000 byte(s) ({{.*}}%) in 17 allocation(s) -// CHECK-100: 672000 byte(s) ({{.*}}%) in 28 allocation(s) -// CHECK-50: Live Heap Allocations: {{.*}}; showing top 50% -// CHECK-50: 2227000 byte(s) ({{.*}}%) in 17 allocation(s) -// CHECK-50-NOT: allocation +// CHECK-100-10: Live Heap Allocations: {{.*}}; showing top 100% (at most 10 unique contexts) +// CHECK-100-10: 2227000 byte(s) ({{.*}}%) in 17 allocation(s) +// CHECK-100-10: 672000 byte(s) ({{.*}}%) in 28 allocation(s) + +// CHECK-100-1: Live Heap Allocations: {{.*}}; showing top 100% (at most 1 unique contexts) +// CHECK-100-1: 2227000 byte(s) ({{.*}}%) in 17 allocation(s) +// CHECK-100-1-NOT: allocation + +// CHECK-50-10: Live Heap Allocations: {{.*}}; showing top 50% (at most 10 unique contexts) +// CHECK-50-10: 2227000 byte(s) ({{.*}}%) in 17 allocation(s) +// CHECK-50-10-NOT: allocation diff --git a/test/asan/TestCases/Posix/asan-sigbus.cpp b/test/asan/TestCases/Posix/asan-sigbus.cpp new file mode 100644 index 0000000000..e07392b4cd --- /dev/null +++ b/test/asan/TestCases/Posix/asan-sigbus.cpp @@ -0,0 +1,40 @@ +// Check handle_bus flag +// Defaults to true +// RUN: %clangxx_asan -std=c++11 %s -o %t +// RUN: not %run %t %T/file 2>&1 | FileCheck %s -check-prefix=CHECK-BUS +// RUN: %env_asan_opts=handle_sigbus=false not --crash %run %t %T/file 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include + +char array[4096]; +int main(int argc, char **argv) { + assert(argc > 1); + int fd = open(argv[1], O_RDWR | O_CREAT, 0700); + if (fd < 0) { + perror("open"); + exit(1); + } + assert(write(fd, array, sizeof(array)) == sizeof(array)); + + // Write some zeroes to the file, then mmap it while it has a 4KiB size + char *addr = (char *)mmap(nullptr, sizeof(array), PROT_READ, + MAP_FILE | MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + // Truncate the file so our memory isn't valid any more + assert(ftruncate(fd, 0) == 0); + + // Try to access the memory + return addr[42]; + // CHECK-NOT: DEADLYSIGNAL + // CHECK-BUS: DEADLYSIGNAL + // CHECK-BUS: ERROR: AddressSanitizer: BUS +} diff --git a/test/asan/TestCases/Posix/closed-fds.cc b/test/asan/TestCases/Posix/closed-fds.cc index b7bca26c30..75e3216aab 100644 --- a/test/asan/TestCases/Posix/closed-fds.cc +++ b/test/asan/TestCases/Posix/closed-fds.cc @@ -2,7 +2,8 @@ // symbolizer still works. // RUN: rm -f %t.log.* -// RUN: %clangxx_asan -O0 %s -o %t 2>&1 && %env_asan_opts=log_path='"%t.log"':verbosity=2 not %run %t 2>&1 +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: %env_asan_opts=log_path='"%t.log"':verbosity=2 not %run %t // RUN: FileCheck %s --check-prefix=CHECK-FILE < %t.log.* // FIXME: copy %t.log back from the device and re-enable on Android. diff --git a/test/asan/TestCases/Posix/coverage-maybe-open-file.cc b/test/asan/TestCases/Posix/coverage-maybe-open-file.cc index cab3d5770a..95f2b5449e 100644 --- a/test/asan/TestCases/Posix/coverage-maybe-open-file.cc +++ b/test/asan/TestCases/Posix/coverage-maybe-open-file.cc @@ -6,7 +6,7 @@ // RUN: mkdir -p %T/coverage-maybe-open-file && cd %T/coverage-maybe-open-file // RUN: %env_asan_opts=coverage=1 %run %t | FileCheck %s --check-prefix=CHECK-success // RUN: %env_asan_opts=coverage=0 %run %t | FileCheck %s --check-prefix=CHECK-fail -// RUN: [ "$(cat test.sancov.packed)" == "test" ] +// RUN: FileCheck %s < test.sancov.packed -implicit-check-not={{.}} --check-prefix=CHECK-test // RUN: cd .. && rm -rf %T/coverage-maybe-open-file #include @@ -30,3 +30,4 @@ int main(int argc, char **argv) { // CHECK-success: SUCCESS // CHECK-fail: FAIL +// CHECK-test: {{^}}test{{$}} diff --git a/test/asan/TestCases/Posix/coverage.cc b/test/asan/TestCases/Posix/coverage.cc index 7c1c4949f6..3d1dccfbd5 100644 --- a/test/asan/TestCases/Posix/coverage.cc +++ b/test/asan/TestCases/Posix/coverage.cc @@ -2,15 +2,15 @@ // RUN: %clangxx_asan -fsanitize-coverage=func %s %ld_flags_rpath_exe -o %t // RUN: rm -rf %T/coverage && mkdir -p %T/coverage && cd %T/coverage // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-main -// RUN: %sancov print `ls coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 +// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo -// RUN: %sancov print `ls coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 +// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar -// RUN: %sancov print `ls *coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 +// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar -// RUN: %sancov print `ls *coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 -// RUN: %sancov print `ls *coverage.*sancov | grep '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 -// RUN: %sancov merge `ls *coverage.*sancov | grep -v '.so'` > merged-cov +// RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 +// RUN: %sancov print libcoverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 +// RUN: %sancov merge coverage.*sancov > merged-cov // RUN: %sancov print merged-cov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4 2>&1 | FileCheck %s --check-prefix=CHECK-report // RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4 5 2>&1 | FileCheck %s --check-prefix=CHECK-segv diff --git a/test/asan/TestCases/Posix/fread_fwrite.cc b/test/asan/TestCases/Posix/fread_fwrite.cc new file mode 100644 index 0000000000..97d44b7528 --- /dev/null +++ b/test/asan/TestCases/Posix/fread_fwrite.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_asan -g %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FWRITE +// RUN: not %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-FREAD + +#include +#include + +int test_fread() { + FILE *f = fopen("/dev/zero", "r"); + char buf[2]; + fread(buf, sizeof(buf), 2, f); // BOOM + fclose(f); + return 0; +} + +int test_fwrite() { + FILE *f = fopen("/dev/null", "w"); + char buf[2]; + fwrite(buf, sizeof(buf), 2, f); // BOOM + return fclose(f); +} + +int main(int argc, char *argv[]) { + if (argc > 1) + test_fread(); + else + test_fwrite(); + return 0; +} + +// CHECK-FREAD: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}} +// CHECK-FREAD: #{{.*}} in {{(wrap_|__interceptor_)?}}fread +// CHECK-FWRITE: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}} +// CHECK-FWRITE: #{{.*}} in {{(wrap_|__interceptor_)?}}fwrite diff --git a/test/asan/TestCases/Posix/halt_on_error-torture.cc b/test/asan/TestCases/Posix/halt_on_error-torture.cc index 5d7eff06e3..1b26173d71 100644 --- a/test/asan/TestCases/Posix/halt_on_error-torture.cc +++ b/test/asan/TestCases/Posix/halt_on_error-torture.cc @@ -5,7 +5,7 @@ // RUN: rm -f 1.txt // RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t 1 10 >>1.txt 2>&1 // RUN: FileCheck %s < 1.txt -// RUN: [ $(grep -c 'ERROR: AddressSanitizer: use-after-poison' 1.txt) -eq 10 ] +// RUN: grep 'ERROR: AddressSanitizer: use-after-poison' 1.txt | count 10 // RUN: FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt // // Collisions are unlikely but still possible so we need the ||. diff --git a/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc b/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc index 98ef851657..b9d85ef94b 100644 --- a/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc +++ b/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc @@ -8,7 +8,7 @@ // Check that we die after reaching different reports number threshold. // RUN: rm -f %t1.log // RUN: %env_asan_opts=halt_on_error=false not %run %t 1 >> %t1.log 2>&1 -// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' %t1.log) -eq 25 ] +// RUN: grep 'ERROR: AddressSanitizer: stack-buffer-overflow' %t1.log | count 25 // // Check suppress_equal_pcs=true behavior is equal to default one. // RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=true %run %t 2>&1 | FileCheck %s @@ -16,7 +16,7 @@ // Check suppress_equal_pcs=false behavior isn't equal to default one. // RUN: rm -f %t2.log // RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t >> %t2.log 2>&1 -// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' %t2.log) -eq 30 ] +// RUN: grep 'ERROR: AddressSanitizer: stack-buffer-overflow' %t2.log | count 30 #define ACCESS_ARRAY_FIVE_ELEMENTS(array, i) \ array[i] = i; \ diff --git a/test/asan/TestCases/Posix/stack-use-after-return.cc b/test/asan/TestCases/Posix/stack-use-after-return.cc index cf114be97d..822d5be949 100644 --- a/test/asan/TestCases/Posix/stack-use-after-return.cc +++ b/test/asan/TestCases/Posix/stack-use-after-return.cc @@ -4,7 +4,7 @@ // RUN: %clangxx_asan -O3 %s -pthread -o %t && %env_asan_opts=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck %s // RUN: %env_asan_opts=detect_stack_use_after_return=0 %run %t // Regression test for a CHECK failure with small stack size and large frame. -// RUN: %clangxx_asan -O3 %s -pthread -o %t -DkSize=10000 -DUseThread -DkStackSize=65536 && %env_asan_opts=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck --check-prefix=THREAD %s +// RUN: %clangxx_asan -O3 %s -pthread -o %t -DkSize=10000 -DUseThread -DkStackSize=131072 && %env_asan_opts=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck --check-prefix=THREAD %s // // Test that we can find UAR in a thread other than main: // RUN: %clangxx_asan -DUseThread -O2 %s -pthread -o %t && %env_asan_opts=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck --check-prefix=THREAD %s @@ -14,8 +14,13 @@ // RUN: %env_asan_opts=detect_stack_use_after_return=1:max_uar_stack_size_log=20:verbosity=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-20 %s // RUN: %env_asan_opts=detect_stack_use_after_return=1:min_uar_stack_size_log=24:max_uar_stack_size_log=24:verbosity=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-24 %s -#include +// This test runs out of stack on AArch64. +// UNSUPPORTED: aarch64 + +#include #include +#include +#include #ifndef kSize # define kSize 1 @@ -48,11 +53,11 @@ void Func2(char *x) { // CHECK: WRITE of size 1 {{.*}} thread T0 // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] // CHECK: is located in stack of thread T0 at offset - // CHECK: 'local' <== Memory access at offset {{16|32}} is inside this variable + // CHECK: 'local'{{.*}} <== Memory access at offset {{16|32}} is inside this variable // THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}} // THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]] // THREAD: is located in stack of thread T{{[1-9]}} at offset - // THREAD: 'local' <== Memory access at offset {{16|32}} is inside this variable + // THREAD: 'local'{{.*}} <== Memory access at offset {{16|32}} is inside this variable // CHECK-20: T0: FakeStack created:{{.*}} stack_size_log: 20 // CHECK-24: T0: FakeStack created:{{.*}} stack_size_log: 24 } @@ -66,8 +71,31 @@ int main(int argc, char **argv) { #if UseThread pthread_attr_t attr; pthread_attr_init(&attr); - if (kStackSize > 0) - pthread_attr_setstacksize(&attr, kStackSize); + if (kStackSize > 0) { + size_t desired_stack_size = kStackSize; + if (desired_stack_size < PTHREAD_STACK_MIN) { + desired_stack_size = PTHREAD_STACK_MIN; + } + + int ret = pthread_attr_setstacksize(&attr, desired_stack_size); + if (ret != 0) { + fprintf(stderr, "pthread_attr_setstacksize returned %d\n", ret); + abort(); + } + + size_t stacksize_check; + ret = pthread_attr_getstacksize(&attr, &stacksize_check); + if (ret != 0) { + fprintf(stderr, "pthread_attr_getstacksize returned %d\n", ret); + abort(); + } + + if (stacksize_check != desired_stack_size) { + fprintf(stderr, "Unable to set stack size to %d, the stack size is %d.\n", + desired_stack_size, stacksize_check); + abort(); + } + } pthread_t t; pthread_create(&t, &attr, Thread, 0); pthread_attr_destroy(&attr); diff --git a/test/asan/TestCases/Windows/dll_intercept_memchr.cc b/test/asan/TestCases/Windows/dll_intercept_memchr.cc index 4f794a2127..6360cec87e 100644 --- a/test/asan/TestCases/Windows/dll_intercept_memchr.cc +++ b/test/asan/TestCases/Windows/dll_intercept_memchr.cc @@ -22,6 +22,6 @@ int test_function() { // CHECK-NEXT: test_function {{.*}}dll_intercept_memchr.cc:[[@LINE-5]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame // CHECK-NEXT: test_function {{.*}}dll_intercept_memchr.cc -// CHECK: 'buff' <== Memory access at offset {{.*}} overflows this variable +// CHECK: 'buff'{{.*}} <== Memory access at offset {{.*}} overflows this variable return 0; } diff --git a/test/asan/TestCases/Windows/dll_intercept_memcpy.cc b/test/asan/TestCases/Windows/dll_intercept_memcpy.cc index 736e6969d5..a5981fa5b2 100644 --- a/test/asan/TestCases/Windows/dll_intercept_memcpy.cc +++ b/test/asan/TestCases/Windows/dll_intercept_memcpy.cc @@ -27,6 +27,6 @@ int test_function() { // CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy.cc:[[@LINE-4]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame // CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy.cc -// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable +// CHECK: 'buff2'{{.*}} <== Memory access at offset {{.*}} overflows this variable return 0; } diff --git a/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc b/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc index 4e28905923..f05ee21211 100644 --- a/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc +++ b/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc @@ -29,6 +29,6 @@ int test_function() { // CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy_indirect.cc:[[@LINE-5]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame // CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy_indirect.cc -// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable +// CHECK: 'buff2'{{.*}} <== Memory access at offset {{.*}} overflows this variable return 0; } diff --git a/test/asan/TestCases/Windows/dll_intercept_memset.cc b/test/asan/TestCases/Windows/dll_intercept_memset.cc index d4be376f24..4baa0a1610 100644 --- a/test/asan/TestCases/Windows/dll_intercept_memset.cc +++ b/test/asan/TestCases/Windows/dll_intercept_memset.cc @@ -27,6 +27,6 @@ int test_function() { // CHECK-NEXT: test_function {{.*}}dll_intercept_memset.cc:[[@LINE-4]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame // CHECK-NEXT: test_function {{.*}}dll_intercept_memset.cc -// CHECK: 'buff' <== Memory access at offset {{.*}} overflows this variable +// CHECK: 'buff'{{.*}} <== Memory access at offset {{.*}} overflows this variable return 0; } diff --git a/test/asan/TestCases/Windows/dll_noreturn.cc b/test/asan/TestCases/Windows/dll_noreturn.cc index 8b5e3d0058..2f6f0c755c 100644 --- a/test/asan/TestCases/Windows/dll_noreturn.cc +++ b/test/asan/TestCases/Windows/dll_noreturn.cc @@ -17,7 +17,7 @@ void noreturn_f() { // // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: noreturn_f{{.*}}dll_noreturn.cc -// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable +// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] underflows this variable // CHECK-LABEL: SUMMARY } diff --git a/test/asan/TestCases/Windows/dll_poison_unpoison.cc b/test/asan/TestCases/Windows/dll_poison_unpoison.cc index 9b25a126ef..6bd58eca21 100644 --- a/test/asan/TestCases/Windows/dll_poison_unpoison.cc +++ b/test/asan/TestCases/Windows/dll_poison_unpoison.cc @@ -30,6 +30,6 @@ int test_function() { // // CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: test_function{{.*}}\dll_poison_unpoison.cc -// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] is inside this variable +// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] is inside this variable return 0; } diff --git a/test/asan/TestCases/Windows/dll_stack_use_after_return.cc b/test/asan/TestCases/Windows/dll_stack_use_after_return.cc index 6428718469..b6166d6812 100644 --- a/test/asan/TestCases/Windows/dll_stack_use_after_return.cc +++ b/test/asan/TestCases/Windows/dll_stack_use_after_return.cc @@ -22,7 +22,7 @@ int test_function() { // // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: #0 {{.*}} foo{{.*}}dll_stack_use_after_return.cc -// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable +// CHECK: 'stack_buffer'{{.*}} <== Memory access at offset [[OFFSET]] is inside this variable return 0; } diff --git a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc index dc7c7c6ad7..75a094e548 100644 --- a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc +++ b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc @@ -16,7 +16,7 @@ DWORD WINAPI thread_proc(void *context) { // CHECK: Address [[ADDR]] is located in stack of thread T1 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: thread_proc{{.*}}dll_thread_stack_array_left_oob.cc // -// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] underflows this variable +// CHECK: 'stack_buffer'{{.*}} <== Memory access at offset [[OFFSET]] underflows this variable return 0; } diff --git a/test/asan/TestCases/Windows/fuse-lld.cc b/test/asan/TestCases/Windows/fuse-lld.cc index 76c36d828f..7fa5d4e8a8 100644 --- a/test/asan/TestCases/Windows/fuse-lld.cc +++ b/test/asan/TestCases/Windows/fuse-lld.cc @@ -5,8 +5,8 @@ // FIXME: Use -fuse-ld=lld after the old COFF linker is removed. // FIXME: Test will fail until we add flags for requesting dwarf or cv. // RUNX: %clangxx_asan -O2 %s -o %t.exe -fuse-ld=lld -Wl,-debug -// RUN: %clangxx_asan -c -O2 %s -o %t.o -gdwarf -// RUN: lld-link %t.o -out:%t.exe -debug -defaultlib:libcmt %asan_lib %asan_cxx_lib +// RUN: %clangxx_asan -c -O2 %s -o %t.o -g -gdwarf +// RUN: lld-link %t.o -out:%t.exe -debug -nopdb -defaultlib:libcmt %asan_lib %asan_cxx_lib // RUN: not %run %t.exe 2>&1 | FileCheck %s #include diff --git a/test/asan/TestCases/Windows/intercept_memcpy.cc b/test/asan/TestCases/Windows/intercept_memcpy.cc index 6e45e7fc6b..d71333d0b7 100644 --- a/test/asan/TestCases/Windows/intercept_memcpy.cc +++ b/test/asan/TestCases/Windows/intercept_memcpy.cc @@ -27,5 +27,5 @@ int main() { // CHECK-NEXT: main {{.*}}intercept_memcpy.cc:[[@LINE-5]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame // CHECK-NEXT: #0 {{.*}} main -// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable +// CHECK: 'buff2'{{.*}} <== Memory access at offset {{.*}} overflows this variable } diff --git a/test/asan/TestCases/Windows/intercept_strlen.cc b/test/asan/TestCases/Windows/intercept_strlen.cc index 928a286bed..938e6c9b5b 100644 --- a/test/asan/TestCases/Windows/intercept_strlen.cc +++ b/test/asan/TestCases/Windows/intercept_strlen.cc @@ -22,6 +22,6 @@ int main() { // CHECK-NEXT: main {{.*}}intercept_strlen.cc:[[@LINE-5]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame // CHECK-NEXT: main {{.*}}intercept_strlen.cc -// CHECK: 'str' <== Memory access at offset {{.*}} overflows this variable +// CHECK: 'str'{{.*}} <== Memory access at offset {{.*}} overflows this variable return len < 6; } diff --git a/test/asan/TestCases/Windows/stack_array_left_oob.cc b/test/asan/TestCases/Windows/stack_array_left_oob.cc index 845a1f3326..8d601fc8d4 100644 --- a/test/asan/TestCases/Windows/stack_array_left_oob.cc +++ b/test/asan/TestCases/Windows/stack_array_left_oob.cc @@ -12,5 +12,5 @@ int main() { // CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}} -// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable +// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] underflows this variable } diff --git a/test/asan/TestCases/Windows/stack_array_right_oob.cc b/test/asan/TestCases/Windows/stack_array_right_oob.cc index a370246aa0..721834d1ad 100644 --- a/test/asan/TestCases/Windows/stack_array_right_oob.cc +++ b/test/asan/TestCases/Windows/stack_array_right_oob.cc @@ -12,5 +12,5 @@ int main() { // CHECK-NEXT: {{#0 .* main .*stack_array_right_oob.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: {{#0 .* main .*stack_array_right_oob.cc}} -// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] overflows this variable +// CHECK: 'buffer'{{.*}} <== Memory access at offset [[OFFSET]] overflows this variable } diff --git a/test/asan/TestCases/Windows/stack_use_after_return.cc b/test/asan/TestCases/Windows/stack_use_after_return.cc index 9c31922af1..ca1c142af2 100644 --- a/test/asan/TestCases/Windows/stack_use_after_return.cc +++ b/test/asan/TestCases/Windows/stack_use_after_return.cc @@ -18,5 +18,5 @@ int main() { // CHECK: is located in stack of thread T0 at offset [[OFFSET:.*]] in frame // CHECK-NEXT: {{#0 0x.* in foo.*stack_use_after_return.cc}} // -// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable +// CHECK: 'stack_buffer'{{.*}} <== Memory access at offset [[OFFSET]] is inside this variable } diff --git a/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc b/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc index 2859ecc521..7848cf3be9 100644 --- a/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc +++ b/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc @@ -20,7 +20,7 @@ int main(void) { // CHECK-NEXT: {{#0 0x[0-9a-f]* in main .*wrong_downcast_on_stack.cc}}:[[@LINE-3]] // CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:[0-9]+]] in frame // CHECK-NEXT: {{#0 0x[0-9a-f]* in main }} -// CHECK: 'p' <== Memory access at offset [[OFFSET]] overflows this variable +// CHECK: 'p'{{.*}} <== Memory access at offset [[OFFSET]] overflows this variable return 0; } diff --git a/test/asan/TestCases/invalid-pointer-pairs.cc b/test/asan/TestCases/invalid-pointer-pairs.cc index b36e6cd9c1..e1df151d13 100644 --- a/test/asan/TestCases/invalid-pointer-pairs.cc +++ b/test/asan/TestCases/invalid-pointer-pairs.cc @@ -13,10 +13,10 @@ int f(char c, char *p, char *q) { // [[PTR1:0x[0-9a-f]+]] [[PTR2:0x[0-9a-f]+]] switch (c) { case 'g': - // CMP: #{{[0-9]+ .*}} in f({{char, char\*, char\*|char,char \*,char \*}}) {{.*}}invalid-pointer-pairs.cc:[[@LINE+1]]:14 + // CMP: #{{[0-9]+ .*}} in f({{char, char\*, char\*|char,char \*,char \*}}) {{.*}}invalid-pointer-pairs.cc:[[@LINE+1]] return p > q; case 's': - // SUB: #{{[0-9]+ .*}} in f({{char, char\*, char\*|char,char \*,char \*}}) {{.*}}invalid-pointer-pairs.cc:[[@LINE+1]]:14 + // SUB: #{{[0-9]+ .*}} in f({{char, char\*, char\*|char,char \*,char \*}}) {{.*}}invalid-pointer-pairs.cc:[[@LINE+1]] return p - q; case 'k': { // OK-NOT: ERROR @@ -26,7 +26,7 @@ int f(char c, char *p, char *q) { case 'f': { char *p3 = p + 20; free(p); - // FREE: #{{[0-9]+ .*}} in f({{char, char\*, char\*|char,char \*,char \*}}) {{.*}}invalid-pointer-pairs.cc:[[@LINE+2]]:14 + // FREE: #{{[0-9]+ .*}} in f({{char, char\*, char\*|char,char \*,char \*}}) {{.*}}invalid-pointer-pairs.cc:[[@LINE+2]] // FREE: freed by thread return p < p3; } diff --git a/test/asan/TestCases/realloc.cc b/test/asan/TestCases/realloc.cc new file mode 100644 index 0000000000..fcf383b1a7 --- /dev/null +++ b/test/asan/TestCases/realloc.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// Default is true (free on realloc to 0 size) +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=allocator_frees_and_returns_null_on_realloc_zero=true %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=allocator_frees_and_returns_null_on_realloc_zero=false %run %t 2>&1 | FileCheck %s --check-prefix=NO-FREE + +#include +#include + +int main() { + void *p = malloc(42); + p = realloc(p, 0); + if (p) { + // NO-FREE: Allocated something on realloc(p, 0) + fprintf(stderr, "Allocated something on realloc(p, 0)\n"); + } else { + // CHECK: realloc(p, 0) returned nullptr + fprintf(stderr, "realloc(p, 0) returned nullptr\n"); + } + free(p); +} diff --git a/test/asan/TestCases/stack-buffer-overflow-with-position.cc b/test/asan/TestCases/stack-buffer-overflow-with-position.cc index 88f5825baf..2a575f7755 100644 --- a/test/asan/TestCases/stack-buffer-overflow-with-position.cc +++ b/test/asan/TestCases/stack-buffer-overflow-with-position.cc @@ -30,15 +30,15 @@ int main(int argc, char **argv) { // make sure BBB and CCC are not removed; return *(short*)(p) + BBB[argc % 2] + CCC[argc % 2]; } -// CHECK-m2: 'AAA' <== {{.*}}underflows this variable -// CHECK-m1: 'AAA' <== {{.*}}partially underflows this variable -// CHECK-9: 'AAA' <== {{.*}}partially overflows this variable -// CHECK-10: 'AAA' <== {{.*}}overflows this variable -// CHECK-30: 'BBB' <== {{.*}}underflows this variable -// CHECK-31: 'BBB' <== {{.*}}partially underflows this variable -// CHECK-41: 'BBB' <== {{.*}}partially overflows this variable -// CHECK-42: 'BBB' <== {{.*}}overflows this variable -// CHECK-62: 'CCC' <== {{.*}}underflows this variable -// CHECK-63: 'CCC' <== {{.*}}partially underflows this variable -// CHECK-73: 'CCC' <== {{.*}}partially overflows this variable -// CHECK-74: 'CCC' <== {{.*}}overflows this variable +// CHECK-m2: 'AAA'{{.*}} <== {{.*}}underflows this variable +// CHECK-m1: 'AAA'{{.*}} <== {{.*}}partially underflows this variable +// CHECK-9: 'AAA'{{.*}} <== {{.*}}partially overflows this variable +// CHECK-10: 'AAA'{{.*}} <== {{.*}}overflows this variable +// CHECK-30: 'BBB'{{.*}} <== {{.*}}underflows this variable +// CHECK-31: 'BBB'{{.*}} <== {{.*}}partially underflows this variable +// CHECK-41: 'BBB'{{.*}} <== {{.*}}partially overflows this variable +// CHECK-42: 'BBB'{{.*}} <== {{.*}}overflows this variable +// CHECK-62: 'CCC'{{.*}} <== {{.*}}underflows this variable +// CHECK-63: 'CCC'{{.*}} <== {{.*}}partially underflows this variable +// CHECK-73: 'CCC'{{.*}} <== {{.*}}partially overflows this variable +// CHECK-74: 'CCC'{{.*}} <== {{.*}}overflows this variable diff --git a/test/asan/TestCases/strcasestr-1.c b/test/asan/TestCases/strcasestr-1.c index c38871ea53..dccfbcde77 100644 --- a/test/asan/TestCases/strcasestr-1.c +++ b/test/asan/TestCases/strcasestr-1.c @@ -19,7 +19,7 @@ int main(int argc, char **argv) { char s1[4] = "abC"; __asan_poison_memory_region ((char *)&s1[2], 2); r = strcasestr(s1, s2); - // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == s1 + 2); return 0; } diff --git a/test/asan/TestCases/strcasestr-2.c b/test/asan/TestCases/strcasestr-2.c index 47fd69225d..70de2dda43 100644 --- a/test/asan/TestCases/strcasestr-2.c +++ b/test/asan/TestCases/strcasestr-2.c @@ -20,6 +20,6 @@ int main(int argc, char **argv) { __asan_poison_memory_region ((char *)&s2[2], 2); r = strcasestr(s1, s2); assert(r == 0); - // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable return 0; } diff --git a/test/asan/TestCases/strcspn-1.c b/test/asan/TestCases/strcspn-1.c index 6cda2e210c..2a9f7d7fbd 100644 --- a/test/asan/TestCases/strcspn-1.c +++ b/test/asan/TestCases/strcspn-1.c @@ -14,7 +14,7 @@ int main(int argc, char **argv) { char s1[4] = "caB"; __asan_poison_memory_region ((char *)&s1[2], 2); r = strcspn(s1, s2); - // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == 1); return 0; } diff --git a/test/asan/TestCases/strcspn-2.c b/test/asan/TestCases/strcspn-2.c index 8bb4b8a57e..a51fb911e8 100644 --- a/test/asan/TestCases/strcspn-2.c +++ b/test/asan/TestCases/strcspn-2.c @@ -14,7 +14,7 @@ int main(int argc, char **argv) { char s2[4] = "abc"; __asan_poison_memory_region ((char *)&s2[2], 2); r = strcspn(s1, s2); - // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == 0); return 0; } diff --git a/test/asan/TestCases/strpbrk-1.c b/test/asan/TestCases/strpbrk-1.c index 626e8777e1..eb32326782 100644 --- a/test/asan/TestCases/strpbrk-1.c +++ b/test/asan/TestCases/strpbrk-1.c @@ -14,7 +14,7 @@ int main(int argc, char **argv) { char s1[4] = "cab"; __asan_poison_memory_region ((char *)&s1[2], 2); r = strpbrk(s1, s2); - // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == s1 + 1); return 0; } diff --git a/test/asan/TestCases/strpbrk-2.c b/test/asan/TestCases/strpbrk-2.c index 29f3150e66..1f24656dcb 100644 --- a/test/asan/TestCases/strpbrk-2.c +++ b/test/asan/TestCases/strpbrk-2.c @@ -14,7 +14,7 @@ int main(int argc, char **argv) { char s2[4] = "bca"; __asan_poison_memory_region ((char *)&s2[2], 2); r = strpbrk(s1, s2); - // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == s1); return 0; } diff --git a/test/asan/TestCases/strspn-1.c b/test/asan/TestCases/strspn-1.c index b0c40ea4d7..5ddb14f507 100644 --- a/test/asan/TestCases/strspn-1.c +++ b/test/asan/TestCases/strspn-1.c @@ -14,7 +14,7 @@ int main(int argc, char **argv) { char s1[4] = "acb"; __asan_poison_memory_region ((char *)&s1[2], 2); r = strspn(s1, s2); - // CHECK:'s1' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == 1); return 0; } diff --git a/test/asan/TestCases/strspn-2.c b/test/asan/TestCases/strspn-2.c index 4c899108de..d564ef8aef 100644 --- a/test/asan/TestCases/strspn-2.c +++ b/test/asan/TestCases/strspn-2.c @@ -14,7 +14,7 @@ int main(int argc, char **argv) { char s2[5] = "abcd"; __asan_poison_memory_region ((char *)&s2[3], 2); r = strspn(s1, s2); - // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r >= 2); return 0; } diff --git a/test/asan/TestCases/strstr-1.c b/test/asan/TestCases/strstr-1.c index 06a8a8a55d..319cff546d 100644 --- a/test/asan/TestCases/strstr-1.c +++ b/test/asan/TestCases/strstr-1.c @@ -15,7 +15,7 @@ int main(int argc, char **argv) { char s1[4] = "acb"; __asan_poison_memory_region ((char *)&s1[2], 2); r = strstr(s1, s2); - // CHECK:'s1' <== Memory access at offset {{[0-9]+}} {{partially overflows this variable|is inside this variable}} + // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} {{partially overflows this variable|is inside this variable}} assert(r == s1 + 1); return 0; } diff --git a/test/asan/TestCases/strstr-2.c b/test/asan/TestCases/strstr-2.c index 8bc6e9902d..4d00c6e632 100644 --- a/test/asan/TestCases/strstr-2.c +++ b/test/asan/TestCases/strstr-2.c @@ -15,7 +15,7 @@ int main(int argc, char **argv) { char s2[4] = "cab"; __asan_poison_memory_region ((char *)&s2[2], 2); r = strstr(s1, s2); - // CHECK:'s2' <== Memory access at offset {{[0-9]+}} partially overflows this variable + // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable assert(r == 0); return 0; } diff --git a/test/asan/TestCases/strtok.c b/test/asan/TestCases/strtok.c new file mode 100644 index 0000000000..e1eee89ee7 --- /dev/null +++ b/test/asan/TestCases/strtok.c @@ -0,0 +1,103 @@ +// RUN: %clang_asan %s -o %t + +// Test overflows with strict_string_checks + +// RUN: %env_asan_opts=strict_string_checks=true not %run %t test1 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK1 +// RUN: %env_asan_opts=intercept_strtok=false%run %t test1 2>&1 +// RUN: %env_asan_opts=strict_string_checks=true not %run %t test2 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK2 +// RUN: %env_asan_opts=intercept_strtok=false %run %t test2 2>&1 +// RUN: %env_asan_opts=strict_string_checks=true not %run %t test3 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK3 +// RUN: %env_asan_opts=intercept_strtok=false %run %t test3 2>&1 +// RUN: %env_asan_opts=strict_string_checks=true %run %t test4 2>&1 +// RUN: %env_asan_opts=intercept_strtok=false %run %t test4 2>&1 + +// Test overflows with !strict_string_checks +// RUN: %env_asan_opts=strict_string_checks=false not %run %t test5 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK5 +// RUN: %env_asan_opts=intercept_strtok=false %run %t test5 2>&1 +// RUN: %env_asan_opts=strict_string_checks=false not %run %t test6 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK6 +// RUN: %env_asan_opts=intercept_strtok=false %run %t test6 2>&1 + + +#include +#include +#include + +// Check that we find overflows in the delimiters on the first call +// with strict_string_checks. +void test1() { + char *token; + char s[4] = "abc"; + char token_delimiter[2] = "b"; + __asan_poison_memory_region ((char *)&token_delimiter[1], 2); + token = strtok(s, token_delimiter); + // CHECK1: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable +} + +// Check that we find overflows in the delimiters on the second call (str == NULL) +// with strict_string_checks. +void test2() { + char *token; + char s[4] = "abc"; + char token_delimiter[2] = "b"; + token = strtok(s, token_delimiter); + assert(strcmp(token, "a") == 0); + __asan_poison_memory_region ((char *)&token_delimiter[1], 2); + token = strtok(NULL, token_delimiter); + // CHECK2: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable +} + +// Check that we find overflows in the string (only on the first call) with strict_string_checks. +void test3() { + char *token; + char s[4] = "abc"; + char token_delimiter[2] = "b"; + __asan_poison_memory_region ((char *)&s[3], 2); + token = strtok(s, token_delimiter); + // CHECK3: 's'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable +} + +// Check that we do not crash when strtok returns NULL with strict_string_checks. +void test4() { + char *token; + char s[] = ""; + char token_delimiter[] = "a"; + token = strtok(s, token_delimiter); + assert(token == NULL); +} + +// Check that we find overflows in the string (only on the first call) with !strict_string_checks. +void test5() { + char *token; + char s[4] = "abc"; + char token_delimiter[2] = "d"; + __asan_poison_memory_region ((char *)&s[2], 2); + __asan_poison_memory_region ((char *)&token_delimiter[1], 2); + token = strtok(s, token_delimiter); + // CHECK5: 's'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable +} + +// Check that we find overflows in the delimiters (only on the first call) with !strict_string_checks. +void test6() { + char *token; + char s[4] = "abc"; + char token_delimiter[1] = {'d'}; + __asan_poison_memory_region ((char *)&token_delimiter[1], 2); + token = strtok(s, &token_delimiter[1]); + // CHECK6: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} overflows this variable +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(); + if (!strcmp(argv[1], "test2")) test2(); + if (!strcmp(argv[1], "test3")) test3(); + if (!strcmp(argv[1], "test4")) test4(); + if (!strcmp(argv[1], "test5")) test5(); + if (!strcmp(argv[1], "test6")) test6(); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope-inlined.cc b/test/asan/TestCases/use-after-scope-inlined.cc index 98a455cbc0..bed9814c8b 100644 --- a/test/asan/TestCases/use-after-scope-inlined.cc +++ b/test/asan/TestCases/use-after-scope-inlined.cc @@ -21,8 +21,8 @@ int main(int argc, char *argv[]) { // CHECK: READ of size 4 at 0x{{.*}} thread T0 // CHECK: #0 0x{{.*}} in main // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] - // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset - // CHECK: [[OFFSET:[^ ]*]] in frame - // CHECK: main - // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i:[[@LINE-15]]' + // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset [[OFFSET:[^ ]*]] in frame + // CHECK: {{.*}} in main + // CHECK: This frame has + // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' (line [[@LINE-15]]) } diff --git a/test/asan/TestCases/use-after-scope.cc b/test/asan/TestCases/use-after-scope.cc index d92dae6572..4c5998abe2 100644 --- a/test/asan/TestCases/use-after-scope.cc +++ b/test/asan/TestCases/use-after-scope.cc @@ -1,6 +1,10 @@ // RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s +// -fsanitize-address-use-after-scope is now on by default: +// RUN: %clangxx_asan -O1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + volatile int *p = 0; int main() { diff --git a/test/builtins/CMakeLists.txt b/test/builtins/CMakeLists.txt index 443e552f81..f37e46d3a2 100644 --- a/test/builtins/CMakeLists.txt +++ b/test/builtins/CMakeLists.txt @@ -9,6 +9,31 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ) +#Unit tests. + +include(builtin-config-ix) + +#TODO: Add support for Apple. +if (NOT APPLE) +foreach(arch ${BUILTIN_SUPPORTED_ARCH}) + set(BUILTINS_TEST_TARGET_ARCH ${arch}) + string(TOLOWER "-${arch}-${OS_NAME}" BUILTINS_TEST_CONFIG_SUFFIX) + get_test_cc_for_arch(${arch} BUILTINS_TEST_TARGET_CC BUILTINS_TEST_TARGET_CFLAGS) + if (${arch} STREQUAL "armhf") + list(APPEND BUILTINS_TEST_TARGET_CFLAGS -fomit-frame-pointer -DCOMPILER_RT_ARMHF_TARGET) + string(REPLACE ";" " " BUILTINS_TEST_TARGET_CFLAGS "${BUILTINS_TEST_TARGET_CFLAGS}") + endif() + + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/${CONFIG_NAME}/lit.site.cfg + ) + list(APPEND BUILTINS_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit/${CONFIG_NAME}) +endforeach() +endif() + add_lit_testsuite(check-builtins "Running the Builtins tests" ${BUILTINS_TESTSUITES} DEPENDS ${BUILTINS_TEST_DEPS}) diff --git a/test/builtins/TestCases/Darwin/os_version_check_test_no_core_foundation.c b/test/builtins/TestCases/Darwin/os_version_check_test_no_core_foundation.c new file mode 100644 index 0000000000..4e0da35cd1 --- /dev/null +++ b/test/builtins/TestCases/Darwin/os_version_check_test_no_core_foundation.c @@ -0,0 +1,12 @@ +// RUN: %clang %s -o %t -mmacosx-version-min=10.5 +// RUN: %run %t + +int __isOSVersionAtLeast(int Major, int Minor, int Subminor); + +int main() { + // When CoreFoundation isn't linked, we expect the system version to be 0, 0, + // 0. + if (__isOSVersionAtLeast(1, 0, 0)) + return 1; + return 0; +} diff --git a/test/builtins/Unit/absvdi2_test.c b/test/builtins/Unit/absvdi2_test.c index f69ae41510..dd9dfd17e6 100644 --- a/test/builtins/Unit/absvdi2_test.c +++ b/test/builtins/Unit/absvdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- absvdi2_test.c - Test __absvdi2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/absvsi2_test.c b/test/builtins/Unit/absvsi2_test.c index c395cca7ad..bae306b2c2 100644 --- a/test/builtins/Unit/absvsi2_test.c +++ b/test/builtins/Unit/absvsi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- absvsi2_test.c - Test __absvsi2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/absvti2_test.c b/test/builtins/Unit/absvti2_test.c index 6c626e97d9..0c0117dfeb 100644 --- a/test/builtins/Unit/absvti2_test.c +++ b/test/builtins/Unit/absvti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- absvti2_test.c - Test __absvti2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/adddf3vfp_test.c b/test/builtins/Unit/adddf3vfp_test.c index c1b9884197..e0da08bc0f 100644 --- a/test/builtins/Unit/adddf3vfp_test.c +++ b/test/builtins/Unit/adddf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- adddf3vfp_test.c - Test __adddf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/addsf3vfp_test.c b/test/builtins/Unit/addsf3vfp_test.c index 958865d68e..ed18de3b60 100644 --- a/test/builtins/Unit/addsf3vfp_test.c +++ b/test/builtins/Unit/addsf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- addsf3vfp_test.c - Test __addsf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/addtf3_test.c b/test/builtins/Unit/addtf3_test.c index 7b92ccee1a..57a4729f44 100644 --- a/test/builtins/Unit/addtf3_test.c +++ b/test/builtins/Unit/addtf3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- addtf3_test.c - Test __addtf3 ------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/addvdi3_test.c b/test/builtins/Unit/addvdi3_test.c index 5f8729a612..99e70403e8 100644 --- a/test/builtins/Unit/addvdi3_test.c +++ b/test/builtins/Unit/addvdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- addvdi3_test.c - Test __addvdi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/addvsi3_test.c b/test/builtins/Unit/addvsi3_test.c index b5358d0f5d..11fdbc3c18 100644 --- a/test/builtins/Unit/addvsi3_test.c +++ b/test/builtins/Unit/addvsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- addvsi3_test.c - Test __addvsi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/addvti3_test.c b/test/builtins/Unit/addvti3_test.c index e2f75cf861..3ffcf4b2d8 100644 --- a/test/builtins/Unit/addvti3_test.c +++ b/test/builtins/Unit/addvti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- addvti3_test.c - Test __addvti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_cdcmpeq_test.c b/test/builtins/Unit/arm/aeabi_cdcmpeq_test.c index ce8b0a4313..0d9e006f01 100644 --- a/test/builtins/Unit/arm/aeabi_cdcmpeq_test.c +++ b/test/builtins/Unit/arm/aeabi_cdcmpeq_test.c @@ -1,3 +1,6 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %arm_call_apsr -o %t.aspr.o +// RUN: %clang_builtins %s %t.aspr.o %librt -o %t && %run %t //===-- aeabi_cdcmpeq.c - Test __aeabi_cdcmpeq ----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_cdcmple_test.c b/test/builtins/Unit/arm/aeabi_cdcmple_test.c index afc7014936..e499bf85a1 100644 --- a/test/builtins/Unit/arm/aeabi_cdcmple_test.c +++ b/test/builtins/Unit/arm/aeabi_cdcmple_test.c @@ -1,3 +1,7 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %arm_call_apsr -o %t.aspr.o +// RUN: %clang_builtins %s %t.aspr.o %librt -o %t && %run %t + //===-- aeabi_cdcmple.c - Test __aeabi_cdcmple and __aeabi_cdrcmple -------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_cfcmpeq_test.c b/test/builtins/Unit/arm/aeabi_cfcmpeq_test.c index fe0166485e..72a556ca77 100644 --- a/test/builtins/Unit/arm/aeabi_cfcmpeq_test.c +++ b/test/builtins/Unit/arm/aeabi_cfcmpeq_test.c @@ -1,3 +1,6 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %arm_call_apsr -o %t.aspr.o +// RUN: %clang_builtins %s %t.aspr.o %librt -o %t && %run %t //===-- aeabi_cfcmpeq.c - Test __aeabi_cfcmpeq ----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_cfcmple_test.c b/test/builtins/Unit/arm/aeabi_cfcmple_test.c index aebe4257e2..a09aead6bd 100644 --- a/test/builtins/Unit/arm/aeabi_cfcmple_test.c +++ b/test/builtins/Unit/arm/aeabi_cfcmple_test.c @@ -1,3 +1,7 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %arm_call_apsr -o %t.aspr.o +// RUN: %clang_builtins %s %t.aspr.o %librt -o %t && %run %t + //===-- aeabi_cfcmple.c - Test __aeabi_cfcmple and __aeabi_cfrcmple -------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_drsub_test.c b/test/builtins/Unit/arm/aeabi_drsub_test.c index 7d867ef2c9..8bd04a9898 100644 --- a/test/builtins/Unit/arm/aeabi_drsub_test.c +++ b/test/builtins/Unit/arm/aeabi_drsub_test.c @@ -1,3 +1,5 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- aeabi_drsub.c - Test __aeabi_drsub --------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_frsub_test.c b/test/builtins/Unit/arm/aeabi_frsub_test.c index b8b21b96e3..3d301616ae 100644 --- a/test/builtins/Unit/arm/aeabi_frsub_test.c +++ b/test/builtins/Unit/arm/aeabi_frsub_test.c @@ -1,3 +1,5 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- aeabi_frsub.c - Test __aeabi_frsub --------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_idivmod_test.c b/test/builtins/Unit/arm/aeabi_idivmod_test.c index c732e4ba43..ac1804694d 100644 --- a/test/builtins/Unit/arm/aeabi_idivmod_test.c +++ b/test/builtins/Unit/arm/aeabi_idivmod_test.c @@ -1,3 +1,5 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- aeabi_idivmod_test.c - Test __aeabi_idivmod -----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_uidivmod_test.c b/test/builtins/Unit/arm/aeabi_uidivmod_test.c index 81d5e0e1f8..9ac51da598 100644 --- a/test/builtins/Unit/arm/aeabi_uidivmod_test.c +++ b/test/builtins/Unit/arm/aeabi_uidivmod_test.c @@ -1,3 +1,5 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- aeabi_uidivmod_test.c - Test __aeabi_uidivmod ---------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/aeabi_uldivmod_test.c b/test/builtins/Unit/arm/aeabi_uldivmod_test.c index f629d6b4d2..a40f006fa6 100644 --- a/test/builtins/Unit/arm/aeabi_uldivmod_test.c +++ b/test/builtins/Unit/arm/aeabi_uldivmod_test.c @@ -1,3 +1,5 @@ +// REQUIRES-ANY: arm-target-arch,armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- aeabi_uldivmod_test.c - Test aeabi_uldivmod -----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/arm/call_apsr.S b/test/builtins/Unit/arm/call_apsr.S index b5e154cff3..2656f8de66 100644 --- a/test/builtins/Unit/arm/call_apsr.S +++ b/test/builtins/Unit/arm/call_apsr.S @@ -22,11 +22,11 @@ // } DEFINE_COMPILERRT_PRIVATE_FUNCTION(call_apsr_d) - push {lr} - ldr ip, [sp, #4] - blx ip + push {r7, lr} + ldr r7, [sp, #8] + blx r7 mrs r0, apsr - pop {pc} + pop {r7, pc} END_COMPILERRT_FUNCTION(call_apsr_d) // __attribute__((pcs("aapcs"))) diff --git a/test/builtins/Unit/ashldi3_test.c b/test/builtins/Unit/ashldi3_test.c index 398fb69beb..f0984e06dc 100644 --- a/test/builtins/Unit/ashldi3_test.c +++ b/test/builtins/Unit/ashldi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ashldi3_test.c - Test __ashldi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ashlti3_test.c b/test/builtins/Unit/ashlti3_test.c index 595e353065..06186614ff 100644 --- a/test/builtins/Unit/ashlti3_test.c +++ b/test/builtins/Unit/ashlti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ashlti3_test.c - Test __ashlti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ashrdi3_test.c b/test/builtins/Unit/ashrdi3_test.c index ee6409c870..a987c95308 100644 --- a/test/builtins/Unit/ashrdi3_test.c +++ b/test/builtins/Unit/ashrdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ashrdi3_test.c - Test __ashrdi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ashrti3_test.c b/test/builtins/Unit/ashrti3_test.c index 201582d4ea..1f08653527 100644 --- a/test/builtins/Unit/ashrti3_test.c +++ b/test/builtins/Unit/ashrti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ashrti3_test.c - Test __ashrti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/bswapdi2_test.c b/test/builtins/Unit/bswapdi2_test.c index 2d830cf5e4..6881c8092c 100644 --- a/test/builtins/Unit/bswapdi2_test.c +++ b/test/builtins/Unit/bswapdi2_test.c @@ -1,3 +1,5 @@ +// UNSUPPORTED: armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- bswapdi2_test.c - Test __bswapdi2 ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/bswapsi2_test.c b/test/builtins/Unit/bswapsi2_test.c index 4488a888e1..c32cbb4421 100644 --- a/test/builtins/Unit/bswapsi2_test.c +++ b/test/builtins/Unit/bswapsi2_test.c @@ -1,3 +1,5 @@ +// UNSUPPORTED: armv6m-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- bswapsi2_test.c - Test __bswapsi2 ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/clear_cache_test.c b/test/builtins/Unit/clear_cache_test.c index 0ef704fcde..590be7eb26 100644 --- a/test/builtins/Unit/clear_cache_test.c +++ b/test/builtins/Unit/clear_cache_test.c @@ -1,3 +1,6 @@ +// REQUIRES: native-run +// UNSUPPORTED: arm, aarch64 +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- clear_cache_test.c - Test clear_cache -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/clzdi2_test.c b/test/builtins/Unit/clzdi2_test.c index 41e120932d..a8c0e1b350 100644 --- a/test/builtins/Unit/clzdi2_test.c +++ b/test/builtins/Unit/clzdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- clzdi2_test.c - Test __clzdi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/clzsi2_test.c b/test/builtins/Unit/clzsi2_test.c index 80b300feea..f86e8885e0 100644 --- a/test/builtins/Unit/clzsi2_test.c +++ b/test/builtins/Unit/clzsi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- clzsi2_test.c - Test __clzsi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/clzti2_test.c b/test/builtins/Unit/clzti2_test.c index 3a2c6fabb8..157838b6bd 100644 --- a/test/builtins/Unit/clzti2_test.c +++ b/test/builtins/Unit/clzti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- clzti2_test.c - Test __clzti2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/cmpdi2_test.c b/test/builtins/Unit/cmpdi2_test.c index 33a12a0424..1420dc773f 100644 --- a/test/builtins/Unit/cmpdi2_test.c +++ b/test/builtins/Unit/cmpdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- cmpdi2_test.c - Test __cmpdi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/cmpti2_test.c b/test/builtins/Unit/cmpti2_test.c index d951923b2d..a2215f3c15 100644 --- a/test/builtins/Unit/cmpti2_test.c +++ b/test/builtins/Unit/cmpti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- cmpti2_test.c - Test __cmpti2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/comparedf2_test.c b/test/builtins/Unit/comparedf2_test.c index 6623722902..844690197d 100644 --- a/test/builtins/Unit/comparedf2_test.c +++ b/test/builtins/Unit/comparedf2_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- cmpdf2_test.c - Test __cmpdf2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/comparesf2_test.c b/test/builtins/Unit/comparesf2_test.c index 026e90053b..1b5902f376 100644 --- a/test/builtins/Unit/comparesf2_test.c +++ b/test/builtins/Unit/comparesf2_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- cmpsf2_test.c - Test __cmpsf2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/cpu_model_test.c b/test/builtins/Unit/cpu_model_test.c index ed484cdd4c..6b47d143f0 100644 --- a/test/builtins/Unit/cpu_model_test.c +++ b/test/builtins/Unit/cpu_model_test.c @@ -1,4 +1,8 @@ -//===-- cpu_model_test.c - Test __builtin_cpu_supports -------------------------------===// +// FIXME: XFAIL the test because it is expected to return non-zero value. +// XFAIL: * +// REQUIRES: x86-target-arch +// RUN: %clang_builtins %s %librt -o %t && %run %t +//===-- cpu_model_test.c - Test __builtin_cpu_supports --------------------===// // // The LLVM Compiler Infrastructure // @@ -11,8 +15,6 @@ // //===----------------------------------------------------------------------===// -// REQUIRES: x86-target-arch - #include int main (void) { diff --git a/test/builtins/Unit/ctzdi2_test.c b/test/builtins/Unit/ctzdi2_test.c index bde66b1e54..0515e20726 100644 --- a/test/builtins/Unit/ctzdi2_test.c +++ b/test/builtins/Unit/ctzdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ctzdi2_test.c - Test __ctzdi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ctzsi2_test.c b/test/builtins/Unit/ctzsi2_test.c index cbc101fca0..bf8982bf03 100644 --- a/test/builtins/Unit/ctzsi2_test.c +++ b/test/builtins/Unit/ctzsi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ctzsi2_test.c - Test __ctzsi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ctzti2_test.c b/test/builtins/Unit/ctzti2_test.c index 0ca1920bd7..bef79b6f86 100644 --- a/test/builtins/Unit/ctzti2_test.c +++ b/test/builtins/Unit/ctzti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ctzti2_test.c - Test __ctzti2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divdc3_test.c b/test/builtins/Unit/divdc3_test.c index 80b9e86e1e..3c69cf3d0c 100644 --- a/test/builtins/Unit/divdc3_test.c +++ b/test/builtins/Unit/divdc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divdc3_test.c - Test __divdc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divdf3vfp_test.c b/test/builtins/Unit/divdf3vfp_test.c index 8735f63782..4f18409a28 100644 --- a/test/builtins/Unit/divdf3vfp_test.c +++ b/test/builtins/Unit/divdf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divdf3vfp_test.c - Test __divdf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divdi3_test.c b/test/builtins/Unit/divdi3_test.c index 1d459803e1..4c8c922260 100644 --- a/test/builtins/Unit/divdi3_test.c +++ b/test/builtins/Unit/divdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divdi3_test.c - Test __divdi3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divmodsi4_test.c b/test/builtins/Unit/divmodsi4_test.c index 6fb1c985ab..e766aaee11 100644 --- a/test/builtins/Unit/divmodsi4_test.c +++ b/test/builtins/Unit/divmodsi4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divmodsi4_test.c - Test __divmodsi4 -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divsc3_test.c b/test/builtins/Unit/divsc3_test.c index 2d7c659379..42430151f0 100644 --- a/test/builtins/Unit/divsc3_test.c +++ b/test/builtins/Unit/divsc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -lm -o %t && %run %t //===-- divsc3_test.c - Test __divsc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divsf3vfp_test.c b/test/builtins/Unit/divsf3vfp_test.c index 039fa7f018..75b7eba7a0 100644 --- a/test/builtins/Unit/divsf3vfp_test.c +++ b/test/builtins/Unit/divsf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divsf3vfp_test.c - Test __divsf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divsi3_test.c b/test/builtins/Unit/divsi3_test.c index c523367455..4c5d0fba5e 100644 --- a/test/builtins/Unit/divsi3_test.c +++ b/test/builtins/Unit/divsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divsi3_test.c - Test __divsi3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divtc3_test.c b/test/builtins/Unit/divtc3_test.c index a1f0613441..f561a9644f 100644 --- a/test/builtins/Unit/divtc3_test.c +++ b/test/builtins/Unit/divtc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -lm -o %t && %run %t //===-- divtc3_test.c - Test __divtc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divtf3_test.c b/test/builtins/Unit/divtf3_test.c index e0def45ffd..12cb94a26e 100644 --- a/test/builtins/Unit/divtf3_test.c +++ b/test/builtins/Unit/divtf3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- divtf3_test.c - Test __divtf3 ------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divti3_test.c b/test/builtins/Unit/divti3_test.c index 3a94dab8c7..9a6bf178b9 100644 --- a/test/builtins/Unit/divti3_test.c +++ b/test/builtins/Unit/divti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- divti3_test.c - Test __divti3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/divxc3_test.c b/test/builtins/Unit/divxc3_test.c index 509b4b18e9..d71660bfe1 100644 --- a/test/builtins/Unit/divxc3_test.c +++ b/test/builtins/Unit/divxc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -lm -o %t && %run %t //===-- divxc3_test.c - Test __divxc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/enable_execute_stack_test.c b/test/builtins/Unit/enable_execute_stack_test.c index 38a142afb2..24165ed9fb 100644 --- a/test/builtins/Unit/enable_execute_stack_test.c +++ b/test/builtins/Unit/enable_execute_stack_test.c @@ -1,3 +1,5 @@ +// REQUIRES: native-run +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- enable_execute_stack_test.c - Test __enable_execute_stack ----------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/eqdf2vfp_test.c b/test/builtins/Unit/eqdf2vfp_test.c index 4780d87ea2..69dd37b8f9 100644 --- a/test/builtins/Unit/eqdf2vfp_test.c +++ b/test/builtins/Unit/eqdf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- eqdf2vfp_test.c - Test __eqdf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/eqsf2vfp_test.c b/test/builtins/Unit/eqsf2vfp_test.c index 7d6f581ce4..9c8dc1659f 100644 --- a/test/builtins/Unit/eqsf2vfp_test.c +++ b/test/builtins/Unit/eqsf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- eqsf2vfp_test.c - Test __eqsf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/eqtf2_test.c b/test/builtins/Unit/eqtf2_test.c index 038583503a..91b35cff28 100644 --- a/test/builtins/Unit/eqtf2_test.c +++ b/test/builtins/Unit/eqtf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ eqtf2_test.c - Test __eqtf2------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/extebdsfdf2vfp_test.c b/test/builtins/Unit/extebdsfdf2vfp_test.c index ec27c4c69b..c350d933d4 100644 --- a/test/builtins/Unit/extebdsfdf2vfp_test.c +++ b/test/builtins/Unit/extebdsfdf2vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- extendsfdf2vfp_test.c - Test __extendsfdf2vfp ---------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/extenddftf2_test.c b/test/builtins/Unit/extenddftf2_test.c index 2cfb32b260..7254141a27 100644 --- a/test/builtins/Unit/extenddftf2_test.c +++ b/test/builtins/Unit/extenddftf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- extenddftf2_test.c - Test __extenddftf2 --------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/extendhfsf2_test.c b/test/builtins/Unit/extendhfsf2_test.c index 5dd994cae1..d423e7b4e5 100644 --- a/test/builtins/Unit/extendhfsf2_test.c +++ b/test/builtins/Unit/extendhfsf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- extendhfsf2_test.c - Test __extendhfsf2 --------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/extendsftf2_test.c b/test/builtins/Unit/extendsftf2_test.c index 7dff5b6be3..4fad9060f0 100644 --- a/test/builtins/Unit/extendsftf2_test.c +++ b/test/builtins/Unit/extendsftf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- extendsftf2_test.c - Test __extendsftf2 --------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ffsdi2_test.c b/test/builtins/Unit/ffsdi2_test.c index a27d154fd3..80c0c08cb5 100644 --- a/test/builtins/Unit/ffsdi2_test.c +++ b/test/builtins/Unit/ffsdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ffsdi2_test.c - Test __ffsdi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ffsti2_test.c b/test/builtins/Unit/ffsti2_test.c index 396269d513..3b312c4309 100644 --- a/test/builtins/Unit/ffsti2_test.c +++ b/test/builtins/Unit/ffsti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ffsti2_test.c - Test __ffsti2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixdfdi_test.c b/test/builtins/Unit/fixdfdi_test.c index 4a7cfa31c7..d2ba7b19fe 100644 --- a/test/builtins/Unit/fixdfdi_test.c +++ b/test/builtins/Unit/fixdfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixdfdi_test.c - Test __fixdfdi -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixdfsivfp_test.c b/test/builtins/Unit/fixdfsivfp_test.c index 73e4e58eda..33b4d24081 100644 --- a/test/builtins/Unit/fixdfsivfp_test.c +++ b/test/builtins/Unit/fixdfsivfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- fixdfsivfp_test.c - Test __fixdfsivfp -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixdfti_test.c b/test/builtins/Unit/fixdfti_test.c index b5da456fc6..0abe187ae9 100644 --- a/test/builtins/Unit/fixdfti_test.c +++ b/test/builtins/Unit/fixdfti_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixdfti_test.c - Test __fixdfti -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixsfdi_test.c b/test/builtins/Unit/fixsfdi_test.c index f37ecef047..468299f9b1 100644 --- a/test/builtins/Unit/fixsfdi_test.c +++ b/test/builtins/Unit/fixsfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixsfdi_test.c - Test __fixsfdi -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixsfsivfp_test.c b/test/builtins/Unit/fixsfsivfp_test.c index 0ded952d18..ee33a1dc9f 100644 --- a/test/builtins/Unit/fixsfsivfp_test.c +++ b/test/builtins/Unit/fixsfsivfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- fixsfsivfp_test.c - Test __fixsfsivfp -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixsfti_test.c b/test/builtins/Unit/fixsfti_test.c index 38748aabc9..ec4e8ddb03 100644 --- a/test/builtins/Unit/fixsfti_test.c +++ b/test/builtins/Unit/fixsfti_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixsfti_test.c - Test __fixsfti -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixtfdi_test.c b/test/builtins/Unit/fixtfdi_test.c index cc25becb2f..3cd1ebf78b 100644 --- a/test/builtins/Unit/fixtfdi_test.c +++ b/test/builtins/Unit/fixtfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- fixtfdi_test.c - Test __fixtfdi ----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixtfsi_test.c b/test/builtins/Unit/fixtfsi_test.c index 1da516bd07..59e43520ab 100644 --- a/test/builtins/Unit/fixtfsi_test.c +++ b/test/builtins/Unit/fixtfsi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- fixtfsi_test.c - Test __fixtfsi ----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixtfti_test.c b/test/builtins/Unit/fixtfti_test.c index 52184ca624..4d3b56c785 100644 --- a/test/builtins/Unit/fixtfti_test.c +++ b/test/builtins/Unit/fixtfti_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- fixtfti_test.c - Test __fixtfti ----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsdfdi_test.c b/test/builtins/Unit/fixunsdfdi_test.c index 3998482876..ab09bd0f39 100644 --- a/test/builtins/Unit/fixunsdfdi_test.c +++ b/test/builtins/Unit/fixunsdfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunsdfdi_test.c - Test __fixunsdfdi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsdfsi_test.c b/test/builtins/Unit/fixunsdfsi_test.c index 8d3d6304f4..7fbd9ee115 100644 --- a/test/builtins/Unit/fixunsdfsi_test.c +++ b/test/builtins/Unit/fixunsdfsi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunsdfsi_test.c - Test __fixunsdfsi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsdfsivfp_test.c b/test/builtins/Unit/fixunsdfsivfp_test.c index 33cec81da1..b707992472 100644 --- a/test/builtins/Unit/fixunsdfsivfp_test.c +++ b/test/builtins/Unit/fixunsdfsivfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunsdfsivfp_test.c - Test __fixunsdfsivfp -----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsdfti_test.c b/test/builtins/Unit/fixunsdfti_test.c index 0298fb9e94..249d8f886d 100644 --- a/test/builtins/Unit/fixunsdfti_test.c +++ b/test/builtins/Unit/fixunsdfti_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunsdfti_test.c - Test __fixunsdfti -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunssfdi_test.c b/test/builtins/Unit/fixunssfdi_test.c index 812457a002..2d693eb9c8 100644 --- a/test/builtins/Unit/fixunssfdi_test.c +++ b/test/builtins/Unit/fixunssfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunssfdi_test.c - Test __fixunssfdi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunssfsi_test.c b/test/builtins/Unit/fixunssfsi_test.c index 17293e4385..3974b5329b 100644 --- a/test/builtins/Unit/fixunssfsi_test.c +++ b/test/builtins/Unit/fixunssfsi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunssfsi_test.c - Test __fixunssfsi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunssfsivfp_test.c b/test/builtins/Unit/fixunssfsivfp_test.c index 9c8194ff0f..c1d8ed77f3 100644 --- a/test/builtins/Unit/fixunssfsivfp_test.c +++ b/test/builtins/Unit/fixunssfsivfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- fixunssfsivfp_test.c - Test __fixunssfsivfp -----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunssfti_test.c b/test/builtins/Unit/fixunssfti_test.c index 979d661910..fd2d108f25 100644 --- a/test/builtins/Unit/fixunssfti_test.c +++ b/test/builtins/Unit/fixunssfti_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunssfti_test.c - Test __fixunssfti -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunstfdi_test.c b/test/builtins/Unit/fixunstfdi_test.c index 817c59b2d5..67fcc2fde8 100644 --- a/test/builtins/Unit/fixunstfdi_test.c +++ b/test/builtins/Unit/fixunstfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunstfdi_test.c - Test __fixunstfdi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunstfsi_test.c b/test/builtins/Unit/fixunstfsi_test.c index 13ed779522..edf5422a8a 100644 --- a/test/builtins/Unit/fixunstfsi_test.c +++ b/test/builtins/Unit/fixunstfsi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- fixunstfsi_test.c - Test __fixunstfsi ----------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunstfti_test.c b/test/builtins/Unit/fixunstfti_test.c index 60a0982b62..019b72049d 100644 --- a/test/builtins/Unit/fixunstfti_test.c +++ b/test/builtins/Unit/fixunstfti_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunstfti_test.c - Test __fixunstfti -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsxfdi_test.c b/test/builtins/Unit/fixunsxfdi_test.c index 6f42079695..c5c27d3594 100644 --- a/test/builtins/Unit/fixunsxfdi_test.c +++ b/test/builtins/Unit/fixunsxfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunsxfdi_test.c - Test __fixunsxfdi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsxfsi_test.c b/test/builtins/Unit/fixunsxfsi_test.c index 0d78dcb53c..e0bed7ec8f 100644 --- a/test/builtins/Unit/fixunsxfsi_test.c +++ b/test/builtins/Unit/fixunsxfsi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixunsxfsi_test.c - Test __fixunsxfsi -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixunsxfti_test.c b/test/builtins/Unit/fixunsxfti_test.c index 94b5aebe4d..28a7e9783c 100644 --- a/test/builtins/Unit/fixunsxfti_test.c +++ b/test/builtins/Unit/fixunsxfti_test.c @@ -1,3 +1,7 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t +// XFAIL: aarch64 +// test fails for aarch64 (see pr32260) + //===-- fixunsxfti_test.c - Test __fixunsxfti -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixxfdi_test.c b/test/builtins/Unit/fixxfdi_test.c index 0a90a56e61..40ad2b04f2 100644 --- a/test/builtins/Unit/fixxfdi_test.c +++ b/test/builtins/Unit/fixxfdi_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- fixxfdi_test.c - Test __fixxfdi -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/fixxfti_test.c b/test/builtins/Unit/fixxfti_test.c index b8573cc141..c6d42c6e13 100644 --- a/test/builtins/Unit/fixxfti_test.c +++ b/test/builtins/Unit/fixxfti_test.c @@ -1,3 +1,7 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t +// XFAIL: aarch64 +// test fails for aarch64 (see pr32260) + //===-- fixxfti_test.c - Test __fixxfti -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatdidf_test.c b/test/builtins/Unit/floatdidf_test.c index 9bf2be97c7..77429173d6 100644 --- a/test/builtins/Unit/floatdidf_test.c +++ b/test/builtins/Unit/floatdidf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatdidf.c - Test __floatdidf ------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatdisf_test.c b/test/builtins/Unit/floatdisf_test.c index a55c6a9617..8299bb171d 100644 --- a/test/builtins/Unit/floatdisf_test.c +++ b/test/builtins/Unit/floatdisf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatdisf_test.c - Test __floatdisf -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatditf_test.c b/test/builtins/Unit/floatditf_test.c index 8cf8a0859e..bdfb7f9483 100644 --- a/test/builtins/Unit/floatditf_test.c +++ b/test/builtins/Unit/floatditf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatditf_test.c - Test __floatditf -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatdixf_test.c b/test/builtins/Unit/floatdixf_test.c index f6ab5a4665..0a4ca3a574 100644 --- a/test/builtins/Unit/floatdixf_test.c +++ b/test/builtins/Unit/floatdixf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatdixf_test.c - Test __floatdixf -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatsidfvfp_test.c b/test/builtins/Unit/floatsidfvfp_test.c index 8404a7ec6f..8a3c7fb4ec 100644 --- a/test/builtins/Unit/floatsidfvfp_test.c +++ b/test/builtins/Unit/floatsidfvfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatsidfvfp_test.c - Test __floatsidfvfp -------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatsisfvfp_test.c b/test/builtins/Unit/floatsisfvfp_test.c index c41cf9d51a..7c044b2f3f 100644 --- a/test/builtins/Unit/floatsisfvfp_test.c +++ b/test/builtins/Unit/floatsisfvfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatsisfvfp_test.c - Test __floatsisfvfp -------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatsitf_test.c b/test/builtins/Unit/floatsitf_test.c index 6f98721b0a..f8d700b3bd 100644 --- a/test/builtins/Unit/floatsitf_test.c +++ b/test/builtins/Unit/floatsitf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- floatsitf_test.c - Test __floatsitf ------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floattidf_test.c b/test/builtins/Unit/floattidf_test.c index 3af382ac00..9da8c5dc68 100644 --- a/test/builtins/Unit/floattidf_test.c +++ b/test/builtins/Unit/floattidf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floattidf.c - Test __floattidf ------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floattisf_test.c b/test/builtins/Unit/floattisf_test.c index 0f5dc544da..9d7282cdea 100644 --- a/test/builtins/Unit/floattisf_test.c +++ b/test/builtins/Unit/floattisf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floattisf_test.c - Test __floattisf -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floattitf_test.c b/test/builtins/Unit/floattitf_test.c index 928b2e8811..5aeb76057b 100644 --- a/test/builtins/Unit/floattitf_test.c +++ b/test/builtins/Unit/floattitf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floattitf.c - Test __floattitf ------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floattixf_test.c b/test/builtins/Unit/floattixf_test.c index d281debdcf..00644fae19 100644 --- a/test/builtins/Unit/floattixf_test.c +++ b/test/builtins/Unit/floattixf_test.c @@ -1,3 +1,7 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t +// XFAIL: aarch64 +// test fails for aarch64 (see pr32260) + //===-- floattixf.c - Test __floattixf ------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatundidf_test.c b/test/builtins/Unit/floatundidf_test.c index 97fb1e5ec0..d5d74195c2 100644 --- a/test/builtins/Unit/floatundidf_test.c +++ b/test/builtins/Unit/floatundidf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatundidf_test.c - Test __floatundidf ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatundisf_test.c b/test/builtins/Unit/floatundisf_test.c index 40b6bcc459..898d8863f2 100644 --- a/test/builtins/Unit/floatundisf_test.c +++ b/test/builtins/Unit/floatundisf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatundisf_test.c - Test __floatundisf ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatunditf_test.c b/test/builtins/Unit/floatunditf_test.c index b66a174a3f..342dc278fb 100644 --- a/test/builtins/Unit/floatunditf_test.c +++ b/test/builtins/Unit/floatunditf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatunditf_test.c - Test __floatunditf ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatundixf_test.c b/test/builtins/Unit/floatundixf_test.c index 690dce1995..47b431697d 100644 --- a/test/builtins/Unit/floatundixf_test.c +++ b/test/builtins/Unit/floatundixf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatundixf_test.c - Test __floatundixf ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatunsitf_test.c b/test/builtins/Unit/floatunsitf_test.c index c3d3fe949c..966a4fd7b7 100644 --- a/test/builtins/Unit/floatunsitf_test.c +++ b/test/builtins/Unit/floatunsitf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- floatunsitf_test.c - Test __floatunsitf --------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatunssidfvfp_test.c b/test/builtins/Unit/floatunssidfvfp_test.c index 1671c74bbd..733755719e 100644 --- a/test/builtins/Unit/floatunssidfvfp_test.c +++ b/test/builtins/Unit/floatunssidfvfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatunssidfvfp_test.c - Test __floatunssidfvfp -------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatunssisfvfp_test.c b/test/builtins/Unit/floatunssisfvfp_test.c index 506f3be51e..9d7fb654fd 100644 --- a/test/builtins/Unit/floatunssisfvfp_test.c +++ b/test/builtins/Unit/floatunssisfvfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatunssisfvfp_test.c - Test __floatunssisfvfp -------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatuntidf_test.c b/test/builtins/Unit/floatuntidf_test.c index 9855ff7970..1bf19ba141 100644 --- a/test/builtins/Unit/floatuntidf_test.c +++ b/test/builtins/Unit/floatuntidf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatuntidf.c - Test __floatuntidf --------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatuntisf_test.c b/test/builtins/Unit/floatuntisf_test.c index 9b5ff790a6..c7c11ba48c 100644 --- a/test/builtins/Unit/floatuntisf_test.c +++ b/test/builtins/Unit/floatuntisf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatuntisf.c - Test __floatuntisf --------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatuntitf_test.c b/test/builtins/Unit/floatuntitf_test.c index 495adcfa90..e81c30e321 100644 --- a/test/builtins/Unit/floatuntitf_test.c +++ b/test/builtins/Unit/floatuntitf_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- floatuntitf.c - Test __floatuntitf --------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/floatuntixf_test.c b/test/builtins/Unit/floatuntixf_test.c index c58b55d3e4..70ad5f36f3 100644 --- a/test/builtins/Unit/floatuntixf_test.c +++ b/test/builtins/Unit/floatuntixf_test.c @@ -1,3 +1,7 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t +// XFAIL: aarch64 +// test fails for aarch64 (see pr32260) + //===-- floatuntixf.c - Test __floatuntixf --------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/gcc_personality_test.c b/test/builtins/Unit/gcc_personality_test.c index f9598c697e..b3345dd732 100644 --- a/test/builtins/Unit/gcc_personality_test.c +++ b/test/builtins/Unit/gcc_personality_test.c @@ -1,3 +1,6 @@ +// FIXME: XFAIL as currently it cannot be built by lit properly. +// XFAIL: * +// RUN: %clangxx_builtins %s %librt -o %t && %run %t /* ===-- gcc_personality_test.c - Tests __gcc_personality_v0 -------------=== * * The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/gedf2vfp_test.c b/test/builtins/Unit/gedf2vfp_test.c index 341fd65d2b..ad72083b40 100644 --- a/test/builtins/Unit/gedf2vfp_test.c +++ b/test/builtins/Unit/gedf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- gedf2vfp_test.c - Test __gedf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/gesf2vfp_test.c b/test/builtins/Unit/gesf2vfp_test.c index 607d9880e4..8a855e12fe 100644 --- a/test/builtins/Unit/gesf2vfp_test.c +++ b/test/builtins/Unit/gesf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- gesf2vfp_test.c - Test __gesf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/getf2_test.c b/test/builtins/Unit/getf2_test.c index 9796b8ab84..115b630b41 100644 --- a/test/builtins/Unit/getf2_test.c +++ b/test/builtins/Unit/getf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ getf2_test.c - Test __getf2------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/gtdf2vfp_test.c b/test/builtins/Unit/gtdf2vfp_test.c index 1bf68bf1ae..e6eb545dbb 100644 --- a/test/builtins/Unit/gtdf2vfp_test.c +++ b/test/builtins/Unit/gtdf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- gtdf2vfp_test.c - Test __gtdf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/gtsf2vfp_test.c b/test/builtins/Unit/gtsf2vfp_test.c index 8209647ce7..e0442c6cdc 100644 --- a/test/builtins/Unit/gtsf2vfp_test.c +++ b/test/builtins/Unit/gtsf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- gtsf2vfp_test.c - Test __gtsf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/gttf2_test.c b/test/builtins/Unit/gttf2_test.c index 6508d4b978..81d68cbf12 100644 --- a/test/builtins/Unit/gttf2_test.c +++ b/test/builtins/Unit/gttf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ gttf2_test.c - Test __gttf2------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ledf2vfp_test.c b/test/builtins/Unit/ledf2vfp_test.c index 2e1daf0448..f0cd56eab5 100644 --- a/test/builtins/Unit/ledf2vfp_test.c +++ b/test/builtins/Unit/ledf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- ledf2vfp_test.c - Test __ledf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/lesf2vfp_test.c b/test/builtins/Unit/lesf2vfp_test.c index 0f8939366c..02ae182e69 100644 --- a/test/builtins/Unit/lesf2vfp_test.c +++ b/test/builtins/Unit/lesf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- lesf2vfp_test.c - Test __lesf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/letf2_test.c b/test/builtins/Unit/letf2_test.c index 1842e3c556..bb8452567a 100644 --- a/test/builtins/Unit/letf2_test.c +++ b/test/builtins/Unit/letf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ letf2_test.c - Test __letf2------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/lit.cfg b/test/builtins/Unit/lit.cfg new file mode 100644 index 0000000000..f29f7e00d0 --- /dev/null +++ b/test/builtins/Unit/lit.cfg @@ -0,0 +1,80 @@ +# -*- Python -*- + +import os +import platform + +import lit.formats + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'Builtins' + config.name_suffix + +# Platform-specific default Builtins_OPTIONS for lit tests. +default_builtins_opts = '' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Path to the static library +base_lib = os.path.join(config.compiler_rt_libdir, "libclang_rt.builtins-%s.a " + % config.target_arch) + +builtins_source_dir = os.path.join( + get_required_attr(config, "compiler_rt_src_root"), "lib", "builtins") +builtins_lit_source_dir = get_required_attr(config, "builtins_lit_source_dir") + +extra_link_flags = ["-nodefaultlibs"] +config.substitutions.append( ("%librt ", base_lib + ' -lc -lm ') ) + +target_cflags = [get_required_attr(config, "target_cflags")] +target_cflags += ['-fno-builtin', '-I', builtins_source_dir] +target_cflags += extra_link_flags +target_cxxflags = config.cxx_mode_flags + target_cflags +clang_builtins_static_cflags = ([""] + + config.debug_info_flags + target_cflags) +clang_builtins_static_cxxflags = config.cxx_mode_flags + \ + clang_builtins_static_cflags + +clang_builtins_cflags = clang_builtins_static_cflags +clang_builtins_cxxflags = clang_builtins_static_cxxflags + + +config.available_features.add('not-android') +clang_wrapper = "" + +def build_invocation(compile_flags): + return " " + " ".join([clang_wrapper, config.clang] + compile_flags) + " " + + +target_arch = config.target_arch +if (target_arch == "arm"): + target_arch = "armv7" + +config.substitutions.append( ("%clang ", build_invocation(target_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) ) +config.substitutions.append( ("%clang_builtins ", \ + build_invocation(clang_builtins_cflags))) +config.substitutions.append( ("%clangxx_builtins ", \ + build_invocation(clang_builtins_cxxflags))) + +# FIXME: move the call_apsr.s into call_apsr.h as inline-asm. +# some ARM tests needs call_apsr.s +call_apsr_source = os.path.join(builtins_lit_source_dir, 'arm', 'call_apsr.S') +march_flag = '-march=' + target_arch +call_apsr_flags = ['-c', march_flag, call_apsr_source] +config.substitutions.append( ("%arm_call_apsr ", \ + build_invocation(call_apsr_flags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +if not config.emulator: + config.available_features.add('native-run') diff --git a/test/builtins/Unit/lit.site.cfg.in b/test/builtins/Unit/lit.site.cfg.in new file mode 100644 index 0000000000..4b4009d1e2 --- /dev/null +++ b/test/builtins/Unit/lit.site.cfg.in @@ -0,0 +1,12 @@ +@LIT_SITE_CFG_IN_HEADER@ + +config.name_suffix = "@BUILTINS_TEST_CONFIG_SUFFIX@" +config.builtins_lit_source_dir = "@BUILTINS_LIT_SOURCE_DIR@/Unit" +config.target_cflags = "@BUILTINS_TEST_TARGET_CFLAGS@" +config.target_arch = "@BUILTINS_TEST_TARGET_ARCH@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@BUILTINS_LIT_SOURCE_DIR@/Unit/lit.cfg") diff --git a/test/builtins/Unit/lshrdi3_test.c b/test/builtins/Unit/lshrdi3_test.c index d48ae4dd79..c5faa98ee6 100644 --- a/test/builtins/Unit/lshrdi3_test.c +++ b/test/builtins/Unit/lshrdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- lshrdi3_test.c - Test __lshrdi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/lshrti3_test.c b/test/builtins/Unit/lshrti3_test.c index f5a0dd616c..91356c8bfe 100644 --- a/test/builtins/Unit/lshrti3_test.c +++ b/test/builtins/Unit/lshrti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- lshrti3_test.c - Test __lshrti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ltdf2vfp_test.c b/test/builtins/Unit/ltdf2vfp_test.c index fdbe9a17bc..1edb319d4c 100644 --- a/test/builtins/Unit/ltdf2vfp_test.c +++ b/test/builtins/Unit/ltdf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- ltdf2vfp_test.c - Test __ltdf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ltsf2vfp_test.c b/test/builtins/Unit/ltsf2vfp_test.c index d4d65ba929..2fc0c11270 100644 --- a/test/builtins/Unit/ltsf2vfp_test.c +++ b/test/builtins/Unit/ltsf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- ltsf2vfp_test.c - Test __ltsf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/lttf2_test.c b/test/builtins/Unit/lttf2_test.c index e8f9dc17c6..cfe1906d2a 100644 --- a/test/builtins/Unit/lttf2_test.c +++ b/test/builtins/Unit/lttf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ lttf2_test.c - Test __lttf2------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/moddi3_test.c b/test/builtins/Unit/moddi3_test.c index 62e8f227b3..8325ad75b2 100644 --- a/test/builtins/Unit/moddi3_test.c +++ b/test/builtins/Unit/moddi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- moddi3_test.c - Test __moddi3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/modsi3_test.c b/test/builtins/Unit/modsi3_test.c index 8c9f58832b..8075f51604 100644 --- a/test/builtins/Unit/modsi3_test.c +++ b/test/builtins/Unit/modsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t /* ===-- modsi3_test.c - Test __modsi3 -------------------------------------=== * * The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/modti3_test.c b/test/builtins/Unit/modti3_test.c index 99413aaa2f..53fbf5bbad 100644 --- a/test/builtins/Unit/modti3_test.c +++ b/test/builtins/Unit/modti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- modti3_test.c - Test __modti3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/muldc3_test.c b/test/builtins/Unit/muldc3_test.c index 6902ef3222..add09f4e83 100644 --- a/test/builtins/Unit/muldc3_test.c +++ b/test/builtins/Unit/muldc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -lm -o %t && %run %t //===-- muldc3_test.c - Test __muldc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/muldf3vfp_test.c b/test/builtins/Unit/muldf3vfp_test.c index 024c9a8a87..36a22625c6 100644 --- a/test/builtins/Unit/muldf3vfp_test.c +++ b/test/builtins/Unit/muldf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- muldf3vfp_test.c - Test __muldf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/muldi3_test.c b/test/builtins/Unit/muldi3_test.c index 651dd01773..d09e74d1ae 100644 --- a/test/builtins/Unit/muldi3_test.c +++ b/test/builtins/Unit/muldi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- muldi3_test.c - Test __muldi3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulodi4_test.c b/test/builtins/Unit/mulodi4_test.c index 4546609fb8..f03b8fecfc 100644 --- a/test/builtins/Unit/mulodi4_test.c +++ b/test/builtins/Unit/mulodi4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- mulodi4_test.c - Test __mulodi4 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulosi4_test.c b/test/builtins/Unit/mulosi4_test.c index 6a27d69bd6..12913f1d71 100644 --- a/test/builtins/Unit/mulosi4_test.c +++ b/test/builtins/Unit/mulosi4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- mulosi4_test.c - Test __mulosi4 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/muloti4_test.c b/test/builtins/Unit/muloti4_test.c index d00e7bb6b6..e7c78cf168 100644 --- a/test/builtins/Unit/muloti4_test.c +++ b/test/builtins/Unit/muloti4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- muloti4_test.c - Test __muloti4 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulsc3_test.c b/test/builtins/Unit/mulsc3_test.c index eeb537ac13..b14e237d35 100644 --- a/test/builtins/Unit/mulsc3_test.c +++ b/test/builtins/Unit/mulsc3_test.c @@ -1,3 +1,6 @@ +// RUN: %clang_builtins %s %librt -lm -o %t && %run %t +// UNSUPPORTED: armhf-target-arch +// see pr 32475. //===-- mulsc3_test.c - Test __mulsc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulsf3vfp_test.c b/test/builtins/Unit/mulsf3vfp_test.c index 9fe88f29ba..8ee05510b0 100644 --- a/test/builtins/Unit/mulsf3vfp_test.c +++ b/test/builtins/Unit/mulsf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- mulsf3vfp_test.c - Test __mulsf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/multc3_test.c b/test/builtins/Unit/multc3_test.c index f6cf4ca875..5ef84670e4 100644 --- a/test/builtins/Unit/multc3_test.c +++ b/test/builtins/Unit/multc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- multc3_test.c - Test __multc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/multf3_test.c b/test/builtins/Unit/multf3_test.c index 42147550c7..2bce70793f 100644 --- a/test/builtins/Unit/multf3_test.c +++ b/test/builtins/Unit/multf3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- multf3_test.c - Test __multf3 ------------------------===// // // The LLVM Compiler Infrastructure @@ -15,6 +16,7 @@ #if __LDBL_MANT_DIG__ == 113 +#include "int_lib.h" #include "fp_test.h" // Returns: a * b diff --git a/test/builtins/Unit/multi3_test.c b/test/builtins/Unit/multi3_test.c index 04b1b8aa85..8227f24268 100644 --- a/test/builtins/Unit/multi3_test.c +++ b/test/builtins/Unit/multi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- multi3_test.c - Test __multi3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulvdi3_test.c b/test/builtins/Unit/mulvdi3_test.c index 7f16c4c4f6..0e10bbef18 100644 --- a/test/builtins/Unit/mulvdi3_test.c +++ b/test/builtins/Unit/mulvdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- mulvdi3_test.c - Test __mulvdi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulvsi3_test.c b/test/builtins/Unit/mulvsi3_test.c index 64df4fe24d..f62a5aa42a 100644 --- a/test/builtins/Unit/mulvsi3_test.c +++ b/test/builtins/Unit/mulvsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- mulvsi3_test.c - Test __mulvsi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulvti3_test.c b/test/builtins/Unit/mulvti3_test.c index bf2f7316ba..36e96ad60f 100644 --- a/test/builtins/Unit/mulvti3_test.c +++ b/test/builtins/Unit/mulvti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- mulvti3_test.c - Test __mulvti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/mulxc3_test.c b/test/builtins/Unit/mulxc3_test.c index e77e94fa9c..384c6ee114 100644 --- a/test/builtins/Unit/mulxc3_test.c +++ b/test/builtins/Unit/mulxc3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -lm -o %t && %run %t //===-- mulxc3_test.c - Test __mulxc3 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/nedf2vfp_test.c b/test/builtins/Unit/nedf2vfp_test.c index 69587d4689..536917af3d 100644 --- a/test/builtins/Unit/nedf2vfp_test.c +++ b/test/builtins/Unit/nedf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- nedf2vfp_test.c - Test __nedf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negdf2vfp_test.c b/test/builtins/Unit/negdf2vfp_test.c index 72bde7c018..776dca6185 100644 --- a/test/builtins/Unit/negdf2vfp_test.c +++ b/test/builtins/Unit/negdf2vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negdf2vfp_test.c - Test __negdf2vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negdi2_test.c b/test/builtins/Unit/negdi2_test.c index beccd71ee7..c85e91581c 100644 --- a/test/builtins/Unit/negdi2_test.c +++ b/test/builtins/Unit/negdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negdi2_test.c - Test __negdi2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negsf2vfp_test.c b/test/builtins/Unit/negsf2vfp_test.c index 1c9ba52921..e15e43c803 100644 --- a/test/builtins/Unit/negsf2vfp_test.c +++ b/test/builtins/Unit/negsf2vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negsf2vfp_test.c - Test __negsf2vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negti2_test.c b/test/builtins/Unit/negti2_test.c index b07597868c..bb7379cae2 100644 --- a/test/builtins/Unit/negti2_test.c +++ b/test/builtins/Unit/negti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negti2_test.c - Test __negti2 -------------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negvdi2_test.c b/test/builtins/Unit/negvdi2_test.c index 5c202e55cf..4e17c30895 100644 --- a/test/builtins/Unit/negvdi2_test.c +++ b/test/builtins/Unit/negvdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negvdi2_test.c - Test __negvdi2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negvsi2_test.c b/test/builtins/Unit/negvsi2_test.c index 6330803d5f..3deb4235bf 100644 --- a/test/builtins/Unit/negvsi2_test.c +++ b/test/builtins/Unit/negvsi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negvsi2_test.c - Test __negvsi2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/negvti2_test.c b/test/builtins/Unit/negvti2_test.c index 005f8a8aca..980f448693 100644 --- a/test/builtins/Unit/negvti2_test.c +++ b/test/builtins/Unit/negvti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- negvti2_test.c - Test __negvti2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/nesf2vfp_test.c b/test/builtins/Unit/nesf2vfp_test.c index 0ebcd6721c..bb01490877 100644 --- a/test/builtins/Unit/nesf2vfp_test.c +++ b/test/builtins/Unit/nesf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- nesf2vfp_test.c - Test __nesf2vfp ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/netf2_test.c b/test/builtins/Unit/netf2_test.c index bbd953ab59..c0b839d647 100644 --- a/test/builtins/Unit/netf2_test.c +++ b/test/builtins/Unit/netf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ netf2_test.c - Test __netf2------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/paritydi2_test.c b/test/builtins/Unit/paritydi2_test.c index 98220bd8cb..cc56eda57e 100644 --- a/test/builtins/Unit/paritydi2_test.c +++ b/test/builtins/Unit/paritydi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- paritydi2_test.c - Test __paritydi2 -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/paritysi2_test.c b/test/builtins/Unit/paritysi2_test.c index 175aeb2c9c..42d687f170 100644 --- a/test/builtins/Unit/paritysi2_test.c +++ b/test/builtins/Unit/paritysi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- paritysi2_test.c - Test __paritysi2 -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/parityti2_test.c b/test/builtins/Unit/parityti2_test.c index cc1e999f29..bcd26d0af8 100644 --- a/test/builtins/Unit/parityti2_test.c +++ b/test/builtins/Unit/parityti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- parityti2_test.c - Test __parityti2 -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/popcountdi2_test.c b/test/builtins/Unit/popcountdi2_test.c index bfd4977b41..1d52fb8dbd 100644 --- a/test/builtins/Unit/popcountdi2_test.c +++ b/test/builtins/Unit/popcountdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- popcountdi2_test.c - Test __popcountdi2 ----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/popcountsi2_test.c b/test/builtins/Unit/popcountsi2_test.c index 10b757d814..5eb88ac626 100644 --- a/test/builtins/Unit/popcountsi2_test.c +++ b/test/builtins/Unit/popcountsi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- popcountsi2_test.c - Test __popcountsi2 ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/popcountti2_test.c b/test/builtins/Unit/popcountti2_test.c index 3a3c3cb4fa..ef8b2c3836 100644 --- a/test/builtins/Unit/popcountti2_test.c +++ b/test/builtins/Unit/popcountti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- popcountti2_test.c - Test __popcountti2 ----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/powidf2_test.c b/test/builtins/Unit/powidf2_test.c index c499d9abae..210e3c3c75 100644 --- a/test/builtins/Unit/powidf2_test.c +++ b/test/builtins/Unit/powidf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- powidf2_test.cpp - Test __powidf2 ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/powisf2_test.c b/test/builtins/Unit/powisf2_test.c index 1186ef4af5..add4be43fa 100644 --- a/test/builtins/Unit/powisf2_test.c +++ b/test/builtins/Unit/powisf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- powisf2_test.cpp - Test __powisf2 ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/powitf2_test.c b/test/builtins/Unit/powitf2_test.c index 13c890a0dc..9d11b76cb0 100644 --- a/test/builtins/Unit/powitf2_test.c +++ b/test/builtins/Unit/powitf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- powitf2_test.cpp - Test __powitf2 ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/powixf2_test.c b/test/builtins/Unit/powixf2_test.c index a28f1f9696..bf5a812fa1 100644 --- a/test/builtins/Unit/powixf2_test.c +++ b/test/builtins/Unit/powixf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- powixf2_test.cpp - Test __powixf2 ---------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ppc/fixtfdi_test.c b/test/builtins/Unit/ppc/fixtfdi_test.c index b4865fb8e6..ea6c405637 100644 --- a/test/builtins/Unit/ppc/fixtfdi_test.c +++ b/test/builtins/Unit/ppc/fixtfdi_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include #include diff --git a/test/builtins/Unit/ppc/floatditf_test.c b/test/builtins/Unit/ppc/floatditf_test.c index 71ecf7c4b5..5c08ade4bd 100644 --- a/test/builtins/Unit/ppc/floatditf_test.c +++ b/test/builtins/Unit/ppc/floatditf_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include diff --git a/test/builtins/Unit/ppc/floatunditf_test.c b/test/builtins/Unit/ppc/floatunditf_test.c index 4d1ce08847..3e50128578 100644 --- a/test/builtins/Unit/ppc/floatunditf_test.c +++ b/test/builtins/Unit/ppc/floatunditf_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include diff --git a/test/builtins/Unit/ppc/qadd_test.c b/test/builtins/Unit/ppc/qadd_test.c index 6d4ca93c26..327fd21e8a 100644 --- a/test/builtins/Unit/ppc/qadd_test.c +++ b/test/builtins/Unit/ppc/qadd_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include "DD.h" diff --git a/test/builtins/Unit/ppc/qdiv_test.c b/test/builtins/Unit/ppc/qdiv_test.c index 8e4e75a50a..7530e428fa 100644 --- a/test/builtins/Unit/ppc/qdiv_test.c +++ b/test/builtins/Unit/ppc/qdiv_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include "DD.h" diff --git a/test/builtins/Unit/ppc/qmul_test.c b/test/builtins/Unit/ppc/qmul_test.c index fc5b46d336..dbe3536f30 100644 --- a/test/builtins/Unit/ppc/qmul_test.c +++ b/test/builtins/Unit/ppc/qmul_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include "DD.h" diff --git a/test/builtins/Unit/ppc/qsub_test.c b/test/builtins/Unit/ppc/qsub_test.c index 43bc7f4a85..e21224096f 100644 --- a/test/builtins/Unit/ppc/qsub_test.c +++ b/test/builtins/Unit/ppc/qsub_test.c @@ -1,3 +1,5 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_builtins %s -o %t && %run %t #include #include "DD.h" diff --git a/test/builtins/Unit/subdf3vfp_test.c b/test/builtins/Unit/subdf3vfp_test.c index 398494e1d4..75bfe4542a 100644 --- a/test/builtins/Unit/subdf3vfp_test.c +++ b/test/builtins/Unit/subdf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- subdf3vfp_test.c - Test __subdf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/subsf3vfp_test.c b/test/builtins/Unit/subsf3vfp_test.c index 8d529e5253..fb3839f233 100644 --- a/test/builtins/Unit/subsf3vfp_test.c +++ b/test/builtins/Unit/subsf3vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- subsf3vfp_test.c - Test __subsf3vfp -------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/subtf3_test.c b/test/builtins/Unit/subtf3_test.c index ad115a40ca..771242ba4a 100644 --- a/test/builtins/Unit/subtf3_test.c +++ b/test/builtins/Unit/subtf3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- subtf3_test.c - Test __subtf3 ------------------------===// // // The LLVM Compiler Infrastructure @@ -15,6 +16,7 @@ #if __LDBL_MANT_DIG__ == 113 +#include "int_lib.h" #include "fp_test.h" // Returns: a - b diff --git a/test/builtins/Unit/subvdi3_test.c b/test/builtins/Unit/subvdi3_test.c index 96e825cafb..7606e27d16 100644 --- a/test/builtins/Unit/subvdi3_test.c +++ b/test/builtins/Unit/subvdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- subvdi3_test.c - Test __subvdi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/subvsi3_test.c b/test/builtins/Unit/subvsi3_test.c index 03ef5045d2..f52a12b605 100644 --- a/test/builtins/Unit/subvsi3_test.c +++ b/test/builtins/Unit/subvsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- subvsi3_test.c - Test __subvsi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/subvti3_test.c b/test/builtins/Unit/subvti3_test.c index 40eb518691..66806ba0bb 100644 --- a/test/builtins/Unit/subvti3_test.c +++ b/test/builtins/Unit/subvti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- subvti3_test.c - Test __subvti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/trampoline_setup_test.c b/test/builtins/Unit/trampoline_setup_test.c index dc30fb6df6..5a75724f44 100644 --- a/test/builtins/Unit/trampoline_setup_test.c +++ b/test/builtins/Unit/trampoline_setup_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -fnested-functions -o %t && %run %t /* ===-- trampoline_setup_test.c - Test __trampoline_setup -----------------=== * * The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/truncdfhf2_test.c b/test/builtins/Unit/truncdfhf2_test.c index 6627a001ea..b172811a13 100644 --- a/test/builtins/Unit/truncdfhf2_test.c +++ b/test/builtins/Unit/truncdfhf2_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===--------------- truncdfhf2_test.c - Test __truncdfhf2 ----------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/truncdfsf2_test.c b/test/builtins/Unit/truncdfsf2_test.c index a208a3a56b..04bebf4c6c 100644 --- a/test/builtins/Unit/truncdfsf2_test.c +++ b/test/builtins/Unit/truncdfsf2_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===--------------- truncdfsf2_test.c - Test __truncdfsf2 ----------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/truncdfsf2vfp_test.c b/test/builtins/Unit/truncdfsf2vfp_test.c index efe6f97ab5..f034dd2da2 100644 --- a/test/builtins/Unit/truncdfsf2vfp_test.c +++ b/test/builtins/Unit/truncdfsf2vfp_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- truncdfsf2vfp_test.c - Test __truncdfsf2vfp -----------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/truncsfhf2_test.c b/test/builtins/Unit/truncsfhf2_test.c index 5bc3c8e22d..2240c14a5d 100644 --- a/test/builtins/Unit/truncsfhf2_test.c +++ b/test/builtins/Unit/truncsfhf2_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===--------------- truncsfhf2_test.c - Test __truncsfhf2 ----------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/trunctfdf2_test.c b/test/builtins/Unit/trunctfdf2_test.c index 0366a8ce99..1d8c2bf05d 100644 --- a/test/builtins/Unit/trunctfdf2_test.c +++ b/test/builtins/Unit/trunctfdf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-------------- trunctfdf2_test.c - Test __trunctfdf2 -----------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/trunctfsf2_test.c b/test/builtins/Unit/trunctfsf2_test.c index a6b922ce12..177508319a 100644 --- a/test/builtins/Unit/trunctfsf2_test.c +++ b/test/builtins/Unit/trunctfsf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===--------------- trunctfsf2_test.c - Test __trunctfsf2 ----------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ucmpdi2_test.c b/test/builtins/Unit/ucmpdi2_test.c index 65ae4fce0e..ee815454a2 100644 --- a/test/builtins/Unit/ucmpdi2_test.c +++ b/test/builtins/Unit/ucmpdi2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ucmpdi2_test.c - Test __ucmpdi2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/ucmpti2_test.c b/test/builtins/Unit/ucmpti2_test.c index 826cd6439d..17a857fb59 100644 --- a/test/builtins/Unit/ucmpti2_test.c +++ b/test/builtins/Unit/ucmpti2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- ucmpti2_test.c - Test __ucmpti2 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/udivdi3_test.c b/test/builtins/Unit/udivdi3_test.c index 48c99e3097..5858bc7f92 100644 --- a/test/builtins/Unit/udivdi3_test.c +++ b/test/builtins/Unit/udivdi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- udivdi3_test.c - Test __udivdi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/udivmoddi4_test.c b/test/builtins/Unit/udivmoddi4_test.c index 79af1eeff6..a5024bae01 100644 --- a/test/builtins/Unit/udivmoddi4_test.c +++ b/test/builtins/Unit/udivmoddi4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- udivmoddi4_test.c - Test __udivmoddi4 -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/udivmodsi4_test.c b/test/builtins/Unit/udivmodsi4_test.c index 4c14e297d5..320018cba5 100644 --- a/test/builtins/Unit/udivmodsi4_test.c +++ b/test/builtins/Unit/udivmodsi4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- udivmodsi4_test.c - Test __udivmodsi4 -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/udivmodti4_test.c b/test/builtins/Unit/udivmodti4_test.c index c4246613d2..b07ce8c242 100644 --- a/test/builtins/Unit/udivmodti4_test.c +++ b/test/builtins/Unit/udivmodti4_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- udivmodti4_test.c - Test __udivmodti4 -----------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/udivsi3_test.c b/test/builtins/Unit/udivsi3_test.c index 49053865bf..7adf30a151 100644 --- a/test/builtins/Unit/udivsi3_test.c +++ b/test/builtins/Unit/udivsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- udivsi3_test.c - Test __udivsi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/udivti3_test.c b/test/builtins/Unit/udivti3_test.c index f24ff03b24..81cedadff2 100644 --- a/test/builtins/Unit/udivti3_test.c +++ b/test/builtins/Unit/udivti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- udivti3_test.c - Test __udivti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/umoddi3_test.c b/test/builtins/Unit/umoddi3_test.c index b46fb40228..b1382b621a 100644 --- a/test/builtins/Unit/umoddi3_test.c +++ b/test/builtins/Unit/umoddi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- umoddi3_test.c - Test __umoddi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/umodsi3_test.c b/test/builtins/Unit/umodsi3_test.c index 3655da1395..c430538748 100644 --- a/test/builtins/Unit/umodsi3_test.c +++ b/test/builtins/Unit/umodsi3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- umodsi3_test.c - Test __umodsi3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/umodti3_test.c b/test/builtins/Unit/umodti3_test.c index 21e82a4a21..5bce0258ae 100644 --- a/test/builtins/Unit/umodti3_test.c +++ b/test/builtins/Unit/umodti3_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===-- umodti3_test.c - Test __umodti3 -----------------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/unorddf2vfp_test.c b/test/builtins/Unit/unorddf2vfp_test.c index be17e41138..21938d044a 100644 --- a/test/builtins/Unit/unorddf2vfp_test.c +++ b/test/builtins/Unit/unorddf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- unorddf2vfp_test.c - Test __unorddf2vfp ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/unordsf2vfp_test.c b/test/builtins/Unit/unordsf2vfp_test.c index e40d71f77f..712665294c 100644 --- a/test/builtins/Unit/unordsf2vfp_test.c +++ b/test/builtins/Unit/unordsf2vfp_test.c @@ -1,3 +1,5 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t + //===-- unordsf2vfp_test.c - Test __unordsf2vfp ---------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/builtins/Unit/unordtf2_test.c b/test/builtins/Unit/unordtf2_test.c index 114cd8cb38..22907ce767 100644 --- a/test/builtins/Unit/unordtf2_test.c +++ b/test/builtins/Unit/unordtf2_test.c @@ -1,3 +1,4 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t //===------------ unordtf2_test.c - Test __unordtf2------------------------===// // // The LLVM Compiler Infrastructure diff --git a/test/cfi/CMakeLists.txt b/test/cfi/CMakeLists.txt index bd51eacb66..c3123a8204 100644 --- a/test/cfi/CMakeLists.txt +++ b/test/cfi/CMakeLists.txt @@ -34,7 +34,7 @@ if(NOT COMPILER_RT_STANDALONE_BUILD) LTO ) endif() - if(WIN32 AND COMPILER_RT_HAS_LLD_SOURCES) + if(WIN32 AND COMPILER_RT_HAS_LLD) list(APPEND CFI_TEST_DEPS lld ) diff --git a/test/cfi/bad-split.cpp b/test/cfi/bad-split.cpp new file mode 100644 index 0000000000..53504bd271 --- /dev/null +++ b/test/cfi/bad-split.cpp @@ -0,0 +1,21 @@ +// GlobalSplit used to lose type metadata for classes with virtual bases but no virtual methods. +// RUN: %clangxx_cfi -o %t1 %s && %t1 + +// UNSUPPORTED: win32 + +struct Z { +}; + +struct ZZ : public virtual Z { +}; + +struct A : public ZZ { +}; + +struct B : public A { +}; + +int main() { + A* a = new B(); + B *b = (B*)a; +} diff --git a/test/esan/TestCases/workingset-samples.cpp b/test/esan/TestCases/workingset-samples.cpp index cf198d2f39..7db2110b92 100644 --- a/test/esan/TestCases/workingset-samples.cpp +++ b/test/esan/TestCases/workingset-samples.cpp @@ -8,7 +8,6 @@ #include const int size = 0x1 << 25; // 523288 cache lines -const int iters = 6; int main(int argc, char **argv) { char *buf = (char *)mmap(0, size, PROT_READ | PROT_WRITE, diff --git a/test/lit.common.cfg b/test/lit.common.cfg index bffdc43314..d59d7d6681 100644 --- a/test/lit.common.cfg +++ b/test/lit.common.cfg @@ -11,8 +11,19 @@ import subprocess import lit.formats import lit.util -# Setup test format. Use bash on Unix and the lit shell on Windows. -execute_external = (not sys.platform in ['win32']) + +# Choose between lit's internal shell pipeline runner and a real shell. If +# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override. +use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL") +if use_lit_shell: + # 0 is external, "" is default, and everything else is internal. + execute_external = (use_lit_shell == "0") +else: + # Otherwise we default to internal on Windows and external elsewhere, as + # bash on Windows is usually very slow. + execute_external = (not sys.platform in ['win32']) + +# Setup test format. config.test_format = lit.formats.ShTest(execute_external) if execute_external: config.available_features.add('shell') @@ -126,10 +137,11 @@ if config.can_symbolize: lit.util.usePlatformSdkOnDarwin(config, lit_config) if config.host_os == 'Darwin': + osx_version = (10, 0, 0) try: osx_version = subprocess.check_output(["sw_vers", "-productVersion"]) osx_version = tuple(int(x) for x in osx_version.split('.')) - config.darwin_osx_version = osx_version + if len(osx_version) == 2: osx_version = (osx_version[0], osx_version[1], 0) if osx_version >= (10, 11): config.available_features.add('osx-autointerception') config.available_features.add('osx-ld64-live_support') @@ -142,6 +154,8 @@ if config.host_os == 'Darwin': except: pass + config.darwin_osx_version = osx_version + # Detect x86_64h try: output = subprocess.check_output(["sysctl", "hw.cpusubtype"]) @@ -192,7 +206,8 @@ elif config.host_os == 'Linux' and is_linux_lto_supported(): elif config.host_os == 'Windows' and is_windows_lto_supported(): config.lto_supported = True config.lto_launch = [] - config.lto_flags = ["-fuse-ld=lld"] + # FIXME: Remove -nopdb when PDB writing is ready. + config.lto_flags = ["-fuse-ld=lld -Wl,-nopdb"] else: config.lto_supported = False diff --git a/test/lit.common.configured.in b/test/lit.common.configured.in index 862d06bf29..387f4d4a74 100644 --- a/test/lit.common.configured.in +++ b/test/lit.common.configured.in @@ -26,7 +26,7 @@ set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@) set_default("compiler_rt_libdir", "@COMPILER_RT_LIBRARY_OUTPUT_DIR@") set_default("emulator", "@COMPILER_RT_EMULATOR@") set_default("sanitizer_can_use_cxxabi", @SANITIZER_CAN_USE_CXXABI_PYBOOL@) -set_default("has_lld", @COMPILER_RT_HAS_LLD_SOURCES_PYBOOL@) +set_default("has_lld", @COMPILER_RT_HAS_LLD_PYBOOL@) set_default("can_symbolize", @CAN_SYMBOLIZE@) config.available_features.add('target-is-%s' % config.target_arch) diff --git a/test/lsan/TestCases/use_tls_dynamic.cc b/test/lsan/TestCases/use_tls_dynamic.cc index 770ffafe22..c20232dc34 100644 --- a/test/lsan/TestCases/use_tls_dynamic.cc +++ b/test/lsan/TestCases/use_tls_dynamic.cc @@ -5,6 +5,7 @@ // RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s // RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 // RUN: LSAN_OPTIONS="" %run %t 2>&1 +// UNSUPPORTED: i386-linux,i686-linux #ifndef BUILD_DSO #include diff --git a/test/msan/fread_fwrite.cc b/test/msan/fread_fwrite.cc new file mode 100644 index 0000000000..3d500342a1 --- /dev/null +++ b/test/msan/fread_fwrite.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -g %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %t 1 + +#include +#include + +int test_fread() { + FILE *f = fopen("/dev/zero", "r"); + char c; + unsigned read = fread(&c, sizeof(c), 1, f); + fclose(f); + if (c == '1') // No error + return 1; + return 0; +} + +int test_fwrite() { + FILE *f = fopen("/dev/null", "w"); + char c; + if (fwrite(&c, sizeof(c), 1, f) != sizeof(c)) // BOOM + return 1; + return fclose(f); +} + +int main(int argc, char *argv[]) { + if (argc > 1) + test_fread(); + else + test_fwrite(); + return 0; +} + +// CHECK: Uninitialized bytes in __interceptor_fwrite at offset 0 inside diff --git a/test/msan/getloadavg.cc b/test/msan/getloadavg.cc new file mode 100644 index 0000000000..7facd580da --- /dev/null +++ b/test/msan/getloadavg.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t + +#define _BSD_SOURCE +#include +#include + +#include + +int main(void) { + double x[4]; + int ret = getloadavg(x, 3); + assert(ret > 0); + assert(ret <= 3); + assert(__msan_test_shadow(x, sizeof(double) * ret) == -1); + assert(__msan_test_shadow(&x[ret], sizeof(double)) == 0); +} diff --git a/test/msan/wcsncpy.cc b/test/msan/wcsncpy.cc new file mode 100644 index 0000000000..f582c37b7a --- /dev/null +++ b/test/msan/wcsncpy.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +#include +#include + +#include + +int main() { + const wchar_t *s = L"abc"; + assert(wcslen(s) == 3); + + wchar_t s2[5]; + assert(wcsncpy(s2, s, 3) == s2); + assert(__msan_test_shadow(&s2, 5 * sizeof(wchar_t)) == 3 * sizeof(wchar_t)); + assert(wcsncpy(s2, s, 5) == s2); + assert(__msan_test_shadow(&s2, 5 * sizeof(wchar_t)) == -1); + + wchar_t s3[5]; + assert(wcsncpy(s3, s, 2) == s3); + assert(__msan_test_shadow(&s3, 5 * sizeof(wchar_t)) == 2 * sizeof(wchar_t)); + + __msan_allocated_memory(&s2[1], sizeof(wchar_t)); + wchar_t s4[5]; + assert(wcsncpy(s4, s2, 3) == s4); + __msan_check_mem_is_initialized(&s4, sizeof(s4)); +} +// CHECK: Uninitialized bytes in __msan_check_mem_is_initialized +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK: in main {{.*}}wcsncpy.cc:26 + +// CHECK: Uninitialized value was stored to memory at +// CHECK: in {{[^\s]*}}wcsncpy +// CHECK: in main {{.*}}wcsncpy.cc:25 + +// CHECK: Memory was marked as uninitialized +// CHECK: in __msan_allocated_memory +// CHECK: in main {{.*}}wcsncpy.cc:23 diff --git a/test/sanitizer_common/TestCases/Linux/sysconf_interceptor_bypass_test.cc b/test/sanitizer_common/TestCases/Linux/sysconf_interceptor_bypass_test.cc new file mode 100644 index 0000000000..eb4deace06 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/sysconf_interceptor_bypass_test.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx -O2 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include + +// getauxval() used instead of sysconf() in GetPageSize() is defined starting +// glbc version 2.16. +#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 16) +extern "C" long sysconf(int name) { + fprintf(stderr, "sysconf wrapper called\n"); + return 0; +} +#endif // defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 16) + +int main() { + // All we need to check is that the sysconf() interceptor defined above was + // not called. Should it get called, it will crash right there, any + // instrumented code executed before sanitizer init is finished will crash + // accessing non-initialized sanitizer internals. Even if it will not crash + // in some configuration, it should never be called anyway. + fprintf(stderr, "Passed\n"); + // CHECK-NOT: sysconf wrapper called + // CHECK: Passed + // CHECK-NOT: sysconf wrapper called + return 0; +} diff --git a/test/scudo/threads.cpp b/test/scudo/threads.cpp new file mode 100644 index 0000000000..d9f4a86c1a --- /dev/null +++ b/test/scudo/threads.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_scudo %s -o %t +// RUN: SCUDO_OPTIONS="QuarantineSizeMb=0:ThreadLocalQuarantineSizeKb=0" %run %t 5 1000000 2>&1 +// RUN: SCUDO_OPTIONS="QuarantineSizeMb=1:ThreadLocalQuarantineSizeKb=64" %run %t 5 1000000 2>&1 + +// Tests parallel allocations and deallocations of memory chunks from a number +// of concurrent threads, with and without quarantine. +// This test passes if everything executes properly without crashing. + +#include +#include +#include +#include + +#include + +int num_threads; +int total_num_alloc; +const int kMaxNumThreads = 500; +pthread_t tid[kMaxNumThreads]; + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +bool go = false; + +void *thread_fun(void *arg) { + pthread_mutex_lock(&mutex); + while (!go) pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < total_num_alloc / num_threads; i++) { + void *p = malloc(10); + __asm__ __volatile__("" : : "r"(p) : "memory"); + free(p); + } + return 0; +} + +int main(int argc, char** argv) { + assert(argc == 3); + num_threads = atoi(argv[1]); + assert(num_threads > 0); + assert(num_threads <= kMaxNumThreads); + total_num_alloc = atoi(argv[2]); + assert(total_num_alloc > 0); + + printf("%d threads, %d allocations in each\n", num_threads, + total_num_alloc / num_threads); + fprintf(stderr, "Heap size before: %zd\n", __sanitizer_get_heap_size()); + fprintf(stderr, "Allocated bytes before: %zd\n", + __sanitizer_get_current_allocated_bytes()); + + for (int i = 0; i < num_threads; i++) + pthread_create(&tid[i], 0, thread_fun, 0); + pthread_mutex_lock(&mutex); + go = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < num_threads; i++) + pthread_join(tid[i], 0); + + fprintf(stderr, "Heap size after: %zd\n", __sanitizer_get_heap_size()); + fprintf(stderr, "Allocated bytes after: %zd\n", + __sanitizer_get_current_allocated_bytes()); + + return 0; +} diff --git a/test/tsan/Darwin/debug_external.cc b/test/tsan/Darwin/debug_external.cc index ac05a01c41..217690fc5d 100644 --- a/test/tsan/Darwin/debug_external.cc +++ b/test/tsan/Darwin/debug_external.cc @@ -6,6 +6,8 @@ #include #include +#include "../test.h" + extern "C" { void __tsan_on_report(void *report); int __tsan_get_report_loc(void *report, unsigned long idx, const char **type, @@ -20,11 +22,13 @@ void __tsan_external_assign_tag(void *addr, void *tag); } void *Thread(void *arg) { + barrier_wait(&barrier); *((long *)arg) = 42; return NULL; } int main() { + barrier_init(&barrier, 2); void *tag = __tsan_external_register_tag("MyObject"); long *obj = (long *)malloc(sizeof(long)); fprintf(stderr, "obj = %p\n", obj); @@ -34,6 +38,7 @@ int main() { pthread_t t; pthread_create(&t, 0, Thread, obj); *obj = 41; + barrier_wait(&barrier); pthread_join(t, 0); fprintf(stderr, "Done.\n"); return 0; diff --git a/test/tsan/Darwin/external.cc b/test/tsan/Darwin/external.cc index 3c5e71a0f8..2605480d7b 100644 --- a/test/tsan/Darwin/external.cc +++ b/test/tsan/Darwin/external.cc @@ -1,6 +1,15 @@ -// RUN: %clangxx_tsan %s -o %t-lib-instrumented.dylib -shared -DSHARED_LIB -// RUN: %clangxx_tsan %s -o %t-lib-noninstrumented.dylib -shared -DSHARED_LIB -fno-sanitize=thread -// RUN: %clangxx_tsan %s -o %t-lib-noninstrumented-callbacks.dylib -shared -DSHARED_LIB -fno-sanitize=thread -DUSE_TSAN_CALLBACKS +// RUN: %clangxx_tsan %s -shared -DSHARED_LIB \ +// RUN: -o %t-lib-instrumented.dylib \ +// RUN: -install_name @rpath/`basename %t-lib-instrumented.dylib` + +// RUN: %clangxx_tsan %s -shared -DSHARED_LIB -fno-sanitize=thread \ +// RUN: -o %t-lib-noninstrumented.dylib \ +// RUN: -install_name @rpath/`basename %t-lib-noninstrumented.dylib` + +// RUN: %clangxx_tsan %s -shared -DSHARED_LIB -fno-sanitize=thread -DUSE_TSAN_CALLBACKS \ +// RUN: -o %t-lib-noninstrumented-callbacks.dylib \ +// RUN: -install_name @rpath/`basename %t-lib-noninstrumented-callbacks.dylib` + // RUN: %clangxx_tsan %s %t-lib-instrumented.dylib -o %t-lib-instrumented // RUN: %clangxx_tsan %s %t-lib-noninstrumented.dylib -o %t-lib-noninstrumented // RUN: %clangxx_tsan %s %t-lib-noninstrumented-callbacks.dylib -o %t-lib-noninstrumented-callbacks diff --git a/test/tsan/Darwin/gcd-after-null.mm b/test/tsan/Darwin/gcd-after-null.mm new file mode 100644 index 0000000000..7c9913c0fb --- /dev/null +++ b/test/tsan/Darwin/gcd-after-null.mm @@ -0,0 +1,23 @@ +// Regression test to make sure we don't crash when dispatch_after is called with a NULL queue. + +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 | FileCheck %s + +#import + +int main(int argc, const char *argv[]) { + fprintf(stderr, "start\n"); + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_MSEC)), NULL, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetMain()); + }); + }); + CFRunLoopRun(); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: done diff --git a/test/tsan/Darwin/xpc-cancel.mm b/test/tsan/Darwin/xpc-cancel.mm new file mode 100644 index 0000000000..5e326b7e49 --- /dev/null +++ b/test/tsan/Darwin/xpc-cancel.mm @@ -0,0 +1,37 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 | FileCheck %s + +#import +#import + +long global; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t server_q = dispatch_queue_create("server.queue", DISPATCH_QUEUE_CONCURRENT); + xpc_connection_t server_conn = xpc_connection_create(NULL, server_q); + + xpc_connection_set_event_handler(server_conn, ^(xpc_object_t client) { + if (client == XPC_ERROR_CONNECTION_INTERRUPTED || client == XPC_ERROR_CONNECTION_INVALID) { + global = 43; + + dispatch_async(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + } + }); + xpc_connection_resume(server_conn); + + global = 42; + + xpc_connection_cancel(server_conn); + + CFRunLoopRun(); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Linux/pie_no_aslr.cc b/test/tsan/Linux/pie_no_aslr.cc new file mode 100644 index 0000000000..b99342da06 --- /dev/null +++ b/test/tsan/Linux/pie_no_aslr.cc @@ -0,0 +1,6 @@ +// RUN: %clang_tsan %s -pie -fPIE -o %t && %run setarch x86_64 -R %t +// REQUIRES: x86_64-target-arch + +int main() { + return 0; +} diff --git a/test/tsan/atomic_hle.cc b/test/tsan/atomic_hle.cc new file mode 100644 index 0000000000..345e363c2b --- /dev/null +++ b/test/tsan/atomic_hle.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include + +#ifndef __ATOMIC_HLE_ACQUIRE +#define __ATOMIC_HLE_ACQUIRE (1 << 16) +#endif +#ifndef __ATOMIC_HLE_RELEASE +#define __ATOMIC_HLE_RELEASE (1 << 17) +#endif + +int main() { + volatile int x = 0; + //__atomic_fetch_add(&x, 1, __ATOMIC_ACQUIRE | __ATOMIC_HLE_ACQUIRE); + //__atomic_store_n(&x, 0, __ATOMIC_RELEASE | __ATOMIC_HLE_RELEASE); + __tsan_atomic32_fetch_add(&x, 1, + (__tsan_memory_order)(__ATOMIC_ACQUIRE | __ATOMIC_HLE_ACQUIRE)); + __tsan_atomic32_store(&x, 0, + (__tsan_memory_order)(__ATOMIC_RELEASE | __ATOMIC_HLE_RELEASE)); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE + diff --git a/test/tsan/custom_mutex.h b/test/tsan/custom_mutex.h new file mode 100644 index 0000000000..675ad59b32 --- /dev/null +++ b/test/tsan/custom_mutex.h @@ -0,0 +1,91 @@ +#include "test.h" +#include +#include +#include + +// A very primitive mutex annotated with tsan annotations. +class Mutex { + public: + Mutex(bool prof = true) + : prof_(prof) + , locked_(false) + , seq_(0) { + __tsan_mutex_create(this, 0); + } + + ~Mutex() { + __tsan_mutex_destroy(this, 0); + } + + void Lock() { + __tsan_mutex_pre_lock(this, 0); + LockImpl(); + __tsan_mutex_post_lock(this, 0, 0); + } + + bool TryLock() { + __tsan_mutex_pre_lock(this, __tsan_mutex_try_lock); + bool ok = TryLockImpl(); + __tsan_mutex_post_lock(this, __tsan_mutex_try_lock | + (ok ? 0 : __tsan_mutex_try_lock_failed), 0); + return ok; + } + + void Unlock() { + __tsan_mutex_pre_unlock(this, 0); + UnlockImpl(); + __tsan_mutex_post_unlock(this, 0); + } + + void Wait() { + for (int seq = seq_; seq == seq_;) { + Unlock(); + usleep(100); + Lock(); + } + } + + void Broadcast() { + __tsan_mutex_pre_signal(this, 0); + LockImpl(false); + seq_++; + UnlockImpl(); + __tsan_mutex_post_signal(this, 0); + } + + private: + const bool prof_; + std::atomic locked_; + int seq_; + + // This models mutex profiling subsystem. + static Mutex prof_mu_; + static int prof_data_; + + void LockImpl(bool prof = true) { + while (!TryLockImpl()) + usleep(100); + if (prof && prof_) + Prof(); + } + + bool TryLockImpl() { + return !locked_.exchange(true); + } + + void UnlockImpl() { + locked_.store(false); + } + + void Prof() { + // This happens inside of mutex lock annotations. + __tsan_mutex_pre_divert(this, 0); + prof_mu_.Lock(); + prof_data_++; + prof_mu_.Unlock(); + __tsan_mutex_post_divert(this, 0); + } +}; + +Mutex Mutex::prof_mu_(false); +int Mutex::prof_data_; diff --git a/test/tsan/custom_mutex0.cc b/test/tsan/custom_mutex0.cc new file mode 100644 index 0000000000..998385ca1c --- /dev/null +++ b/test/tsan/custom_mutex0.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_tsan -O1 --std=c++11 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +// Test that custom annoations provide normal mutex synchronization +// (no race reports for properly protected critical sections). + +Mutex mu; +long data; + +void *thr(void *arg) { + barrier_wait(&barrier); + mu.Lock(); + data++; + mu.Unlock(); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + barrier_wait(&barrier); + mu.Lock(); + data++; + mu.Unlock(); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/custom_mutex1.cc b/test/tsan/custom_mutex1.cc new file mode 100644 index 0000000000..06186515f6 --- /dev/null +++ b/test/tsan/custom_mutex1.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 --std=c++11 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +// Test that failed TryLock does not induce parasitic synchronization. + +Mutex mu; +long data; + +void *thr(void *arg) { + mu.Lock(); + data++; + mu.Unlock(); + mu.Lock(); + barrier_wait(&barrier); + barrier_wait(&barrier); + mu.Unlock(); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + barrier_wait(&barrier); + if (mu.TryLock()) { + fprintf(stderr, "TryLock succeeded, should not\n"); + exit(0); + } + data++; + barrier_wait(&barrier); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 8 at {{.*}} by main thread: +// CHECK-NEXT: #0 main {{.*}}custom_mutex1.cc:29 +// CHECK: DONE diff --git a/test/tsan/custom_mutex2.cc b/test/tsan/custom_mutex2.cc new file mode 100644 index 0000000000..9329cbc3f4 --- /dev/null +++ b/test/tsan/custom_mutex2.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 --std=c++11 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +// Test that Broadcast does not induce parasitic synchronization. + +Mutex mu; +long data; + +void *thr(void *arg) { + barrier_wait(&barrier); + mu.Lock(); + data++; + mu.Unlock(); + data++; + mu.Broadcast(); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + mu.Lock(); + barrier_wait(&barrier); + while (data == 0) + mu.Wait(); + mu.Unlock(); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/ubsan/TestCases/Misc/nullability.c b/test/ubsan/TestCases/Misc/nullability.c new file mode 100644 index 0000000000..583b8ec125 --- /dev/null +++ b/test/ubsan/TestCases/Misc/nullability.c @@ -0,0 +1,62 @@ +// RUN: %clang -w -fsanitize=nullability-arg,nullability-assign,nullability-return %s -O3 -o %t +// RUN: %run %t foo 2>&1 | count 0 +// RUN: %run %t 2>&1 | FileCheck %s + +// CHECK: nullability.c:[[@LINE+2]]:51: runtime error: null pointer returned from function declared to never return null +// CHECK-NEXT: nullability.c:[[@LINE+1]]:6: note: _Nonnull return type annotation specified here +int *_Nonnull nonnull_retval1(int *p) { return p; } + +// CHECK: nullability.c:1001:22: runtime error: null pointer passed as argument 2, which is declared to never be null +// CHECK-NEXT: nullability.c:[[@LINE+1]]:56: note: _Nonnull type annotation specified here +int *_Nonnull nonnull_retval2(int *_Nonnull arg1, int *_Nonnull arg2, + int *_Nullable arg3, int *arg4, int arg5, ...) { + return arg1; +} + +// CHECK: nullability.c:1002:15: runtime error: null pointer passed as argument 1, which is declared to never be null +// CHECK-NEXT: nullability.c:[[@LINE+1]]:23: note: _Nonnull type annotation specified here +void nonnull_arg(int *_Nonnull p) {} + +void nonnull_assign1(int *p) { + int *_Nonnull local; +// CHECK: nullability.c:[[@LINE+1]]:9: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull' + local = p; +} + +void nonnull_assign2(int *p) { + int *_Nonnull arr[1]; + // CHECK: nullability.c:[[@LINE+1]]:10: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull' + arr[0] = p; +} + +struct S1 { + int *_Nonnull mptr; +}; + +void nonnull_assign3(int *p) { + struct S1 s; + // CHECK: nullability.c:[[@LINE+1]]:10: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull' + s.mptr = p; +} + +// CHECK: nullability.c:[[@LINE+1]]:52: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull' +void nonnull_init1(int *p) { int *_Nonnull local = p; } + +// CHECK: nullability.c:[[@LINE+2]]:53: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull' +// CHECK: nullability.c:[[@LINE+1]]:56: runtime error: _Nonnull binding to null pointer of type 'int * _Nonnull' +void nonnull_init2(int *p) { int *_Nonnull arr[] = {p, p}; } + +int main(int argc, char **argv) { + int *p = (argc > 1) ? &argc : ((int *)0); + +#line 1000 + nonnull_retval1(p); + nonnull_retval2(p, p, p, p, 0, 0, 0, 0); + nonnull_arg(p); + nonnull_assign1(p); + nonnull_assign2(p); + nonnull_assign3(p); + nonnull_init1(p); + nonnull_init2(p); + return 0; +} diff --git a/test/xray/TestCases/Linux/fdr-mode.cc b/test/xray/TestCases/Linux/fdr-mode.cc new file mode 100644 index 0000000000..4aa3964c9d --- /dev/null +++ b/test/xray/TestCases/Linux/fdr-mode.cc @@ -0,0 +1,82 @@ +// RUN: %clangxx_xray -g -std=c++11 %s -o %t +// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-logging-test- xray_fdr_log=true verbosity=1" %run %t 2>&1 | FileCheck %s +// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-logging-test-* | head -1`" | FileCheck %s --check-prefix TRACE +// RUN: rm fdr-logging-test-* +// FIXME: Make llvm-xray work on non-x86_64 as well. +// REQUIRES: x86_64-linux + +#include "xray/xray_log_interface.h" +#include +#include +#include +#include +#include +#include +#include + +constexpr auto kBufferSize = 16384; +constexpr auto kBufferMax = 10; + +thread_local uint64_t var = 0; +[[clang::xray_always_instrument]] void __attribute__((noinline)) fC() { ++var; } + +[[clang::xray_always_instrument]] void __attribute__((noinline)) fB() { fC(); } + +[[clang::xray_always_instrument]] void __attribute__((noinline)) fA() { fB(); } + +int main(int argc, char *argv[]) { + using namespace __xray; + FDRLoggingOptions Options; + std::cout << "Logging before init." << std::endl; + // CHECK: Logging before init. + auto status = __xray_log_init(kBufferSize, kBufferMax, &Options, + sizeof(FDRLoggingOptions)); + assert(status == XRayLogInitStatus::XRAY_LOG_INITIALIZED); + std::cout << "Init status " << status << std::endl; + // CHECK: Init status {{.*}} + std::cout << "Patching..." << std::endl; + // CHECK: Patching... + __xray_patch(); + fA(); + fC(); + fB(); + fA(); + fC(); + std::thread other_thread([]() { + fC(); + fB(); + fA(); + }); + other_thread.join(); + std::cout << "Joined" << std::endl; + // CHECK: Joined + std::cout << "Finalize status " << __xray_log_finalize() << std::endl; + // CHECK: Finalize status {{.*}} + fC(); + std::cout << "Main execution var = " << var << std::endl; + // CHECK: Main execution var = 6 + std::cout << "Flush status " << __xray_log_flushLog() << std::endl; + // CHECK: Flush status {{.*}} + __xray_unpatch(); + return 0; +} + + + +// Check that we're able to see two threads, each entering and exiting fA(). +// TRACE-DAG: - { type: 0, func-id: [[FIDA:[0-9]+]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } +// TRACE-DAG: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } +// +// Do the same as above for fC() +// TRACE-DAG: - { type: 0, func-id: [[FIDC:[0-9]+]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } +// TRACE-DAG: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } + +// Do the same as above for fB() +// TRACE-DAG: - { type: 0, func-id: [[FIDB:[0-9]+]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } +// TRACE-DAG: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } diff --git a/test/xray/TestCases/Linux/fdr-thread-order.cc b/test/xray/TestCases/Linux/fdr-thread-order.cc new file mode 100644 index 0000000000..5edef2be2d --- /dev/null +++ b/test/xray/TestCases/Linux/fdr-thread-order.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_xray -g -std=c++11 %s -o %t +// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-thread-order. xray_fdr_log=true verbosity=1" %run %t 2>&1 | FileCheck %s +// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-thread-order.* | head -1`" | FileCheck %s --check-prefix TRACE +// RUN: rm fdr-thread-order.* +// FIXME: Make llvm-xray work on non-x86_64 as well. +// REQUIRES: x86_64-linux +#include "xray/xray_log_interface.h" +#include +#include + +constexpr auto kBufferSize = 16384; +constexpr auto kBufferMax = 10; + +thread_local uint64_t var = 0; +[[clang::xray_always_instrument]] void __attribute__((noinline)) f1() { ++var; } +[[clang::xray_always_instrument]] void __attribute__((noinline)) f2() { ++var; } + +int main(int argc, char *argv[]) { + using namespace __xray; + FDRLoggingOptions Options; + assert(__xray_log_init(kBufferSize, kBufferMax, &Options, + sizeof(FDRLoggingOptions)) == + XRayLogInitStatus::XRAY_LOG_INITIALIZED); + __xray_patch(); + std::thread t1([] { f1(); }); + std::thread t2([] { f2(); }); + t1.join(); + t2.join(); + __xray_log_finalize(); + __xray_log_flushLog(); + // CHECK: =={{[0-9]+}}==XRay: Log file in '{{.*}}' +} + +// We want to make sure that the order of the function log doesn't matter. +// TRACE-DAG: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE-DAG: - { type: 0, func-id: [[FID2:[0-9]+]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } +// TRACE-DAG: - { type: 0, func-id: [[FID1]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } +// TRACE-DAG: - { type: 0, func-id: [[FID2]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } diff --git a/test/xray/lit.cfg b/test/xray/lit.cfg index 9142ad1361..b07dcbd791 100644 --- a/test/xray/lit.cfg +++ b/test/xray/lit.cfg @@ -16,6 +16,9 @@ clang_xray_cxxflags = config.cxx_mode_flags + clang_xray_cflags def build_invocation(compile_flags): return ' ' + ' '.join([config.clang] + compile_flags) + ' ' +# Assume that llvm-xray is in the config.llvm_tools_dir. +llvm_xray = os.path.join(config.llvm_tools_dir, 'llvm-xray') + # Setup substitutions. config.substitutions.append( ('%clang ', build_invocation([config.target_cflags]))) @@ -26,6 +29,8 @@ config.substitutions.append( ('%clang_xray ', build_invocation(clang_xray_cflags))) config.substitutions.append( ('%clangxx_xray', build_invocation(clang_xray_cxxflags))) +config.substitutions.append( + ('%llvm_xray', llvm_xray)) # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp']