diff options
author | Robert Nagy <robert@cvs.openbsd.org> | 2024-01-26 11:36:33 +0000 |
---|---|---|
committer | Robert Nagy <robert@cvs.openbsd.org> | 2024-01-26 11:36:33 +0000 |
commit | c9e2866066bb17c8868e9df6b974e6b7716ef0b2 (patch) | |
tree | 264304c95d71d516728cef777006db683a94ce00 /gnu | |
parent | 02f26912a28b816763bdc72874e6b85d315154c8 (diff) |
merge llvm compiler-rt 16.0.6
Diffstat (limited to 'gnu')
60 files changed, 621 insertions, 13091 deletions
diff --git a/gnu/llvm/compiler-rt/cmake/Modules/CustomLibcxx/CMakeLists.txt b/gnu/llvm/compiler-rt/cmake/Modules/CustomLibcxx/CMakeLists.txt deleted file mode 100644 index e61c222587e..00000000000 --- a/gnu/llvm/compiler-rt/cmake/Modules/CustomLibcxx/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.4.3) -project(custom-libcxx C CXX) - -# Build static libcxxabi. -set(LIBCXXABI_STANDALONE_BUILD 1) -set(LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "") -set(LIBCXXABI_ENABLE_EXCEPTIONS ON CACHE BOOL "") -set(LIBCXXABI_HERMETIC_STATIC_LIBRARY ON CACHE STRING "") -set(LIBCXXABI_LIBCXX_PATH ${COMPILER_RT_LIBCXX_PATH} CACHE PATH "") -set(LIBCXXABI_INCLUDE_TESTS OFF CACHE BOOL "") -add_subdirectory(${COMPILER_RT_LIBCXXABI_PATH} ${CMAKE_CURRENT_BINARY_DIR}/cxxabi) - -# Build static libcxx without exceptions. -set(LIBCXX_STANDALONE_BUILD 1) -set(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY OFF CACHE BOOL "") -set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "") -set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "") -set(LIBCXX_HERMETIC_STATIC_LIBRARY ON CACHE BOOL "") - -# Use above libcxxabi. -set(LIBCXX_CXX_ABI "libcxxabi" CACHE STRING "") -set(LIBCXX_CXX_ABI_INTREE 1) -set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "") -set(LIBCXX_CXX_ABI_INCLUDE_PATHS ${COMPILER_RT_LIBCXXABI_PATH}/include CACHE PATH "") - -add_subdirectory(${COMPILER_RT_LIBCXX_PATH} ${CMAKE_CURRENT_BINARY_DIR}/cxx) diff --git a/gnu/llvm/compiler-rt/cmake/Modules/HandleCompilerRT.cmake b/gnu/llvm/compiler-rt/cmake/Modules/HandleCompilerRT.cmake deleted file mode 100644 index 61b7792789e..00000000000 --- a/gnu/llvm/compiler-rt/cmake/Modules/HandleCompilerRT.cmake +++ /dev/null @@ -1,24 +0,0 @@ -function(find_compiler_rt_library name variable) - set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${SANITIZER_COMMON_CFLAGS} - "--rtlib=compiler-rt" "--print-libgcc-file-name") - if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET) - list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}") - endif() - get_property(SANITIZER_CXX_FLAGS CACHE CMAKE_CXX_FLAGS PROPERTY VALUE) - string(REPLACE " " ";" SANITIZER_CXX_FLAGS "${SANITIZER_CXX_FLAGS}") - list(APPEND CLANG_COMMAND ${SANITIZER_CXX_FLAGS}) - execute_process( - COMMAND ${CLANG_COMMAND} - RESULT_VARIABLE HAD_ERROR - OUTPUT_VARIABLE LIBRARY_FILE - ) - string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) - file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE) - string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}") - if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}") - message(STATUS "Found compiler-rt ${name} library: ${LIBRARY_FILE}") - set(${variable} "${LIBRARY_FILE}" PARENT_SCOPE) - else() - message(STATUS "Failed to find compiler-rt ${name} library") - endif() -endfunction() diff --git a/gnu/llvm/compiler-rt/lib/builtins/clear_cache.c b/gnu/llvm/compiler-rt/lib/builtins/clear_cache.c index da0715914b4..8993761eb3d 100644 --- a/gnu/llvm/compiler-rt/lib/builtins/clear_cache.c +++ b/gnu/llvm/compiler-rt/lib/builtins/clear_cache.c @@ -91,12 +91,31 @@ void __clear_cache(void *start, void *end) { #else compilerrt_abort(); #endif -#elif defined(__linux__) && defined(__mips__) +#elif defined(__linux__) && defined(__loongarch__) + __asm__ volatile("ibar 0"); +#elif defined(__mips__) const uintptr_t start_int = (uintptr_t)start; const uintptr_t end_int = (uintptr_t)end; - syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); -#elif defined(__mips__) && defined(__OpenBSD__) - cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE); + uintptr_t synci_step; + __asm__ volatile("rdhwr %0, $1" : "=r"(synci_step)); + if (synci_step != 0) { +#if __mips_isa_rev >= 6 + for (uintptr_t p = start_int; p < end_int; p += synci_step) + __asm__ volatile("synci 0(%0)" : : "r"(p)); + + // The last "move $at, $0" is the target of jr.hb instead of delay slot. + __asm__ volatile(".set noat\n" + "sync\n" + "addiupc $at, 12\n" + "jr.hb $at\n" + "move $at, $0\n" + ".set at"); +#else + // Pre-R6 may not be globalized. And some implementations may give strange + // synci_step. So, let's use libc call for it. + cacheflush(start, end_int - start_int, BCACHE); +#endif + } #elif defined(__aarch64__) && !defined(__APPLE__) uint64_t xstart = (uint64_t)(uintptr_t)start; uint64_t xend = (uint64_t)(uintptr_t)end; @@ -130,7 +149,10 @@ void __clear_cache(void *start, void *end) { __asm __volatile("dsb ish"); } __asm __volatile("isb sy"); -#elif defined(__powerpc64__) +#elif defined(__powerpc__) + // Newer CPUs have a bigger line size made of multiple blocks, so the + // following value is a minimal common denominator for what used to be + // a single block cache line and is therefore inneficient. const size_t line_size = 32; const size_t len = (uintptr_t)end - (uintptr_t)start; @@ -173,12 +195,12 @@ void __clear_cache(void *start, void *end) { arg.len = (uintptr_t)end - (uintptr_t)start; sysarch(RISCV_SYNC_ICACHE, &arg); +#elif defined(__ve__) + __asm__ volatile("fencec 2"); #else #if __APPLE__ // On Darwin, sys_icache_invalidate() provides this functionality sys_icache_invalidate(start, end - start); -#elif defined(__ve__) - __asm__ volatile("fencec 2"); #else compilerrt_abort(); #endif diff --git a/gnu/llvm/compiler-rt/lib/builtins/emutls.c b/gnu/llvm/compiler-rt/lib/builtins/emutls.c index 88af348c9e2..390ffb25f6c 100644 --- a/gnu/llvm/compiler-rt/lib/builtins/emutls.c +++ b/gnu/llvm/compiler-rt/lib/builtins/emutls.c @@ -30,7 +30,7 @@ // MSVC raises a warning about a nonstandard extension being used for the 0 // sized element in this array. Disable this for warn-as-error builds. #pragma warning(push) -#pragma warning(disable : 4206) +#pragma warning(disable : 4200) #endif typedef struct emutls_address_array { @@ -93,7 +93,7 @@ static __inline void emutls_setspecific(emutls_address_array *value) { pthread_setspecific(emutls_pthread_key, (void *)value); } -static __inline emutls_address_array *emutls_getspecific() { +static __inline emutls_address_array *emutls_getspecific(void) { return (emutls_address_array *)pthread_getspecific(emutls_pthread_key); } @@ -125,9 +125,9 @@ static __inline void emutls_init_once(void) { pthread_once(&once, emutls_init); } -static __inline void emutls_lock() { pthread_mutex_lock(&emutls_mutex); } +static __inline void emutls_lock(void) { pthread_mutex_lock(&emutls_mutex); } -static __inline void emutls_unlock() { pthread_mutex_unlock(&emutls_mutex); } +static __inline void emutls_unlock(void) { pthread_mutex_unlock(&emutls_mutex); } #else // _WIN32 @@ -150,7 +150,7 @@ static void win_error(DWORD last_err, const char *hint) { NULL, last_err, 0, (LPSTR)&buffer, 1, NULL)) { fprintf(stderr, "Windows error: %s\n", buffer); } else { - fprintf(stderr, "Unkown Windows error: %s\n", hint); + fprintf(stderr, "Unknown Windows error: %s\n", hint); } LocalFree(buffer); } @@ -209,16 +209,16 @@ static __inline void emutls_init_once(void) { InitOnceExecuteOnce(&once, emutls_init, NULL, NULL); } -static __inline void emutls_lock() { EnterCriticalSection(emutls_mutex); } +static __inline void emutls_lock(void) { EnterCriticalSection(emutls_mutex); } -static __inline void emutls_unlock() { LeaveCriticalSection(emutls_mutex); } +static __inline void emutls_unlock(void) { LeaveCriticalSection(emutls_mutex); } static __inline void emutls_setspecific(emutls_address_array *value) { if (TlsSetValue(emutls_tls_index, (LPVOID)value) == 0) win_abort(GetLastError(), "TlsSetValue"); } -static __inline emutls_address_array *emutls_getspecific() { +static __inline emutls_address_array *emutls_getspecific(void) { LPVOID value = TlsGetValue(emutls_tls_index); if (value == NULL) { const DWORD err = GetLastError(); diff --git a/gnu/llvm/compiler-rt/lib/builtins/mingw_fixfloat.c b/gnu/llvm/compiler-rt/lib/builtins/mingw_fixfloat.c deleted file mode 100644 index 945be9d4344..00000000000 --- a/gnu/llvm/compiler-rt/lib/builtins/mingw_fixfloat.c +++ /dev/null @@ -1,34 +0,0 @@ -//===-- mingw_fixfloat.c - Wrap int/float conversions for arm/windows -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "int_lib.h" - -COMPILER_RT_ABI di_int __fixdfdi(double a); -COMPILER_RT_ABI di_int __fixsfdi(float a); -COMPILER_RT_ABI du_int __fixunsdfdi(double a); -COMPILER_RT_ABI du_int __fixunssfdi(float a); -COMPILER_RT_ABI double __floatdidf(di_int a); -COMPILER_RT_ABI float __floatdisf(di_int a); -COMPILER_RT_ABI double __floatundidf(du_int a); -COMPILER_RT_ABI float __floatundisf(du_int a); - -COMPILER_RT_ABI di_int __dtoi64(double a) { return __fixdfdi(a); } - -COMPILER_RT_ABI di_int __stoi64(float a) { return __fixsfdi(a); } - -COMPILER_RT_ABI du_int __dtou64(double a) { return __fixunsdfdi(a); } - -COMPILER_RT_ABI du_int __stou64(float a) { return __fixunssfdi(a); } - -COMPILER_RT_ABI double __i64tod(di_int a) { return __floatdidf(a); } - -COMPILER_RT_ABI float __i64tos(di_int a) { return __floatdisf(a); } - -COMPILER_RT_ABI double __u64tod(du_int a) { return __floatundidf(a); } - -COMPILER_RT_ABI float __u64tos(du_int a) { return __floatundisf(a); } diff --git a/gnu/llvm/compiler-rt/lib/hwasan/hwasan_setjmp.S b/gnu/llvm/compiler-rt/lib/hwasan/hwasan_setjmp.S deleted file mode 100644 index 0c135433194..00000000000 --- a/gnu/llvm/compiler-rt/lib/hwasan/hwasan_setjmp.S +++ /dev/null @@ -1,100 +0,0 @@ -//===-- hwasan_setjmp.S --------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of HWAddressSanitizer. -// -// HWAddressSanitizer runtime. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_asm.h" - -#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__) -#include "sanitizer_common/sanitizer_platform.h" - -// We want to save the context of the calling function. -// That requires -// 1) No modification of the link register by this function. -// 2) No modification of the stack pointer by this function. -// 3) (no modification of any other saved register, but that's not really going -// to occur, and hence isn't as much of a worry). -// -// There's essentially no way to ensure that the compiler will not modify the -// stack pointer when compiling a C function. -// Hence we have to write this function in assembly. - -.section .text -.file "hwasan_setjmp.S" - -.global __interceptor_setjmp -ASM_TYPE_FUNCTION(__interceptor_setjmp) -__interceptor_setjmp: - CFI_STARTPROC - mov x1, #0 - b __interceptor_sigsetjmp - CFI_ENDPROC -ASM_SIZE(__interceptor_setjmp) - -#if SANITIZER_ANDROID -// Bionic also defines a function `setjmp` that calls `sigsetjmp` saving the -// current signal. -.global __interceptor_setjmp_bionic -ASM_TYPE_FUNCTION(__interceptor_setjmp_bionic) -__interceptor_setjmp_bionic: - CFI_STARTPROC - mov x1, #1 - b __interceptor_sigsetjmp - CFI_ENDPROC -ASM_SIZE(__interceptor_setjmp_bionic) -#endif - -.global __interceptor_sigsetjmp -ASM_TYPE_FUNCTION(__interceptor_sigsetjmp) -__interceptor_sigsetjmp: - CFI_STARTPROC - stp x19, x20, [x0, #0<<3] - stp x21, x22, [x0, #2<<3] - stp x23, x24, [x0, #4<<3] - stp x25, x26, [x0, #6<<3] - stp x27, x28, [x0, #8<<3] - stp x29, x30, [x0, #10<<3] - stp d8, d9, [x0, #14<<3] - stp d10, d11, [x0, #16<<3] - stp d12, d13, [x0, #18<<3] - stp d14, d15, [x0, #20<<3] - mov x2, sp - str x2, [x0, #13<<3] - // We always have the second argument to __sigjmp_save (savemask) set, since - // the _setjmp function above has set it for us as `false`. - // This function is defined in hwasan_interceptors.cc - b __sigjmp_save - CFI_ENDPROC -ASM_SIZE(__interceptor_sigsetjmp) - - -.macro ALIAS first second - .globl \second - .equ \second\(), \first -.endm - -#if SANITIZER_ANDROID -ALIAS __interceptor_sigsetjmp, sigsetjmp -.weak sigsetjmp - -ALIAS __interceptor_setjmp_bionic, setjmp -.weak setjmp -#else -ALIAS __interceptor_sigsetjmp, __sigsetjmp -.weak __sigsetjmp -#endif - -ALIAS __interceptor_setjmp, _setjmp -.weak _setjmp -#endif - -// We do not need executable stack. -NO_EXEC_STACK_DIRECTIVE diff --git a/gnu/llvm/compiler-rt/lib/interception/interception.h b/gnu/llvm/compiler-rt/lib/interception/interception.h index fb91a4cc32b..f2bd94fcc9a 100644 --- a/gnu/llvm/compiler-rt/lib/interception/interception.h +++ b/gnu/llvm/compiler-rt/lib/interception/interception.h @@ -16,7 +16,7 @@ #include "sanitizer_common/sanitizer_internal_defs.h" -#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_MAC && \ +#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_APPLE && \ !SANITIZER_NETBSD && !SANITIZER_WINDOWS && !SANITIZER_FUCHSIA && \ !SANITIZER_OPENBSD && !SANITIZER_SOLARIS # error "Interception doesn't work on this operating system." @@ -88,7 +88,7 @@ typedef __sanitizer::OFF64_T OFF64_T; // As it's decided at compile time which functions are to be intercepted on Mac, // INTERCEPT_FUNCTION() is effectively a no-op on this system. -#if SANITIZER_MAC +#if SANITIZER_APPLE #include <sys/cdefs.h> // For __DARWIN_ALIAS_C(). // Just a pair of pointers. @@ -157,7 +157,7 @@ const interpose_substitution substitution_##func_name[] \ # define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) # define REAL(x) __unsanitized_##x # define DECLARE_REAL(ret_type, func, ...) -#elif !SANITIZER_MAC +#elif !SANITIZER_APPLE # define PTR_TO_REAL(x) real_##x # define REAL(x) __interception::PTR_TO_REAL(x) # define FUNC_TYPE(x) x##_type @@ -168,12 +168,12 @@ const interpose_substitution substitution_##func_name[] \ extern FUNC_TYPE(func) PTR_TO_REAL(func); \ } # define ASSIGN_REAL(dst, src) REAL(dst) = REAL(src) -#else // SANITIZER_MAC +#else // SANITIZER_APPLE # define REAL(x) x # define DECLARE_REAL(ret_type, func, ...) \ extern "C" ret_type func(__VA_ARGS__); # define ASSIGN_REAL(x, y) -#endif // SANITIZER_MAC +#endif // SANITIZER_APPLE #if !SANITIZER_FUCHSIA # define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ @@ -193,7 +193,7 @@ const interpose_substitution substitution_##func_name[] \ // macros does its job. In exceptional cases you may need to call REAL(foo) // without defining INTERCEPTOR(..., foo, ...). For example, if you override // foo with an interceptor for other function. -#if !SANITIZER_MAC && !SANITIZER_FUCHSIA +#if !SANITIZER_APPLE && !SANITIZER_FUCHSIA # define DEFINE_REAL(ret_type, func, ...) \ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ namespace __interception { \ @@ -213,7 +213,7 @@ const interpose_substitution substitution_##func_name[] \ __interceptor_##func(__VA_ARGS__); \ extern "C" INTERCEPTOR_ATTRIBUTE ret_type func(__VA_ARGS__) -#elif !SANITIZER_MAC +#elif !SANITIZER_APPLE #define INTERCEPTOR(ret_type, func, ...) \ DEFINE_REAL(ret_type, func, __VA_ARGS__) \ @@ -226,7 +226,7 @@ const interpose_substitution substitution_##func_name[] \ #define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ INTERCEPTOR(ret_type, func, __VA_ARGS__) -#else // SANITIZER_MAC +#else // SANITIZER_APPLE #define INTERCEPTOR_ZZZ(suffix, ret_type, func, ...) \ extern "C" ret_type func(__VA_ARGS__) suffix; \ @@ -278,7 +278,7 @@ typedef unsigned long uptr; # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) # define INTERCEPT_FUNCTION_VER(func, symver) \ INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) -#elif SANITIZER_MAC +#elif SANITIZER_APPLE # include "interception_mac.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) # define INTERCEPT_FUNCTION_VER(func, symver) \ diff --git a/gnu/llvm/compiler-rt/lib/orc/c_api.h b/gnu/llvm/compiler-rt/lib/orc/c_api.h deleted file mode 100644 index 6677da06ede..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/c_api.h +++ /dev/null @@ -1,208 +0,0 @@ -/*===- c_api.h - C API for the ORC runtime ------------------------*- C -*-===*\ -|* *| -|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| -|* Exceptions. *| -|* See https://llvm.org/LICENSE.txt for license information. *| -|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| -|* *| -|*===----------------------------------------------------------------------===*| -|* *| -|* This file defines the C API for the ORC runtime *| -|* *| -|* NOTE: The OrtRTWrapperFunctionResult type must be kept in sync with the *| -|* definition in llvm/include/llvm-c/OrcShared.h. *| -|* *| -\*===----------------------------------------------------------------------===*/ - -#ifndef ORC_RT_C_API_H -#define ORC_RT_C_API_H - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/* Helper to suppress strict prototype warnings. */ -#ifdef __clang__ -#define ORC_RT_C_STRICT_PROTOTYPES_BEGIN \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic error \"-Wstrict-prototypes\"") -#define ORC_RT_C_STRICT_PROTOTYPES_END _Pragma("clang diagnostic pop") -#else -#define ORC_RT_C_STRICT_PROTOTYPES_BEGIN -#define ORC_RT_C_STRICT_PROTOTYPES_END -#endif - -/* Helper to wrap C code for C++ */ -#ifdef __cplusplus -#define ORC_RT_C_EXTERN_C_BEGIN \ - extern "C" { \ - ORC_RT_C_STRICT_PROTOTYPES_BEGIN -#define ORC_RT_C_EXTERN_C_END \ - ORC_RT_C_STRICT_PROTOTYPES_END \ - } -#else -#define ORC_RT_C_EXTERN_C_BEGIN ORC_RT_C_STRICT_PROTOTYPES_BEGIN -#define ORC_RT_C_EXTERN_C_END ORC_RT_C_STRICT_PROTOTYPES_END -#endif - -ORC_RT_C_EXTERN_C_BEGIN - -typedef union { - char *ValuePtr; - char Value[sizeof(ValuePtr)]; -} __orc_rt_CWrapperFunctionResultDataUnion; - -/** - * __orc_rt_CWrapperFunctionResult is a kind of C-SmallVector with an - * out-of-band error state. - * - * If Size == 0 and Data.ValuePtr is non-zero then the value is in the - * 'out-of-band error' state, and Data.ValuePtr points at a malloc-allocated, - * null-terminated string error message. - * - * If Size <= sizeof(__orc_rt_CWrapperFunctionResultData) then the value is in - * the 'small' state and the content is held in the first Size bytes of - * Data.Value. - * - * If Size > sizeof(OrtRTCWrapperFunctionResultData) then the value is in the - * 'large' state and the content is held in the first Size bytes of the - * memory pointed to by Data.ValuePtr. This memory must have been allocated by - * malloc, and will be freed with free when this value is destroyed. - */ -typedef struct { - __orc_rt_CWrapperFunctionResultDataUnion Data; - size_t Size; -} __orc_rt_CWrapperFunctionResult; - -typedef struct __orc_rt_CSharedOpaqueJITProcessControl - *__orc_rt_SharedJITProcessControlRef; - -/** - * Zero-initialize an __orc_rt_CWrapperFunctionResult. - */ -static inline void -__orc_rt_CWrapperFunctionResultInit(__orc_rt_CWrapperFunctionResult *R) { - R->Size = 0; - R->Data.ValuePtr = 0; -} - -/** - * Create an __orc_rt_CWrapperFunctionResult with an uninitialized buffer of - * size Size. The buffer is returned via the DataPtr argument. - */ -static inline char * -__orc_rt_CWrapperFunctionResultAllocate(__orc_rt_CWrapperFunctionResult *R, - size_t Size) { - R->Size = Size; - if (Size <= sizeof(R->Data.Value)) - return R->Data.Value; - - R->Data.ValuePtr = (char *)malloc(Size); - return R->Data.ValuePtr; -} - -/** - * Create an __orc_rt_WrapperFunctionResult from the given data range. - */ -static inline __orc_rt_CWrapperFunctionResult -__orc_rt_CreateCWrapperFunctionResultFromRange(const char *Data, size_t Size) { - __orc_rt_CWrapperFunctionResult R; - R.Size = Size; - if (R.Size > sizeof(R.Data.Value)) { - char *Tmp = (char *)malloc(Size); - memcpy(Tmp, Data, Size); - R.Data.ValuePtr = Tmp; - } else - memcpy(R.Data.Value, Data, Size); - return R; -} - -/** - * Create an __orc_rt_CWrapperFunctionResult by copying the given string, - * including the null-terminator. - * - * This function copies the input string. The client is responsible for freeing - * the ErrMsg arg. - */ -static inline __orc_rt_CWrapperFunctionResult -__orc_rt_CreateCWrapperFunctionResultFromString(const char *Source) { - return __orc_rt_CreateCWrapperFunctionResultFromRange(Source, - strlen(Source) + 1); -} - -/** - * Create an __orc_rt_CWrapperFunctionResult representing an out-of-band - * error. - * - * This function takes ownership of the string argument which must have been - * allocated with malloc. - */ -static inline __orc_rt_CWrapperFunctionResult -__orc_rt_CreateCWrapperFunctionResultFromOutOfBandError(const char *ErrMsg) { - __orc_rt_CWrapperFunctionResult R; - R.Size = 0; - char *Tmp = (char *)malloc(strlen(ErrMsg) + 1); - strcpy(Tmp, ErrMsg); - R.Data.ValuePtr = Tmp; - return R; -} - -/** - * This should be called to destroy __orc_rt_CWrapperFunctionResult values - * regardless of their state. - */ -static inline void -__orc_rt_DisposeCWrapperFunctionResult(__orc_rt_CWrapperFunctionResult *R) { - if (R->Size > sizeof(R->Data.Value) || - (R->Size == 0 && R->Data.ValuePtr)) - free(R->Data.ValuePtr); -} - -/** - * Get a pointer to the data contained in the given - * __orc_rt_CWrapperFunctionResult. - */ -static inline const char * -__orc_rt_CWrapperFunctionResultData(const __orc_rt_CWrapperFunctionResult *R) { - assert((R->Size != 0 || R->Data.ValuePtr == nullptr) && - "Cannot get data for out-of-band error value"); - return R->Size > sizeof(R->Data.Value) ? R->Data.ValuePtr : R->Data.Value; -} - -/** - * Safely get the size of the given __orc_rt_CWrapperFunctionResult. - * - * Asserts that we're not trying to access the size of an error value. - */ -static inline size_t -__orc_rt_CWrapperFunctionResultSize(const __orc_rt_CWrapperFunctionResult *R) { - assert((R->Size != 0 || R->Data.ValuePtr == nullptr) && - "Cannot get size for out-of-band error value"); - return R->Size; -} - -/** - * Returns 1 if this value is equivalent to a value just initialized by - * __orc_rt_CWrapperFunctionResultInit, 0 otherwise. - */ -static inline size_t -__orc_rt_CWrapperFunctionResultEmpty(const __orc_rt_CWrapperFunctionResult *R) { - return R->Size == 0 && R->Data.ValuePtr == 0; -} - -/** - * Returns a pointer to the out-of-band error string for this - * __orc_rt_CWrapperFunctionResult, or null if there is no error. - * - * The __orc_rt_CWrapperFunctionResult retains ownership of the error - * string, so it should be copied if the caller wishes to preserve it. - */ -static inline const char *__orc_rt_CWrapperFunctionResultGetOutOfBandError( - const __orc_rt_CWrapperFunctionResult *R) { - return R->Size == 0 ? R->Data.ValuePtr : 0; -} - -ORC_RT_C_EXTERN_C_END - -#endif /* ORC_RT_C_API_H */ diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/CMakeLists.txt b/gnu/llvm/compiler-rt/lib/orc/unittests/CMakeLists.txt deleted file mode 100644 index fbbf7faec1e..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/CMakeLists.txt +++ /dev/null @@ -1,108 +0,0 @@ -include(CompilerRTCompile) - -include_directories(..) - -add_custom_target(OrcRTUnitTests) -set_target_properties(OrcRTUnitTests PROPERTIES FOLDER "OrcRT unittests") - -set(ORC_UNITTEST_CFLAGS - ${ORC_CFLAGS} - ${COMPILER_RT_UNITTEST_CFLAGS} - ${COMPILER_RT_GTEST_CFLAGS} - -I${COMPILER_RT_SOURCE_DIR}/lib/orc - ) - -# We add the include directories one at a time in our CFLAGS. -foreach (DIR ${LLVM_INCLUDE_DIR} ${LLVM_MAIN_INCLUDE_DIR}) - list(APPEND ORC_UNITTEST_CFLAGS -I${DIR}) -endforeach() - -function(add_orc_lib library) - add_library(${library} STATIC ${ARGN}) - set_target_properties(${library} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - FOLDER "Compiler-RT Runtime tests") -endfunction() - -function(get_orc_lib_for_arch arch lib) - if(APPLE) - set(tgt_name "RTOrc.test.osx") - else() - set(tgt_name "RTOrc.test.${arch}") - endif() - set(${lib} "${tgt_name}" PARENT_SCOPE) -endfunction() - -set(ORC_TEST_ARCH ${ORC_SUPPORTED_ARCH}) -set(ORC_UNITTEST_LINK_FLAGS - ${COMPILER_RT_UNITTEST_LINK_FLAGS} - ${CMAKE_THREAD_LIBS_INIT} - ) - -if(APPLE) - darwin_filter_host_archs(ORC_SUPPORTED_ARCH ORC_TEST_ARCH) - list(APPEND ORC_UNITTEST_CFLAGS ${DARWIN_osx_CFLAGS}) - list(APPEND ORC_UNITTEST_LINK_FLAGS ${DARWIN_osx_LINK_FLAGS}) -else() - append_list_if(COMPILER_RT_HAS_LIBM -lm ORC_UNITTEST_LINK_FLAGS) - append_list_if(COMPILER_RT_HAS_LIBRT -lrt ORC_UNITTEST_LINK_FLAGS) - append_list_if(COMPILER_RT_HAS_LIBDL -ldl ORC_UNITTEST_LINK_FLAGS) - append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ORC_UNITTEST_LINK_FLAGS) - append_list_if(COMPILER_RT_HAS_LIBEXECINFO -lexecinfo ORC_UNITTEST_LINK_FLAGS) -endif() - -foreach(lib ${SANITIZER_TEST_CXX_LIBRARIES}) - list(APPEND ORC_UNITTEST_LINK_FLAGS -l${lib}) -endforeach() - -set(ORC_DEPS gtest orc) -# ORC uses C++ standard library headers. -if (TARGET cxx-headers OR HAVE_LIBCXX) - set(ORC_DEPS cxx-headers) -endif() - -macro(add_orc_unittest testname) - cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN}) - if(UNIX) - foreach(arch ${ORC_TEST_ARCH}) - set(TEST_OBJECTS) - get_orc_lib_for_arch(${arch} ORC_RUNTIME_LIBS) - generate_compiler_rt_tests(TEST_OBJECTS - OrcRTUnitTests "${testname}-${arch}-Test" "${arch}" - SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE} - RUNTIME "${ORC_RUNTIME_LIBS}" - COMPILE_DEPS ${TEST_HEADERS} - DEPS ${ORC_DEPS} - CFLAGS ${ORC_UNITTEST_CFLAGS} - LINK_FLAGS ${ORC_UNITTEST_LINK_FLAGS}) - endforeach() - endif() -endmacro() - -set(UNITTEST_SOURCES - adt_test.cpp - c_api_test.cpp - endian_test.cpp - error_test.cpp - extensible_rtti_test.cpp - orc_unit_test_main.cpp - stl_extras_test.cpp - wrapper_function_utils_test.cpp - simple_packed_serialization_test.cpp - ) - -if (COMPILER_RT_CAN_EXECUTE_TESTS) - - if (APPLE) - add_orc_lib("RTOrc.test.osx" - $<TARGET_OBJECTS:RTOrc.osx>) - else() - foreach(arch ${ORC_SUPPORTED_ARCH}) - add_orc_lib("RTOrc.test.${arch}" - $<TARGET_OBJECTS:RTOrc.${arch}>) - endforeach() - endif() - - add_orc_unittest(OrcUnitTest SOURCES ${UNITTEST_SOURCES}) - -endif() diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/adt_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/adt_test.cpp deleted file mode 100644 index dec636e837d..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/adt_test.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//===-- adt_test.cpp ------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -//===----------------------------------------------------------------------===// - -#include "adt.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -TEST(ADTTest, SpanDefaultConstruction) { - span<int> S; - EXPECT_TRUE(S.empty()) << "Default constructed span not empty"; - EXPECT_EQ(S.size(), 0U) << "Default constructed span size not zero"; - EXPECT_EQ(S.begin(), S.end()) << "Default constructed span begin != end"; -} - -TEST(ADTTest, SpanConstructFromFixedArray) { - int A[] = {1, 2, 3, 4, 5}; - span<int> S(A); - EXPECT_FALSE(S.empty()) << "Span should be non-empty"; - EXPECT_EQ(S.size(), 5U) << "Span has unexpected size"; - EXPECT_EQ(std::distance(S.begin(), S.end()), 5U) - << "Unexpected iterator range size"; - EXPECT_EQ(S.data(), &A[0]) << "Span data has unexpected value"; - for (unsigned I = 0; I != S.size(); ++I) - EXPECT_EQ(S[I], A[I]) << "Unexpected span element value"; -} - -TEST(ADTTest, SpanConstructFromIteratorAndSize) { - int A[] = {1, 2, 3, 4, 5}; - span<int> S(&A[0], 5); - EXPECT_FALSE(S.empty()) << "Span should be non-empty"; - EXPECT_EQ(S.size(), 5U) << "Span has unexpected size"; - EXPECT_EQ(std::distance(S.begin(), S.end()), 5U) - << "Unexpected iterator range size"; - EXPECT_EQ(S.data(), &A[0]) << "Span data has unexpected value"; - for (unsigned I = 0; I != S.size(); ++I) - EXPECT_EQ(S[I], A[I]) << "Unexpected span element value"; -} - -TEST(ADTTest, StringViewDefaultConstruction) { - string_view S; - EXPECT_TRUE(S.empty()) << "Default constructed span not empty"; - EXPECT_EQ(S.size(), 0U) << "Default constructed span size not zero"; - EXPECT_EQ(S.begin(), S.end()) << "Default constructed span begin != end"; -} - -TEST(ADTTest, StringViewConstructFromCharPtrAndSize) { - const char *Str = "abcdefg"; - string_view S(Str, 5); - EXPECT_FALSE(S.empty()) << "Span should be non-empty"; - EXPECT_EQ(S.size(), 5U) << "Span has unexpected size"; - EXPECT_EQ(std::distance(S.begin(), S.end()), 5U) - << "Unexpected iterator range size"; - EXPECT_EQ(S.data(), &Str[0]) << "Span data has unexpected value"; - for (unsigned I = 0; I != S.size(); ++I) - EXPECT_EQ(S[I], Str[I]) << "Unexpected span element value"; -} - -TEST(ADTTest, StringViewConstructFromCharPtr) { - const char *Str = "abcdefg"; - size_t StrLen = strlen(Str); - string_view S(Str); - - EXPECT_FALSE(S.empty()) << "Span should be non-empty"; - EXPECT_EQ(S.size(), StrLen) << "Span has unexpected size"; - EXPECT_EQ(static_cast<size_t>(std::distance(S.begin(), S.end())), StrLen) - << "Unexpected iterator range size"; - EXPECT_EQ(S.data(), &Str[0]) << "Span data has unexpected value"; - for (unsigned I = 0; I != S.size(); ++I) - EXPECT_EQ(S[I], Str[I]) << "Unexpected span element value"; -} - -TEST(ADTTest, StringViewEquality) { - EXPECT_EQ("", string_view()); - EXPECT_FALSE(string_view("aab") == string_view("aac")); - EXPECT_FALSE(string_view("aab") != string_view("aab")); - EXPECT_NE(string_view("aab"), string_view("aac")); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/c_api_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/c_api_test.cpp deleted file mode 100644 index 4583feb98a1..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/c_api_test.cpp +++ /dev/null @@ -1,200 +0,0 @@ -//===-- c_api_test.cpp ----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -//===----------------------------------------------------------------------===// - -#include "c_api.h" -#include "gtest/gtest.h" - -TEST(CAPITest, CWrapperFunctionResultInit) { - __orc_rt_CWrapperFunctionResult R; - __orc_rt_CWrapperFunctionResultInit(&R); - - EXPECT_EQ(R.Size, 0U); - EXPECT_EQ(R.Data.ValuePtr, nullptr); - - // Check that this value isn't treated as an out-of-band error. - EXPECT_EQ(__orc_rt_CWrapperFunctionResultGetOutOfBandError(&R), nullptr); - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultAllocSmall) { - constexpr size_t SmallAllocSize = sizeof(const char *); - - __orc_rt_CWrapperFunctionResult R; - char *DataPtr = __orc_rt_CWrapperFunctionResultAllocate(&R, SmallAllocSize); - - for (size_t I = 0; I != SmallAllocSize; ++I) - DataPtr[I] = 0x55 + I; - - // Check that the inline storage in R.Data.Value contains the expected - // sequence. - EXPECT_EQ(R.Size, SmallAllocSize); - for (size_t I = 0; I != SmallAllocSize; ++I) - EXPECT_EQ(R.Data.Value[I], (char)(0x55 + I)) - << "Unexpected value at index " << I; - - // Check that this value isn't treated as an out-of-band error. - EXPECT_EQ(__orc_rt_CWrapperFunctionResultGetOutOfBandError(&R), nullptr); - - // Check that __orc_rt_CWrapperFunctionResult(Data|Result|Size) and - // __orc_rt_CWrapperFunctionResultGetOutOfBandError behave as expected. - EXPECT_EQ(__orc_rt_CWrapperFunctionResultData(&R), R.Data.Value); - EXPECT_EQ(__orc_rt_CWrapperFunctionResultSize(&R), SmallAllocSize); - EXPECT_FALSE(__orc_rt_CWrapperFunctionResultEmpty(&R)); - EXPECT_EQ(__orc_rt_CWrapperFunctionResultGetOutOfBandError(&R), nullptr); - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultAllocLarge) { - constexpr size_t LargeAllocSize = sizeof(const char *) + 1; - - __orc_rt_CWrapperFunctionResult R; - char *DataPtr = __orc_rt_CWrapperFunctionResultAllocate(&R, LargeAllocSize); - - for (size_t I = 0; I != LargeAllocSize; ++I) - DataPtr[I] = 0x55 + I; - - // Check that the inline storage in R.Data.Value contains the expected - // sequence. - EXPECT_EQ(R.Size, LargeAllocSize); - EXPECT_EQ(R.Data.ValuePtr, DataPtr); - for (size_t I = 0; I != LargeAllocSize; ++I) - EXPECT_EQ(R.Data.ValuePtr[I], (char)(0x55 + I)) - << "Unexpected value at index " << I; - - // Check that this value isn't treated as an out-of-band error. - EXPECT_EQ(__orc_rt_CWrapperFunctionResultGetOutOfBandError(&R), nullptr); - - // Check that __orc_rt_CWrapperFunctionResult(Data|Result|Size) and - // __orc_rt_CWrapperFunctionResultGetOutOfBandError behave as expected. - EXPECT_EQ(__orc_rt_CWrapperFunctionResultData(&R), R.Data.ValuePtr); - EXPECT_EQ(__orc_rt_CWrapperFunctionResultSize(&R), LargeAllocSize); - EXPECT_FALSE(__orc_rt_CWrapperFunctionResultEmpty(&R)); - EXPECT_EQ(__orc_rt_CWrapperFunctionResultGetOutOfBandError(&R), nullptr); - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultFromRangeSmall) { - constexpr size_t SmallAllocSize = sizeof(const char *); - - char Source[SmallAllocSize]; - for (size_t I = 0; I != SmallAllocSize; ++I) - Source[I] = 0x55 + I; - - __orc_rt_CWrapperFunctionResult R = - __orc_rt_CreateCWrapperFunctionResultFromRange(Source, SmallAllocSize); - - // Check that the inline storage in R.Data.Value contains the expected - // sequence. - EXPECT_EQ(R.Size, SmallAllocSize); - for (size_t I = 0; I != SmallAllocSize; ++I) - EXPECT_EQ(R.Data.Value[I], (char)(0x55 + I)) - << "Unexpected value at index " << I; - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultFromRangeLarge) { - constexpr size_t LargeAllocSize = sizeof(const char *) + 1; - - char Source[LargeAllocSize]; - for (size_t I = 0; I != LargeAllocSize; ++I) - Source[I] = 0x55 + I; - - __orc_rt_CWrapperFunctionResult R = - __orc_rt_CreateCWrapperFunctionResultFromRange(Source, LargeAllocSize); - - // Check that the inline storage in R.Data.Value contains the expected - // sequence. - EXPECT_EQ(R.Size, LargeAllocSize); - for (size_t I = 0; I != LargeAllocSize; ++I) - EXPECT_EQ(R.Data.ValuePtr[I], (char)(0x55 + I)) - << "Unexpected value at index " << I; - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultFromStringSmall) { - constexpr size_t SmallAllocSize = sizeof(const char *); - - char Source[SmallAllocSize]; - for (size_t I = 0; I != SmallAllocSize - 1; ++I) - Source[I] = 'a' + I; - Source[SmallAllocSize - 1] = '\0'; - - __orc_rt_CWrapperFunctionResult R = - __orc_rt_CreateCWrapperFunctionResultFromString(Source); - - // Check that the inline storage in R.Data.Value contains the expected - // sequence. - EXPECT_EQ(R.Size, SmallAllocSize); - for (size_t I = 0; I != SmallAllocSize - 1; ++I) - EXPECT_EQ(R.Data.Value[I], (char)('a' + I)) - << "Unexpected value at index " << I; - EXPECT_EQ(R.Data.Value[SmallAllocSize - 1], '\0') - << "Unexpected value at index " << (SmallAllocSize - 1); - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultFromStringLarge) { - constexpr size_t LargeAllocSize = sizeof(const char *) + 1; - - char Source[LargeAllocSize]; - for (size_t I = 0; I != LargeAllocSize - 1; ++I) - Source[I] = 'a' + I; - Source[LargeAllocSize - 1] = '\0'; - - __orc_rt_CWrapperFunctionResult R = - __orc_rt_CreateCWrapperFunctionResultFromString(Source); - - // Check that the inline storage in R.Data.Value contains the expected - // sequence. - EXPECT_EQ(R.Size, LargeAllocSize); - for (size_t I = 0; I != LargeAllocSize - 1; ++I) - EXPECT_EQ(R.Data.ValuePtr[I], (char)('a' + I)) - << "Unexpected value at index " << I; - EXPECT_EQ(R.Data.ValuePtr[LargeAllocSize - 1], '\0') - << "Unexpected value at index " << (LargeAllocSize - 1); - - // Check that we can dispose of the value. - __orc_rt_DisposeCWrapperFunctionResult(&R); -} - -TEST(CAPITest, CWrapperFunctionResultFromOutOfBandError) { - constexpr const char *ErrMsg = "test error message"; - __orc_rt_CWrapperFunctionResult R = - __orc_rt_CreateCWrapperFunctionResultFromOutOfBandError(ErrMsg); - -#ifndef NDEBUG - EXPECT_DEATH({ __orc_rt_CWrapperFunctionResultData(&R); }, - "Cannot get data for out-of-band error value"); - EXPECT_DEATH({ __orc_rt_CWrapperFunctionResultSize(&R); }, - "Cannot get size for out-of-band error value"); -#endif - - EXPECT_FALSE(__orc_rt_CWrapperFunctionResultEmpty(&R)); - const char *OOBErrMsg = __orc_rt_CWrapperFunctionResultGetOutOfBandError(&R); - EXPECT_NE(OOBErrMsg, nullptr); - EXPECT_NE(OOBErrMsg, ErrMsg); - EXPECT_TRUE(strcmp(OOBErrMsg, ErrMsg) == 0); - - __orc_rt_DisposeCWrapperFunctionResult(&R); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/endian_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/endian_test.cpp deleted file mode 100644 index 71b677af694..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/endian_test.cpp +++ /dev/null @@ -1,174 +0,0 @@ -//===- endian_test.cpp ------------------------- swap byte order test -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -// Adapted from the llvm/unittests/Support/SwapByteOrderTest.cpp LLVM unit test. -// -//===----------------------------------------------------------------------===// - -#include "endianness.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -TEST(Endian, ByteSwap_32) { - EXPECT_EQ(0x44332211u, ByteSwap_32(0x11223344)); - EXPECT_EQ(0xDDCCBBAAu, ByteSwap_32(0xAABBCCDD)); -} - -TEST(Endian, ByteSwap_64) { - EXPECT_EQ(0x8877665544332211ULL, ByteSwap_64(0x1122334455667788LL)); - EXPECT_EQ(0x1100FFEEDDCCBBAAULL, ByteSwap_64(0xAABBCCDDEEFF0011LL)); -} - -// In these first two tests all of the original_uintx values are truncated -// except for 64. We could avoid this, but there's really no point. -TEST(Endian, getSwappedBytes_UnsignedRoundTrip) { - // The point of the bit twiddling of magic is to test with and without bits - // in every byte. - uint64_t value = 1; - for (std::size_t i = 0; i <= sizeof(value); ++i) { - uint8_t original_uint8 = static_cast<uint8_t>(value); - EXPECT_EQ(original_uint8, getSwappedBytes(getSwappedBytes(original_uint8))); - - uint16_t original_uint16 = static_cast<uint16_t>(value); - EXPECT_EQ(original_uint16, - getSwappedBytes(getSwappedBytes(original_uint16))); - - uint32_t original_uint32 = static_cast<uint32_t>(value); - EXPECT_EQ(original_uint32, - getSwappedBytes(getSwappedBytes(original_uint32))); - - uint64_t original_uint64 = static_cast<uint64_t>(value); - EXPECT_EQ(original_uint64, - getSwappedBytes(getSwappedBytes(original_uint64))); - - value = (value << 8) | 0x55; // binary 0101 0101. - } -} - -TEST(Endian, getSwappedBytes_SignedRoundTrip) { - // The point of the bit twiddling of magic is to test with and without bits - // in every byte. - uint64_t value = 1; - for (std::size_t i = 0; i <= sizeof(value); ++i) { - int8_t original_int8 = static_cast<int8_t>(value); - EXPECT_EQ(original_int8, getSwappedBytes(getSwappedBytes(original_int8))); - - int16_t original_int16 = static_cast<int16_t>(value); - EXPECT_EQ(original_int16, getSwappedBytes(getSwappedBytes(original_int16))); - - int32_t original_int32 = static_cast<int32_t>(value); - EXPECT_EQ(original_int32, getSwappedBytes(getSwappedBytes(original_int32))); - - int64_t original_int64 = static_cast<int64_t>(value); - EXPECT_EQ(original_int64, getSwappedBytes(getSwappedBytes(original_int64))); - - // Test other sign. - value *= -1; - - original_int8 = static_cast<int8_t>(value); - EXPECT_EQ(original_int8, getSwappedBytes(getSwappedBytes(original_int8))); - - original_int16 = static_cast<int16_t>(value); - EXPECT_EQ(original_int16, getSwappedBytes(getSwappedBytes(original_int16))); - - original_int32 = static_cast<int32_t>(value); - EXPECT_EQ(original_int32, getSwappedBytes(getSwappedBytes(original_int32))); - - original_int64 = static_cast<int64_t>(value); - EXPECT_EQ(original_int64, getSwappedBytes(getSwappedBytes(original_int64))); - - // Return to normal sign and twiddle. - value *= -1; - value = (value << 8) | 0x55; // binary 0101 0101. - } -} - -TEST(Endian, getSwappedBytes_uint8_t) { - EXPECT_EQ(uint8_t(0x11), getSwappedBytes(uint8_t(0x11))); -} - -TEST(Endian, getSwappedBytes_uint16_t) { - EXPECT_EQ(uint16_t(0x1122), getSwappedBytes(uint16_t(0x2211))); -} - -TEST(Endian, getSwappedBytes_uint32_t) { - EXPECT_EQ(uint32_t(0x11223344), getSwappedBytes(uint32_t(0x44332211))); -} - -TEST(Endian, getSwappedBytes_uint64_t) { - EXPECT_EQ(uint64_t(0x1122334455667788ULL), - getSwappedBytes(uint64_t(0x8877665544332211ULL))); -} - -TEST(Endian, getSwappedBytes_int8_t) { - EXPECT_EQ(int8_t(0x11), getSwappedBytes(int8_t(0x11))); -} - -TEST(Endian, getSwappedBytes_int16_t) { - EXPECT_EQ(int16_t(0x1122), getSwappedBytes(int16_t(0x2211))); -} - -TEST(Endian, getSwappedBytes_int32_t) { - EXPECT_EQ(int32_t(0x11223344), getSwappedBytes(int32_t(0x44332211))); -} - -TEST(Endian, getSwappedBytes_int64_t) { - EXPECT_EQ(int64_t(0x1122334455667788LL), - getSwappedBytes(int64_t(0x8877665544332211LL))); -} - -TEST(Endian, swapByteOrder_uint8_t) { - uint8_t value = 0x11; - swapByteOrder(value); - EXPECT_EQ(uint8_t(0x11), value); -} - -TEST(Endian, swapByteOrder_uint16_t) { - uint16_t value = 0x2211; - swapByteOrder(value); - EXPECT_EQ(uint16_t(0x1122), value); -} - -TEST(Endian, swapByteOrder_uint32_t) { - uint32_t value = 0x44332211; - swapByteOrder(value); - EXPECT_EQ(uint32_t(0x11223344), value); -} - -TEST(Endian, swapByteOrder_uint64_t) { - uint64_t value = 0x8877665544332211ULL; - swapByteOrder(value); - EXPECT_EQ(uint64_t(0x1122334455667788ULL), value); -} - -TEST(Endian, swapByteOrder_int8_t) { - int8_t value = 0x11; - swapByteOrder(value); - EXPECT_EQ(int8_t(0x11), value); -} - -TEST(Endian, swapByteOrder_int16_t) { - int16_t value = 0x2211; - swapByteOrder(value); - EXPECT_EQ(int16_t(0x1122), value); -} - -TEST(Endian, swapByteOrder_int32_t) { - int32_t value = 0x44332211; - swapByteOrder(value); - EXPECT_EQ(int32_t(0x11223344), value); -} - -TEST(Endian, swapByteOrder_int64_t) { - int64_t value = 0x8877665544332211LL; - swapByteOrder(value); - EXPECT_EQ(int64_t(0x1122334455667788LL), value); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/error_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/error_test.cpp deleted file mode 100644 index 5251d788e01..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/error_test.cpp +++ /dev/null @@ -1,295 +0,0 @@ -//===-- error_test.cpp --sssssssss-----------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -// Note: -// This unit test was adapted from -// llvm/unittests/Support/ExtensibleRTTITest.cpp -// -//===----------------------------------------------------------------------===// - -#include "error.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -namespace { - -class CustomError : public RTTIExtends<CustomError, ErrorInfoBase> { -public: - CustomError(int V1) : V1(V1) {} - std::string toString() const override { - return "CustomError V1 = " + std::to_string(V1); - } - int getV1() const { return V1; } - -protected: - int V1; -}; - -class CustomSubError : public RTTIExtends<CustomSubError, CustomError> { -public: - CustomSubError(int V1, std::string V2) - : RTTIExtends<CustomSubError, CustomError>(V1), V2(std::move(V2)) {} - std::string toString() const override { - return "CustomSubError V1 = " + std::to_string(V1) + ", " + V2; - } - const std::string &getV2() const { return V2; } - -protected: - std::string V2; -}; - -} // end anonymous namespace - -// Test that a checked success value doesn't cause any issues. -TEST(Error, CheckedSuccess) { - Error E = Error::success(); - EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'"; -} - -// Check that a consumed success value doesn't cause any issues. -TEST(Error, ConsumeSuccess) { consumeError(Error::success()); } - -TEST(Error, ConsumeError) { - Error E = make_error<CustomError>(42); - if (E) { - consumeError(std::move(E)); - } else - ADD_FAILURE() << "Error failure value should convert to true"; -} - -// Test that unchecked success values cause an abort. -TEST(Error, UncheckedSuccess) { - EXPECT_DEATH({ Error E = Error::success(); }, - "Error must be checked prior to destruction") - << "Unchecked Error Succes value did not cause abort()"; -} - -// Test that a checked but unhandled error causes an abort. -TEST(Error, CheckedButUnhandledError) { - auto DropUnhandledError = []() { - Error E = make_error<CustomError>(42); - (void)!E; - }; - EXPECT_DEATH(DropUnhandledError(), - "Error must be checked prior to destruction") - << "Unhandled Error failure value did not cause an abort()"; -} - -// Test that error_cast works as expected. -TEST(Error, BasicErrorCast) { - { - // Check casting base error value to base error type. - auto E = make_error<CustomError>(42); - if (auto CSE = error_cast<CustomSubError>(E)) { - ADD_FAILURE() << "Derived cast incorrectly matched base error"; - } else if (auto CE = error_cast<CustomError>(E)) { - EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value"; - } else - ADD_FAILURE() << "Unexpected error value"; - } - - { - // Check casting derived error value to base error type. - auto E = make_error<CustomSubError>(42, "foo"); - if (auto CE = error_cast<CustomError>(E)) { - EXPECT_EQ(CE->getV1(), 42) << "Unexpected wrapped value"; - } else - ADD_FAILURE() << "Unexpected error value"; - } - - { - // Check casting derived error value to derived error type. - auto E = make_error<CustomSubError>(42, "foo"); - if (auto CSE = error_cast<CustomSubError>(E)) { - EXPECT_EQ(CSE->getV1(), 42) << "Unexpected wrapped value"; - EXPECT_EQ(CSE->getV2(), "foo") << "Unexpected wrapped value"; - } else - ADD_FAILURE() << "Unexpected error value"; - } -} - -// ErrorAsOutParameter tester. -static void errAsOutParamHelper(Error &Err) { - ErrorAsOutParameter ErrAsOutParam(&Err); - // Verify that checked flag is raised - assignment should not crash. - Err = Error::success(); - // Raise the checked bit manually - caller should still have to test the - // error. - (void)!!Err; -} - -// Test that ErrorAsOutParameter sets the checked flag on construction. -TEST(Error, ErrorAsOutParameterChecked) { - Error E = Error::success(); - errAsOutParamHelper(E); - (void)!!E; -} - -// Test that ErrorAsOutParameter clears the checked flag on destruction. -TEST(Error, ErrorAsOutParameterUnchecked) { - EXPECT_DEATH( - { - Error E = Error::success(); - errAsOutParamHelper(E); - }, - "Error must be checked prior to destruction") - << "ErrorAsOutParameter did not clear the checked flag on destruction."; -} - -// Check 'Error::isA<T>' method handling. -TEST(Error, IsAHandling) { - // Check 'isA' handling. - Error E = make_error<CustomError>(42); - Error F = make_error<CustomSubError>(42, "foo"); - Error G = Error::success(); - - EXPECT_TRUE(E.isA<CustomError>()); - EXPECT_FALSE(E.isA<CustomSubError>()); - EXPECT_TRUE(F.isA<CustomError>()); - EXPECT_TRUE(F.isA<CustomSubError>()); - EXPECT_FALSE(G.isA<CustomError>()); - - consumeError(std::move(E)); - consumeError(std::move(F)); - consumeError(std::move(G)); -} - -TEST(Error, StringError) { - auto E = make_error<StringError>("foo"); - if (auto SE = error_cast<StringError>(E)) { - EXPECT_EQ(SE->toString(), "foo") << "Unexpected StringError value"; - } else - ADD_FAILURE() << "Expected StringError value"; -} - -// Test Checked Expected<T> in success mode. -TEST(Error, CheckedExpectedInSuccessMode) { - Expected<int> A = 7; - EXPECT_TRUE(!!A) << "Expected with non-error value doesn't convert to 'true'"; - // Access is safe in second test, since we checked the error in the first. - EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value"; -} - -// Test Expected with reference type. -TEST(Error, ExpectedWithReferenceType) { - int A = 7; - Expected<int &> B = A; - // 'Check' B. - (void)!!B; - int &C = *B; - EXPECT_EQ(&A, &C) << "Expected failed to propagate reference"; -} - -// Test Unchecked Expected<T> in success mode. -// We expect this to blow up the same way Error would. -// Test runs in debug mode only. -TEST(Error, UncheckedExpectedInSuccessModeDestruction) { - EXPECT_DEATH({ Expected<int> A = 7; }, - "Expected<T> must be checked before access or destruction.") - << "Unchecekd Expected<T> success value did not cause an abort()."; -} - -// Test Unchecked Expected<T> in success mode. -// We expect this to blow up the same way Error would. -// Test runs in debug mode only. -TEST(Error, UncheckedExpectedInSuccessModeAccess) { - EXPECT_DEATH( - { - Expected<int> A = 7; - *A; - }, - "Expected<T> must be checked before access or destruction.") - << "Unchecekd Expected<T> success value did not cause an abort()."; -} - -// Test Unchecked Expected<T> in success mode. -// We expect this to blow up the same way Error would. -// Test runs in debug mode only. -TEST(Error, UncheckedExpectedInSuccessModeAssignment) { - EXPECT_DEATH( - { - Expected<int> A = 7; - A = 7; - }, - "Expected<T> must be checked before access or destruction.") - << "Unchecekd Expected<T> success value did not cause an abort()."; -} - -// Test Expected<T> in failure mode. -TEST(Error, ExpectedInFailureMode) { - Expected<int> A = make_error<CustomError>(42); - EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'"; - Error E = A.takeError(); - EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value"; - consumeError(std::move(E)); -} - -// Check that an Expected instance with an error value doesn't allow access to -// operator*. -// Test runs in debug mode only. -TEST(Error, AccessExpectedInFailureMode) { - Expected<int> A = make_error<CustomError>(42); - EXPECT_DEATH(*A, "Expected<T> must be checked before access or destruction.") - << "Incorrect Expected error value"; - consumeError(A.takeError()); -} - -// Check that an Expected instance with an error triggers an abort if -// unhandled. -// Test runs in debug mode only. -TEST(Error, UnhandledExpectedInFailureMode) { - EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); }, - "Expected<T> must be checked before access or destruction.") - << "Unchecked Expected<T> failure value did not cause an abort()"; -} - -// Test covariance of Expected. -TEST(Error, ExpectedCovariance) { - class B {}; - class D : public B {}; - - Expected<B *> A1(Expected<D *>(nullptr)); - // Check A1 by converting to bool before assigning to it. - (void)!!A1; - A1 = Expected<D *>(nullptr); - // Check A1 again before destruction. - (void)!!A1; - - Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr)); - // Check A2 by converting to bool before assigning to it. - (void)!!A2; - A2 = Expected<std::unique_ptr<D>>(nullptr); - // Check A2 again before destruction. - (void)!!A2; -} - -// Test that the ExitOnError utility works as expected. -TEST(Error, CantFailSuccess) { - cantFail(Error::success()); - - int X = cantFail(Expected<int>(42)); - EXPECT_EQ(X, 42) << "Expected value modified by cantFail"; - - int Dummy = 42; - int &Y = cantFail(Expected<int &>(Dummy)); - EXPECT_EQ(&Dummy, &Y) << "Reference mangled by cantFail"; -} - -// Test that cantFail results in a crash if you pass it a failure value. -TEST(Error, CantFailDeath) { - EXPECT_DEATH(cantFail(make_error<StringError>("foo")), - "cantFail called on failure value") - << "cantFail(Error) did not cause an abort for failure value"; - - EXPECT_DEATH(cantFail(Expected<int>(make_error<StringError>("foo"))), - "cantFail called on failure value") - << "cantFail(Expected<int>) did not cause an abort for failure value"; -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/extensible_rtti_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/extensible_rtti_test.cpp deleted file mode 100644 index feca1ec1d18..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/extensible_rtti_test.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//===-- extensible_rtti_test.cpp ------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -// Note: -// This unit test was adapted from -// llvm/unittests/Support/ExtensibleRTTITest.cpp -// -//===----------------------------------------------------------------------===// - -#include "extensible_rtti.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -namespace { - -class MyBase : public RTTIExtends<MyBase, RTTIRoot> {}; - -class MyDerivedA : public RTTIExtends<MyDerivedA, MyBase> {}; - -class MyDerivedB : public RTTIExtends<MyDerivedB, MyBase> {}; - -} // end anonymous namespace - -TEST(ExtensibleRTTITest, BaseCheck) { - MyBase MB; - MyDerivedA MDA; - MyDerivedB MDB; - - // Check MB properties. - EXPECT_TRUE(isa<RTTIRoot>(MB)); - EXPECT_TRUE(isa<MyBase>(MB)); - EXPECT_FALSE(isa<MyDerivedA>(MB)); - EXPECT_FALSE(isa<MyDerivedB>(MB)); - - // Check MDA properties. - EXPECT_TRUE(isa<RTTIRoot>(MDA)); - EXPECT_TRUE(isa<MyBase>(MDA)); - EXPECT_TRUE(isa<MyDerivedA>(MDA)); - EXPECT_FALSE(isa<MyDerivedB>(MDA)); - - // Check MDB properties. - EXPECT_TRUE(isa<RTTIRoot>(MDB)); - EXPECT_TRUE(isa<MyBase>(MDB)); - EXPECT_FALSE(isa<MyDerivedA>(MDB)); - EXPECT_TRUE(isa<MyDerivedB>(MDB)); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/orc_unit_test_main.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/orc_unit_test_main.cpp deleted file mode 100644 index d02101704d6..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/orc_unit_test_main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -//===-- orc_unit_test_main.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -//===----------------------------------------------------------------------===// -#include "gtest/gtest.h" - -int main(int argc, char **argv) { - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp deleted file mode 100644 index 3b55aa9662c..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp +++ /dev/null @@ -1,163 +0,0 @@ -//===-- simple_packed_serialization_test.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -//===----------------------------------------------------------------------===// - -#include "simple_packed_serialization.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -TEST(SimplePackedSerializationTest, SPSOutputBuffer) { - constexpr unsigned NumBytes = 8; - char Buffer[NumBytes]; - char Zero = 0; - SPSOutputBuffer OB(Buffer, NumBytes); - - // Expect that we can write NumBytes of content. - for (unsigned I = 0; I != NumBytes; ++I) { - char C = I; - EXPECT_TRUE(OB.write(&C, 1)); - } - - // Expect an error when we attempt to write an extra byte. - EXPECT_FALSE(OB.write(&Zero, 1)); - - // Check that the buffer contains the expected content. - for (unsigned I = 0; I != NumBytes; ++I) - EXPECT_EQ(Buffer[I], (char)I); -} - -TEST(SimplePackedSerializationTest, SPSInputBuffer) { - char Buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; - SPSInputBuffer IB(Buffer, sizeof(Buffer)); - - char C; - for (unsigned I = 0; I != sizeof(Buffer); ++I) { - EXPECT_TRUE(IB.read(&C, 1)); - EXPECT_EQ(C, (char)I); - } - - EXPECT_FALSE(IB.read(&C, 1)); -} - -template <typename SPSTagT, typename T> -static void blobSerializationRoundTrip(const T &Value) { - using BST = SPSSerializationTraits<SPSTagT, T>; - - size_t Size = BST::size(Value); - auto Buffer = std::make_unique<char[]>(Size); - SPSOutputBuffer OB(Buffer.get(), Size); - - EXPECT_TRUE(BST::serialize(OB, Value)); - - SPSInputBuffer IB(Buffer.get(), Size); - - T DSValue; - EXPECT_TRUE(BST::deserialize(IB, DSValue)); - - EXPECT_EQ(Value, DSValue) - << "Incorrect value after serialization/deserialization round-trip"; -} - -template <typename T> static void testFixedIntegralTypeSerialization() { - blobSerializationRoundTrip<T, T>(0); - blobSerializationRoundTrip<T, T>(static_cast<T>(1)); - if (std::is_signed<T>::value) { - blobSerializationRoundTrip<T, T>(static_cast<T>(-1)); - blobSerializationRoundTrip<T, T>(std::numeric_limits<T>::min()); - } - blobSerializationRoundTrip<T, T>(std::numeric_limits<T>::max()); -} - -TEST(SimplePackedSerializationTest, BoolSerialization) { - blobSerializationRoundTrip<bool, bool>(true); - blobSerializationRoundTrip<bool, bool>(false); -} - -TEST(SimplePackedSerializationTest, CharSerialization) { - blobSerializationRoundTrip<char, char>((char)0x00); - blobSerializationRoundTrip<char, char>((char)0xAA); - blobSerializationRoundTrip<char, char>((char)0xFF); -} - -TEST(SimplePackedSerializationTest, Int8Serialization) { - testFixedIntegralTypeSerialization<int8_t>(); -} - -TEST(SimplePackedSerializationTest, UInt8Serialization) { - testFixedIntegralTypeSerialization<uint8_t>(); -} - -TEST(SimplePackedSerializationTest, Int16Serialization) { - testFixedIntegralTypeSerialization<int16_t>(); -} - -TEST(SimplePackedSerializationTest, UInt16Serialization) { - testFixedIntegralTypeSerialization<uint16_t>(); -} - -TEST(SimplePackedSerializationTest, Int32Serialization) { - testFixedIntegralTypeSerialization<int32_t>(); -} - -TEST(SimplePackedSerializationTest, UInt32Serialization) { - testFixedIntegralTypeSerialization<uint32_t>(); -} - -TEST(SimplePackedSerializationTest, Int64Serialization) { - testFixedIntegralTypeSerialization<int64_t>(); -} - -TEST(SimplePackedSerializationTest, UInt64Serialization) { - testFixedIntegralTypeSerialization<uint64_t>(); -} - -TEST(SimplePackedSerializationTest, SequenceSerialization) { - std::vector<int32_t> V({1, 2, -47, 139}); - blobSerializationRoundTrip<SPSSequence<int32_t>, std::vector<int32_t>>(V); -} - -TEST(SimplePackedSerializationTest, StringViewCharSequenceSerialization) { - const char *HW = "Hello, world!"; - blobSerializationRoundTrip<SPSString, string_view>(string_view(HW)); -} - -TEST(SimplePackedSerializationTest, StdPairSerialization) { - std::pair<int32_t, std::string> P(42, "foo"); - blobSerializationRoundTrip<SPSTuple<int32_t, SPSString>, - std::pair<int32_t, std::string>>(P); -} - -TEST(SimplePackedSerializationTest, ArgListSerialization) { - using BAL = SPSArgList<bool, int32_t, SPSString>; - - bool Arg1 = true; - int32_t Arg2 = 42; - std::string Arg3 = "foo"; - - size_t Size = BAL::size(Arg1, Arg2, Arg3); - auto Buffer = std::make_unique<char[]>(Size); - SPSOutputBuffer OB(Buffer.get(), Size); - - EXPECT_TRUE(BAL::serialize(OB, Arg1, Arg2, Arg3)); - - SPSInputBuffer IB(Buffer.get(), Size); - - bool ArgOut1; - int32_t ArgOut2; - std::string ArgOut3; - - EXPECT_TRUE(BAL::deserialize(IB, ArgOut1, ArgOut2, ArgOut3)); - - EXPECT_EQ(Arg1, ArgOut1); - EXPECT_EQ(Arg2, ArgOut2); - EXPECT_EQ(Arg3, ArgOut3); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/stl_extras_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/stl_extras_test.cpp deleted file mode 100644 index 0a505c00dc0..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/stl_extras_test.cpp +++ /dev/null @@ -1,65 +0,0 @@ -//===-- stl_extras_test.cpp ------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -// Note: -// This unit test was adapted from tests in -// llvm/unittests/ADT/STLExtrasTest.cpp -// -//===----------------------------------------------------------------------===// - -#include "stl_extras.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -TEST(STLExtrasTest, ApplyTuple) { - auto T = std::make_tuple(1, 3, 7); - auto U = __orc_rt::apply_tuple( - [](int A, int B, int C) { return std::make_tuple(A - B, B - C, C - A); }, - T); - - EXPECT_EQ(-2, std::get<0>(U)); - EXPECT_EQ(-4, std::get<1>(U)); - EXPECT_EQ(6, std::get<2>(U)); - - auto V = __orc_rt::apply_tuple( - [](int A, int B, int C) { - return std::make_tuple(std::make_pair(A, char('A' + A)), - std::make_pair(B, char('A' + B)), - std::make_pair(C, char('A' + C))); - }, - T); - - EXPECT_EQ(std::make_pair(1, 'B'), std::get<0>(V)); - EXPECT_EQ(std::make_pair(3, 'D'), std::get<1>(V)); - EXPECT_EQ(std::make_pair(7, 'H'), std::get<2>(V)); -} - -class apply_variadic { - static int apply_one(int X) { return X + 1; } - static char apply_one(char C) { return C + 1; } - static std::string apply_one(std::string S) { - return S.substr(0, S.size() - 1); - } - -public: - template <typename... Ts> auto operator()(Ts &&... Items) { - return std::make_tuple(apply_one(Items)...); - } -}; - -TEST(STLExtrasTest, ApplyTupleVariadic) { - auto Items = std::make_tuple(1, std::string("Test"), 'X'); - auto Values = __orc_rt::apply_tuple(apply_variadic(), Items); - - EXPECT_EQ(2, std::get<0>(Values)); - EXPECT_EQ("Tes", std::get<1>(Values)); - EXPECT_EQ('Y', std::get<2>(Values)); -} diff --git a/gnu/llvm/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp b/gnu/llvm/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp deleted file mode 100644 index 12f4a93dcea..00000000000 --- a/gnu/llvm/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp +++ /dev/null @@ -1,105 +0,0 @@ -//===-- wrapper_function_utils_test.cpp -----------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of the ORC runtime. -// -//===----------------------------------------------------------------------===// - -#include "wrapper_function_utils.h" -#include "gtest/gtest.h" - -using namespace __orc_rt; - -namespace { -constexpr const char *TestString = "test string"; -} // end anonymous namespace - -TEST(WrapperFunctionUtilsTest, DefaultWrapperFunctionResult) { - WrapperFunctionResult R; - EXPECT_TRUE(R.empty()); - EXPECT_EQ(R.size(), 0U); - EXPECT_EQ(R.getOutOfBandError(), nullptr); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromCStruct) { - __orc_rt_CWrapperFunctionResult CR = - __orc_rt_CreateCWrapperFunctionResultFromString(TestString); - WrapperFunctionResult R(CR); - EXPECT_EQ(R.size(), strlen(TestString) + 1); - EXPECT_TRUE(strcmp(R.data(), TestString) == 0); - EXPECT_FALSE(R.empty()); - EXPECT_EQ(R.getOutOfBandError(), nullptr); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromRange) { - auto R = WrapperFunctionResult::copyFrom(TestString, strlen(TestString) + 1); - EXPECT_EQ(R.size(), strlen(TestString) + 1); - EXPECT_TRUE(strcmp(R.data(), TestString) == 0); - EXPECT_FALSE(R.empty()); - EXPECT_EQ(R.getOutOfBandError(), nullptr); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromCString) { - auto R = WrapperFunctionResult::copyFrom(TestString); - EXPECT_EQ(R.size(), strlen(TestString) + 1); - EXPECT_TRUE(strcmp(R.data(), TestString) == 0); - EXPECT_FALSE(R.empty()); - EXPECT_EQ(R.getOutOfBandError(), nullptr); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromStdString) { - auto R = WrapperFunctionResult::copyFrom(std::string(TestString)); - EXPECT_EQ(R.size(), strlen(TestString) + 1); - EXPECT_TRUE(strcmp(R.data(), TestString) == 0); - EXPECT_FALSE(R.empty()); - EXPECT_EQ(R.getOutOfBandError(), nullptr); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromOutOfBandError) { - auto R = WrapperFunctionResult::createOutOfBandError(TestString); - EXPECT_FALSE(R.empty()); - EXPECT_TRUE(strcmp(R.getOutOfBandError(), TestString) == 0); -} - -static void voidNoop() {} - -static __orc_rt_CWrapperFunctionResult voidNoopWrapper(const char *ArgData, - size_t ArgSize) { - return WrapperFunction<void()>::handle(ArgData, ArgSize, voidNoop).release(); -} - -static __orc_rt_CWrapperFunctionResult addWrapper(const char *ArgData, - size_t ArgSize) { - return WrapperFunction<int32_t(int32_t, int32_t)>::handle( - ArgData, ArgSize, - [](int32_t X, int32_t Y) -> int32_t { return X + Y; }) - .release(); -} - -extern "C" __orc_rt_Opaque __orc_rt_jit_dispatch_ctx{}; - -extern "C" __orc_rt_CWrapperFunctionResult -__orc_rt_jit_dispatch(__orc_rt_Opaque *Ctx, const void *FnTag, - const char *ArgData, size_t ArgSize) { - using WrapperFunctionType = - __orc_rt_CWrapperFunctionResult (*)(const char *, size_t); - - return reinterpret_cast<WrapperFunctionType>(const_cast<void *>(FnTag))( - ArgData, ArgSize); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionCallVoidNoopAndHandle) { - EXPECT_FALSE(!!WrapperFunction<void()>::call((void *)&voidNoopWrapper)); -} - -TEST(WrapperFunctionUtilsTest, WrapperFunctionCallAddWrapperAndHandle) { - int32_t Result; - EXPECT_FALSE(!!WrapperFunction<int32_t(int32_t, int32_t)>::call( - (void *)&addWrapper, Result, 1, 2)); - EXPECT_EQ(Result, (int32_t)3); -} diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_allocator_bytemap.h b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_allocator_bytemap.h deleted file mode 100644 index 0084bb62c83..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_allocator_bytemap.h +++ /dev/null @@ -1,107 +0,0 @@ -//===-- sanitizer_allocator_bytemap.h ---------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Part of the Sanitizer Allocator. -// -//===----------------------------------------------------------------------===// -#ifndef SANITIZER_ALLOCATOR_H -#error This file must be included inside sanitizer_allocator.h -#endif - -// Maps integers in rage [0, kSize) to u8 values. -template <u64 kSize, typename AddressSpaceViewTy = LocalAddressSpaceView> -class FlatByteMap { - public: - using AddressSpaceView = AddressSpaceViewTy; - void Init() { - internal_memset(map_, 0, sizeof(map_)); - } - - void set(uptr idx, u8 val) { - CHECK_LT(idx, kSize); - CHECK_EQ(0U, map_[idx]); - map_[idx] = val; - } - u8 operator[] (uptr idx) { - CHECK_LT(idx, kSize); - // FIXME: CHECK may be too expensive here. - return map_[idx]; - } - private: - u8 map_[kSize]; -}; - -// TwoLevelByteMap maps integers in range [0, kSize1*kSize2) to u8 values. -// It is implemented as a two-dimensional array: array of kSize1 pointers -// to kSize2-byte arrays. The secondary arrays are mmaped on demand. -// Each value is initially zero and can be set to something else only once. -// Setting and getting values from multiple threads is safe w/o extra locking. -template <u64 kSize1, u64 kSize2, - typename AddressSpaceViewTy = LocalAddressSpaceView, - class MapUnmapCallback = NoOpMapUnmapCallback> -class TwoLevelByteMap { - public: - using AddressSpaceView = AddressSpaceViewTy; - void Init() { - internal_memset(map1_, 0, sizeof(map1_)); - mu_.Init(); - } - - void TestOnlyUnmap() { - for (uptr i = 0; i < kSize1; i++) { - u8 *p = Get(i); - if (!p) continue; - MapUnmapCallback().OnUnmap(reinterpret_cast<uptr>(p), kSize2); - UnmapOrDie(p, kSize2); - } - } - - uptr size() const { return kSize1 * kSize2; } - uptr size1() const { return kSize1; } - uptr size2() const { return kSize2; } - - void set(uptr idx, u8 val) { - CHECK_LT(idx, kSize1 * kSize2); - u8 *map2 = GetOrCreate(idx / kSize2); - CHECK_EQ(0U, map2[idx % kSize2]); - map2[idx % kSize2] = val; - } - - u8 operator[] (uptr idx) const { - CHECK_LT(idx, kSize1 * kSize2); - u8 *map2 = Get(idx / kSize2); - if (!map2) return 0; - auto value_ptr = AddressSpaceView::Load(&map2[idx % kSize2]); - return *value_ptr; - } - - private: - u8 *Get(uptr idx) const { - CHECK_LT(idx, kSize1); - return reinterpret_cast<u8 *>( - atomic_load(&map1_[idx], memory_order_acquire)); - } - - u8 *GetOrCreate(uptr idx) { - u8 *res = Get(idx); - if (!res) { - SpinMutexLock l(&mu_); - if (!(res = Get(idx))) { - res = (u8*)MmapOrDie(kSize2, "TwoLevelByteMap"); - MapUnmapCallback().OnMap(reinterpret_cast<uptr>(res), kSize2); - atomic_store(&map1_[idx], reinterpret_cast<uptr>(res), - memory_order_release); - } - } - return res; - } - - atomic_uintptr_t map1_[kSize1]; - StaticSpinMutex mu_; -}; - diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp index 369e5514e3f..f17f86d86d3 100644 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp +++ b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp @@ -14,7 +14,7 @@ #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ - SANITIZER_SOLARIS + SANITIZER_OPENBSD || SANITIZER_SOLARIS #include "sanitizer_common.h" #include "sanitizer_flags.h" @@ -34,7 +34,7 @@ // format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To // access stat from asm/stat.h, without conflicting with definition in // sys/stat.h, we use this trick. -#if defined(__mips64) +#if SANITIZER_MIPS64 #include <asm/unistd.h> #include <sys/types.h> #define stat kernel_stat @@ -78,8 +78,13 @@ #include <sys/personality.h> #endif +#if SANITIZER_LINUX && defined(__loongarch__) +# include <sys/sysmacros.h> +#endif + #if SANITIZER_FREEBSD #include <sys/exec.h> +#include <sys/procctl.h> #include <sys/sysctl.h> #include <machine/atomic.h> extern "C" { @@ -123,8 +128,9 @@ const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG; // Are we using 32-bit or 64-bit Linux syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 // but it still needs to use 64-bit syscalls. -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \ - SANITIZER_WORDSIZE == 64) +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \ + SANITIZER_WORDSIZE == 64 || \ + (defined(__mips__) && _MIPS_SIM == _ABIN32)) # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 #else # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 @@ -150,17 +156,47 @@ const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG; namespace __sanitizer { -#if SANITIZER_LINUX && defined(__x86_64__) -#include "sanitizer_syscall_linux_x86_64.inc" -#elif SANITIZER_LINUX && SANITIZER_RISCV64 -#include "sanitizer_syscall_linux_riscv64.inc" -#elif SANITIZER_LINUX && defined(__aarch64__) -#include "sanitizer_syscall_linux_aarch64.inc" -#elif SANITIZER_LINUX && defined(__arm__) -#include "sanitizer_syscall_linux_arm.inc" -#else -#include "sanitizer_syscall_generic.inc" -#endif +void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *old) { + CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, old)); +} + +ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) { + __sanitizer_sigset_t set; + internal_sigfillset(&set); +# if SANITIZER_LINUX && !SANITIZER_ANDROID + // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked + // on any thread, setuid call hangs. + // See test/sanitizer_common/TestCases/Linux/setuid.c. + internal_sigdelset(&set, 33); +# endif +# if SANITIZER_LINUX + // Seccomp-BPF-sandboxed processes rely on SIGSYS to handle trapped syscalls. + // If this signal is blocked, such calls cannot be handled and the process may + // hang. + internal_sigdelset(&set, 31); +# endif + SetSigProcMask(&set, &saved_); + if (copy) + internal_memcpy(copy, &saved_, sizeof(saved_)); +} + +ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); } + +# if SANITIZER_LINUX && defined(__x86_64__) +# include "sanitizer_syscall_linux_x86_64.inc" +# elif SANITIZER_LINUX && SANITIZER_RISCV64 +# include "sanitizer_syscall_linux_riscv64.inc" +# elif SANITIZER_LINUX && defined(__aarch64__) +# include "sanitizer_syscall_linux_aarch64.inc" +# elif SANITIZER_LINUX && defined(__arm__) +# include "sanitizer_syscall_linux_arm.inc" +# elif SANITIZER_LINUX && defined(__hexagon__) +# include "sanitizer_syscall_linux_hexagon.inc" +# elif SANITIZER_LINUX && SANITIZER_LOONGARCH64 +# include "sanitizer_syscall_linux_loongarch64.inc" +# else +# include "sanitizer_syscall_generic.inc" +# endif // --------------- sanitizer_libc.h #if !SANITIZER_SOLARIS && !SANITIZER_NETBSD @@ -204,7 +240,7 @@ uptr internal_close(fd_t fd) { } uptr internal_open(const char *filename, int flags) { -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# if SANITIZER_LINUX return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags); #else return internal_syscall(SYSCALL(open), (uptr)filename, flags); @@ -212,7 +248,7 @@ uptr internal_open(const char *filename, int flags) { } uptr internal_open(const char *filename, int flags, u32 mode) { -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# if SANITIZER_LINUX return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags, mode); #else @@ -241,7 +277,7 @@ uptr internal_ftruncate(fd_t fd, uptr size) { return res; } -#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX +#if (!SANITIZER_LINUX_USES_64BIT_SYSCALLS || SANITIZER_SPARC) && SANITIZER_LINUX static void stat64_to_stat(struct stat64 *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = in->st_dev; @@ -260,7 +296,29 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) { } #endif -#if defined(__mips64) +#if SANITIZER_LINUX && defined(__loongarch__) +static void statx_to_stat(struct statx *in, struct stat *out) { + internal_memset(out, 0, sizeof(*out)); + out->st_dev = makedev(in->stx_dev_major, in->stx_dev_minor); + out->st_ino = in->stx_ino; + out->st_mode = in->stx_mode; + out->st_nlink = in->stx_nlink; + out->st_uid = in->stx_uid; + out->st_gid = in->stx_gid; + out->st_rdev = makedev(in->stx_rdev_major, in->stx_rdev_minor); + out->st_size = in->stx_size; + out->st_blksize = in->stx_blksize; + out->st_blocks = in->stx_blocks; + out->st_atime = in->stx_atime.tv_sec; + out->st_atim.tv_nsec = in->stx_atime.tv_nsec; + out->st_mtime = in->stx_mtime.tv_sec; + out->st_mtim.tv_nsec = in->stx_mtime.tv_nsec; + out->st_ctime = in->stx_ctime.tv_sec; + out->st_ctim.tv_nsec = in->stx_ctime.tv_nsec; +} +#endif + +#if SANITIZER_MIPS64 // Undefine compatibility macros from <sys/stat.h> // so that they would not clash with the kernel_stat // st_[a|m|c]time fields @@ -311,52 +369,65 @@ static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) { #endif uptr internal_stat(const char *path, void *buf) { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); -#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# elif SANITIZER_LINUX +# if defined(__loongarch__) + struct statx bufx; + int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path, + AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx); + statx_to_stat(&bufx, (struct stat *)buf); + return res; +# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \ + (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \ + !SANITIZER_SPARC return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); -#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS -# if defined(__mips64) - // For mips64, stat syscall fills buffer in the format of kernel_stat - struct kernel_stat kbuf; - int res = internal_syscall(SYSCALL(stat), path, &kbuf); - kernel_stat_to_stat(&kbuf, (struct stat *)buf); +# else + struct stat64 buf64; + int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path, + (uptr)&buf64, 0); + stat64_to_stat(&buf64, (struct stat *)buf); return res; -# else - return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf); -# endif -#else +# endif +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(stat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; -#endif +# endif } uptr internal_lstat(const char *path, void *buf) { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, AT_SYMLINK_NOFOLLOW); -#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# elif SANITIZER_LINUX +# if defined(__loongarch__) + struct statx bufx; + int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path, + AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, + STATX_BASIC_STATS, (uptr)&bufx); + statx_to_stat(&bufx, (struct stat *)buf); + return res; +# elif (defined(_LP64) || SANITIZER_X32 || \ + (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \ + !SANITIZER_SPARC return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, AT_SYMLINK_NOFOLLOW); -#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS -# if SANITIZER_MIPS64 - // For mips64, lstat syscall fills buffer in the format of kernel_stat - struct kernel_stat kbuf; - int res = internal_syscall(SYSCALL(lstat), path, &kbuf); - kernel_stat_to_stat(&kbuf, (struct stat *)buf); +# else + struct stat64 buf64; + int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path, + (uptr)&buf64, AT_SYMLINK_NOFOLLOW); + stat64_to_stat(&buf64, (struct stat *)buf); return res; -# else - return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf); -# endif -#else +# endif +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(lstat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; -#endif +# endif } uptr internal_fstat(fd_t fd, void *buf) { @@ -367,9 +438,15 @@ uptr internal_fstat(fd_t fd, void *buf) { int res = internal_syscall(SYSCALL(fstat), fd, &kbuf); kernel_stat_to_stat(&kbuf, (struct stat *)buf); return res; -# else +# elif SANITIZER_LINUX && defined(__loongarch__) + struct statx bufx; + int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH, + STATX_BASIC_STATS, (uptr)&bufx); + statx_to_stat(&bufx, (struct stat *)buf); + return res; +# else return internal_syscall(SYSCALL(fstat), fd, (uptr)buf); -# endif +# endif #else struct stat64 buf64; int res = internal_syscall(SYSCALL(fstat64), fd, &buf64); @@ -390,7 +467,7 @@ uptr internal_dup(int oldfd) { } uptr internal_dup2(int oldfd, int newfd) { -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# if SANITIZER_LINUX return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0); #else return internal_syscall(SYSCALL(dup2), oldfd, newfd); @@ -398,7 +475,7 @@ uptr internal_dup2(int oldfd, int newfd) { } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# if SANITIZER_LINUX return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf, bufsize); #else @@ -407,7 +484,7 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) { } uptr internal_unlink(const char *path) { -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# if SANITIZER_LINUX return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0); #else return internal_syscall(SYSCALL(unlink), (uptr)path); @@ -415,15 +492,15 @@ uptr internal_unlink(const char *path) { } uptr internal_rename(const char *oldpath, const char *newpath) { -#if defined(__riscv) +# if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__) return internal_syscall(SYSCALL(renameat2), AT_FDCWD, (uptr)oldpath, AT_FDCWD, (uptr)newpath, 0); -#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# elif SANITIZER_LINUX return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD, (uptr)newpath); -#else +# else return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath); -#endif +# endif } uptr internal_sched_yield() { @@ -460,17 +537,20 @@ bool FileExists(const char *filename) { if (ShouldMockFailureToOpen(filename)) return false; struct stat st; -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS - if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0)) -#else if (internal_stat(filename, &st)) -#endif return false; // Sanity check: filename is a regular file. return S_ISREG(st.st_mode); } -#if !SANITIZER_NETBSD +bool DirExists(const char *path) { + struct stat st; + if (internal_stat(path, &st)) + return false; + return S_ISDIR(st.st_mode); +} + +# if !SANITIZER_NETBSD tid_t GetTid() { #if SANITIZER_FREEBSD long Tid; @@ -518,7 +598,7 @@ u64 NanoTime() { // 'environ' array (on some others) and does not use libc. This function // should be called first inside __asan_init. const char *GetEnv(const char *name) { -#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_SOLARIS +#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_SOLARIS if (::environ != 0) { uptr NameLen = internal_strlen(name); for (char **Env = ::environ; *Env != 0; Env++) { @@ -659,48 +739,6 @@ void FutexWake(atomic_uint32_t *p, u32 count) { # endif } -enum { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 }; - -BlockingMutex::BlockingMutex() { - internal_memset(this, 0, sizeof(*this)); -} - -void BlockingMutex::Lock() { - CHECK_EQ(owner_, 0); - atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_); - if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) - return; - while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) { -#if SANITIZER_FREEBSD - _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0); -#elif SANITIZER_NETBSD - sched_yield(); /* No userspace futex-like synchronization */ -#else - internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT_PRIVATE, MtxSleeping, - 0, 0, 0); -#endif - } -} - -void BlockingMutex::Unlock() { - atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_); - u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release); - CHECK_NE(v, MtxUnlocked); - if (v == MtxSleeping) { -#if SANITIZER_FREEBSD - _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0); -#elif SANITIZER_NETBSD - /* No userspace futex-like synchronization */ -#else - internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0); -#endif - } -} - -void BlockingMutex::CheckLocked() const { - auto m = reinterpret_cast<atomic_uint32_t const *>(&opaque_storage_); - CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); -} # endif // !SANITIZER_SOLARIS // ----------------- sanitizer_linux.h @@ -711,17 +749,17 @@ void BlockingMutex::CheckLocked() const { // Not used #else struct linux_dirent { -#if SANITIZER_X32 || defined(__aarch64__) || SANITIZER_RISCV64 +# if SANITIZER_X32 || SANITIZER_LINUX u64 d_ino; u64 d_off; -#else +# else unsigned long d_ino; unsigned long d_off; -#endif +# endif unsigned short d_reclen; -#if defined(__aarch64__) || SANITIZER_RISCV64 +# if SANITIZER_LINUX unsigned char d_type; -#endif +# endif char d_name[256]; }; #endif @@ -757,11 +795,11 @@ int internal_dlinfo(void *handle, int request, void *p) { uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(getdirentries), fd, (uptr)dirp, count, NULL); -#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# elif SANITIZER_LINUX return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count); -#else +# else return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count); -#endif +# endif } uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { @@ -772,18 +810,29 @@ uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5); } -#endif +# if defined(__x86_64__) +# include <asm/unistd_64.h> +// Currently internal_arch_prctl() is only needed on x86_64. +uptr internal_arch_prctl(int option, uptr arg2) { + return internal_syscall(__NR_arch_prctl, option, arg2); +} +# endif +# endif uptr internal_sigaltstack(const void *ss, void *oss) { return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss); } int internal_fork() { -#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS +# if SANITIZER_LINUX +# if SANITIZER_S390 + return internal_syscall(SYSCALL(clone), 0, SIGCHLD); +# else return internal_syscall(SYSCALL(clone), SIGCHLD, 0); -#else +# endif +# else return internal_syscall(SYSCALL(fork)); -#endif +# endif } #if SANITIZER_FREEBSD @@ -911,6 +960,10 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) { return k_set->sig[idx] & ((uptr)1 << bit); } #elif SANITIZER_FREEBSD +uptr internal_procctl(int type, int id, int cmd, void *data) { + return internal_syscall(SYSCALL(procctl), type, id, cmd, data); +} + void internal_sigdelset(__sanitizer_sigset_t *set, int signum) { sigset_t *rset = reinterpret_cast<sigset_t *>(set); sigdelset(rset, signum); @@ -1052,7 +1105,7 @@ uptr GetMaxVirtualAddress() { #if SANITIZER_NETBSD && defined(__x86_64__) return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE) #elif SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) || defined(__aarch64__) +# if defined(__powerpc64__) || defined(__aarch64__) || defined(__loongarch__) // On PowerPC64 we have two different address space layouts: 44- and 46-bit. // We somehow need to figure out which one we are using now and choose // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. @@ -1060,10 +1113,11 @@ uptr GetMaxVirtualAddress() { // of the address space, so simply checking the stack address is not enough. // This should (does) work for both PowerPC64 Endian modes. // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit. + // loongarch64 also has multiple address space layouts: default is 47-bit. return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1; #elif SANITIZER_RISCV64 return (1ULL << 38) - 1; -# elif defined(__mips64) +# elif SANITIZER_MIPS64 return (1ULL << 40) - 1; // 0x000000ffffffffffUL; # elif defined(__s390x__) return (1ULL << 53) - 1; // 0x001fffffffffffffUL; @@ -1217,7 +1271,8 @@ void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { } #endif -#if defined(__x86_64__) && SANITIZER_LINUX +#if SANITIZER_LINUX +#if defined(__x86_64__) // We cannot use glibc's clone wrapper, because it messes with the child // task's TLS. It writes the PID and TID of the child task to its thread // descriptor, but in our case the child task shares the thread descriptor with @@ -1399,7 +1454,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, #elif defined(__aarch64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { - long long res; + register long long res __asm__("x0"); if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); @@ -1447,6 +1502,47 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "x30", "memory"); return res; } +#elif SANITIZER_LOONGARCH64 +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + if (!fn || !child_stack) + return -EINVAL; + + CHECK_EQ(0, (uptr)child_stack % 16); + + register int res __asm__("$a0"); + register int __flags __asm__("$a0") = flags; + register void *__stack __asm__("$a1") = child_stack; + register int *__ptid __asm__("$a2") = parent_tidptr; + register int *__ctid __asm__("$a3") = child_tidptr; + register void *__tls __asm__("$a4") = newtls; + register int (*__fn)(void *) __asm__("$a5") = fn; + register void *__arg __asm__("$a6") = arg; + register int nr_clone __asm__("$a7") = __NR_clone; + + __asm__ __volatile__( + "syscall 0\n" + + // if ($a0 != 0) + // return $a0; + "bnez $a0, 1f\n" + + // In the child, now. Call "fn(arg)". + "move $a0, $a6\n" + "jirl $ra, $a5, 0\n" + + // Call _exit($a0). + "addi.d $a7, $zero, %9\n" + "syscall 0\n" + + "1:\n" + + : "=r"(res) + : "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__ctid), "r"(__tls), + "r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit) + : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8"); + return res; +} #elif defined(__powerpc64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { @@ -1556,7 +1652,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29"); return res; } -#elif defined(__i386__) && SANITIZER_LINUX +#elif defined(__i386__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { int res; @@ -1621,7 +1717,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "memory"); return res; } -#elif defined(__arm__) && SANITIZER_LINUX +#elif defined(__arm__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { unsigned int res; @@ -1687,7 +1783,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "memory"); return res; } -#endif // defined(__x86_64__) && SANITIZER_LINUX +#endif +#endif // SANITIZER_LINUX #if SANITIZER_LINUX int internal_uname(struct utsname *buf) { @@ -1778,23 +1875,18 @@ HandleSignalMode GetHandleSignalMode(int signum) { #if !SANITIZER_GO void *internal_start_thread(void *(*func)(void *arg), void *arg) { + if (&real_pthread_create == 0) + return nullptr; // Start the thread with signals blocked, otherwise it can steal user signals. - __sanitizer_sigset_t set, old; - internal_sigfillset(&set); -#if SANITIZER_LINUX && !SANITIZER_ANDROID - // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked - // on any thread, setuid call hangs (see test/tsan/setuid.c). - internal_sigdelset(&set, 33); -#endif - internal_sigprocmask(SIG_SETMASK, &set, &old); + ScopedBlockSignals block(nullptr); void *th; real_pthread_create(&th, nullptr, func, arg); - internal_sigprocmask(SIG_SETMASK, &old, nullptr); return th; } void internal_join_thread(void *th) { - real_pthread_join(th, nullptr); + if (&real_pthread_join) + real_pthread_join(th, nullptr); } #else void *internal_start_thread(void *(*func)(void *), void *arg) { return 0; } @@ -1802,7 +1894,7 @@ void *internal_start_thread(void *(*func)(void *), void *arg) { return 0; } void internal_join_thread(void *th) {} #endif -#if defined(__aarch64__) +#if SANITIZER_LINUX && defined(__aarch64__) // Android headers in the older NDK releases miss this definition. struct __sanitizer_esr_context { struct _aarch64_ctx head; @@ -1811,7 +1903,7 @@ struct __sanitizer_esr_context { static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { static const u32 kEsrMagic = 0x45535201; - u8 *aux = ucontext->uc_mcontext.__reserved; + u8 *aux = reinterpret_cast<u8 *>(ucontext->uc_mcontext.__reserved); while (true) { _aarch64_ctx *ctx = (_aarch64_ctx *)aux; if (ctx->size == 0) break; @@ -1823,6 +1915,11 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { } return false; } +#elif SANITIZER_FREEBSD && defined(__aarch64__) +// FreeBSD doesn't provide ESR in the ucontext. +static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { + return false; +} #endif using Context = ucontext_t; @@ -1841,7 +1938,7 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #else uptr err = ucontext->uc_mcontext.gregs[REG_ERR]; #endif // SANITIZER_FREEBSD - return err & PF_WRITE ? WRITE : READ; + return err & PF_WRITE ? Write : Read; #elif defined(__mips__) uint32_t *exception_source; uint32_t faulty_instruction; @@ -1864,7 +1961,7 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0x2a: // swl case 0x2e: // swr #endif - return SignalContext::WRITE; + return SignalContext::Write; case 0x20: // lb case 0x24: // lbu @@ -1879,27 +1976,34 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0x22: // lwl case 0x26: // lwr #endif - return SignalContext::READ; + return SignalContext::Read; #if __mips_isa_rev == 6 case 0x3b: // pcrel op_code = (faulty_instruction >> 19) & 0x3; switch (op_code) { case 0x1: // lwpc case 0x2: // lwupc - return SignalContext::READ; + return SignalContext::Read; } #endif } - return SignalContext::UNKNOWN; + return SignalContext::Unknown; #elif defined(__arm__) static const uptr FSR_WRITE = 1U << 11; uptr fsr = ucontext->uc_mcontext.error_code; - return fsr & FSR_WRITE ? WRITE : READ; + return fsr & FSR_WRITE ? Write : Read; #elif defined(__aarch64__) static const u64 ESR_ELx_WNR = 1U << 6; u64 esr; - if (!Aarch64GetESR(ucontext, &esr)) return UNKNOWN; - return esr & ESR_ELx_WNR ? WRITE : READ; + if (!Aarch64GetESR(ucontext, &esr)) return Unknown; + return esr & ESR_ELx_WNR ? Write : Read; +#elif defined(__loongarch__) + u32 flags = ucontext->uc_mcontext.__flags; + if (flags & SC_ADDRERR_RD) + return SignalContext::Read; + if (flags & SC_ADDRERR_WR) + return SignalContext::Write; + return SignalContext::Unknown; #elif defined(__sparc__) // Decode the instruction to determine the access type. // From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype). @@ -1915,9 +2019,13 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #endif #endif u32 instr = *(u32 *)pc; - return (instr >> 21) & 1 ? WRITE: READ; + return (instr >> 21) & 1 ? Write: Read; #elif defined(__riscv) +#if SANITIZER_FREEBSD + unsigned long pc = ucontext->uc_mcontext.mc_gpregs.gp_sepc; +#else unsigned long pc = ucontext->uc_mcontext.__gregs[REG_PC]; +#endif unsigned faulty_instruction = *(uint16_t *)pc; #if defined(__riscv_compressed) @@ -1931,7 +2039,7 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #if __riscv_xlen == 64 case 0b10'011: // c.ldsp (rd != x0) #endif - return rd ? SignalContext::READ : SignalContext::UNKNOWN; + return rd ? SignalContext::Read : SignalContext::Unknown; case 0b00'010: // c.lw #if __riscv_flen >= 32 && __riscv_xlen == 32 case 0b10'011: // c.flwsp @@ -1943,7 +2051,7 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0b00'001: // c.fld case 0b10'001: // c.fldsp #endif - return SignalContext::READ; + return SignalContext::Read; case 0b00'110: // c.sw case 0b10'110: // c.swsp #if __riscv_flen >= 32 || __riscv_xlen == 64 @@ -1954,9 +2062,9 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0b00'101: // c.fsd case 0b10'101: // c.fsdsp #endif - return SignalContext::WRITE; + return SignalContext::Write; default: - return SignalContext::UNKNOWN; + return SignalContext::Unknown; } } #endif @@ -1974,9 +2082,9 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #endif case 0b100: // lbu case 0b101: // lhu - return SignalContext::READ; + return SignalContext::Read; default: - return SignalContext::UNKNOWN; + return SignalContext::Unknown; } case 0b0100011: // stores switch (funct3) { @@ -1986,9 +2094,9 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #if __riscv_xlen == 64 case 0b011: // sd #endif - return SignalContext::WRITE; + return SignalContext::Write; default: - return SignalContext::UNKNOWN; + return SignalContext::Unknown; } #if __riscv_flen >= 32 case 0b0000111: // floating-point loads @@ -1997,9 +2105,9 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #if __riscv_flen == 64 case 0b011: // fld #endif - return SignalContext::READ; + return SignalContext::Read; default: - return SignalContext::UNKNOWN; + return SignalContext::Unknown; } case 0b0100111: // floating-point stores switch (funct3) { @@ -2007,17 +2115,17 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { #if __riscv_flen == 64 case 0b011: // fsd #endif - return SignalContext::WRITE; + return SignalContext::Write; default: - return SignalContext::UNKNOWN; + return SignalContext::Unknown; } #endif default: - return SignalContext::UNKNOWN; + return SignalContext::Unknown; } #else (void)ucontext; - return UNKNOWN; // FIXME: Implement. + return Unknown; // FIXME: Implement. #endif } @@ -2044,10 +2152,17 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *bp = ucontext->uc_mcontext.arm_fp; *sp = ucontext->uc_mcontext.arm_sp; #elif defined(__aarch64__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.mc_gpregs.gp_elr; + *bp = ucontext->uc_mcontext.mc_gpregs.gp_x[29]; + *sp = ucontext->uc_mcontext.mc_gpregs.gp_sp; +# else ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.pc; *bp = ucontext->uc_mcontext.regs[29]; *sp = ucontext->uc_mcontext.sp; +# endif #elif defined(__hppa__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.sc_iaoq[0]; @@ -2092,12 +2207,19 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *sp = ucontext->uc_mcontext.gregs[REG_UESP]; # endif #elif defined(__powerpc__) || defined(__powerpc64__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t *)context; + *pc = ucontext->uc_mcontext.mc_srr0; + *sp = ucontext->uc_mcontext.mc_frame[1]; + *bp = ucontext->uc_mcontext.mc_frame[31]; +# else ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.regs->nip; *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; // The powerpc{,64}-linux ABIs do not specify r31 as the frame // pointer, but GCC always uses r31 when we need a frame pointer. *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; +# endif #elif defined(__sparc__) #if defined(__arch64__) || defined(__sparcv9) #define STACK_BIAS 2047 @@ -2136,12 +2258,28 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *sp = ucontext->uc_mcontext.gregs[15]; #elif defined(__riscv) ucontext_t *ucontext = (ucontext_t*)context; +# if SANITIZER_FREEBSD + *pc = ucontext->uc_mcontext.mc_gpregs.gp_sepc; + *bp = ucontext->uc_mcontext.mc_gpregs.gp_s[0]; + *sp = ucontext->uc_mcontext.mc_gpregs.gp_sp; +# else *pc = ucontext->uc_mcontext.__gregs[REG_PC]; *bp = ucontext->uc_mcontext.__gregs[REG_S0]; *sp = ucontext->uc_mcontext.__gregs[REG_SP]; -#else -# error "Unsupported arch" -#endif +# endif +# elif defined(__hexagon__) + ucontext_t *ucontext = (ucontext_t *)context; + *pc = ucontext->uc_mcontext.pc; + *bp = ucontext->uc_mcontext.r30; + *sp = ucontext->uc_mcontext.r29; +# elif defined(__loongarch__) + ucontext_t *ucontext = (ucontext_t *)context; + *pc = ucontext->uc_mcontext.__pc; + *bp = ucontext->uc_mcontext.__gregs[22]; + *sp = ucontext->uc_mcontext.__gregs[3]; +# else +# error "Unsupported arch" +# endif } void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); } @@ -2150,10 +2288,6 @@ void InitializePlatformEarly() { // Do nothing. } -void MaybeReexec() { - // No need to re-exec on Linux. -} - void CheckASLR() { #if SANITIZER_NETBSD int mib[3]; @@ -2175,49 +2309,35 @@ void CheckASLR() { GetArgv()[0]); Die(); } -#elif SANITIZER_PPC64V2 - // Disable ASLR for Linux PPC64LE. - int old_personality = personality(0xffffffff); - if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) { - VReport(1, "WARNING: Program is being run with address space layout " - "randomization (ASLR) enabled which prevents the thread and " - "memory sanitizers from working on powerpc64le.\n" - "ASLR will be disabled and the program re-executed.\n"); - CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1); - ReExec(); - } #elif SANITIZER_FREEBSD - int aslr_pie; - uptr len = sizeof(aslr_pie); -#if SANITIZER_WORDSIZE == 64 - if (UNLIKELY(internal_sysctlbyname("kern.elf64.aslr.pie_enable", - &aslr_pie, &len, NULL, 0) == -1)) { + int aslr_status; + int r = internal_procctl(P_PID, 0, PROC_ASLR_STATUS, &aslr_status); + if (UNLIKELY(r == -1)) { // We're making things less 'dramatic' here since - // the OID is not necessarily guaranteed to be here + // the cmd is not necessarily guaranteed to be here // just yet regarding FreeBSD release return; } - - if (aslr_pie > 0) { + if ((aslr_status & PROC_ASLR_ACTIVE) != 0) { Printf("This sanitizer is not compatible with enabled ASLR " "and binaries compiled with PIE\n"); Die(); } -#endif - // there might be 32 bits compat for 64 bits - if (UNLIKELY(internal_sysctlbyname("kern.elf32.aslr.pie_enable", - &aslr_pie, &len, NULL, 0) == -1)) { - return; - } - - if (aslr_pie > 0) { - Printf("This sanitizer is not compatible with enabled ASLR " - "and binaries compiled with PIE\n"); - Die(); +# elif SANITIZER_PPC64V2 + // Disable ASLR for Linux PPC64LE. + int old_personality = personality(0xffffffff); + if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) { + VReport(1, + "WARNING: Program is being run with address space layout " + "randomization (ASLR) enabled which prevents the thread and " + "memory sanitizers from working on powerpc64le.\n" + "ASLR will be disabled and the program re-executed.\n"); + CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1); + ReExec(); } -#else +# else // Do nothing -#endif +# endif } void CheckMPROTECT() { diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index 389c53113b1..8b1955d5bf1 100644 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -49,26 +49,43 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); uptr internal_sigaltstack(const void* ss, void* oss); uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset); -#if SANITIZER_GLIBC + +void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset); +struct ScopedBlockSignals { + explicit ScopedBlockSignals(__sanitizer_sigset_t *copy); + ~ScopedBlockSignals(); + + ScopedBlockSignals &operator=(const ScopedBlockSignals &) = delete; + ScopedBlockSignals(const ScopedBlockSignals &) = delete; + + private: + __sanitizer_sigset_t saved_; +}; + +# if SANITIZER_GLIBC uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp); #endif // Linux-only syscalls. #if SANITIZER_LINUX uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); +# if defined(__x86_64__) +uptr internal_arch_prctl(int option, uptr arg2); +# endif // Used only by sanitizer_stoptheworld. Signal handlers that are actually used // (like the process-wide error reporting SEGV handler) must use // internal_sigaction instead. int internal_sigaction_norestorer(int signum, const void *act, void *oldact); void internal_sigdelset(__sanitizer_sigset_t *set, int signum); -#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \ - defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \ - defined(__arm__) || SANITIZER_RISCV64 +# if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \ + defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \ + defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr); #endif int internal_uname(struct utsname *buf); #elif SANITIZER_FREEBSD +uptr internal_procctl(int type, int id, int cmd, void *data); void internal_sigdelset(__sanitizer_sigset_t *set, int signum); #elif SANITIZER_NETBSD void internal_sigdelset(__sanitizer_sigset_t *set, int signum); diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_openbsd.cpp b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_openbsd.cpp deleted file mode 100644 index ed2d8edeb7a..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_openbsd.cpp +++ /dev/null @@ -1,115 +0,0 @@ -//===-- sanitizer_openbsd.cpp ---------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is shared between various sanitizers' runtime libraries and -// implements Solaris-specific functions. -//===----------------------------------------------------------------------===// - -#include "sanitizer_platform.h" -#if SANITIZER_OPENBSD - -#include <stdio.h> - -#include "sanitizer_common.h" -#include "sanitizer_flags.h" -#include "sanitizer_internal_defs.h" -#include "sanitizer_libc.h" -#include "sanitizer_placement_new.h" -#include "sanitizer_platform_limits_posix.h" -#include "sanitizer_procmaps.h" - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <pthread.h> -#include <sched.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/shm.h> -#include <sys/sysctl.h> -#include <sys/types.h> -#include <unistd.h> - -extern char **environ; - -namespace __sanitizer { - -uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd, - u64 offset) { - return (uptr)mmap(addr, length, prot, flags, fd, offset); -} - -uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); } - -int internal_mprotect(void *addr, uptr length, int prot) { - return mprotect(addr, length, prot); -} - -int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp, - const void *newp, uptr newlen) { - Printf("internal_sysctlbyname not implemented for OpenBSD"); - Die(); - return 0; -} - -uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { - // On OpenBSD we cannot get the full path - struct kinfo_proc kp; - uptr kl; - const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; - if (internal_sysctl(Mib, ARRAY_SIZE(Mib), &kp, &kl, NULL, 0) != -1) - return internal_snprintf(buf, - (KI_MAXCOMLEN < buf_len ? KI_MAXCOMLEN : buf_len), - "%s", kp.p_comm); - return (uptr)0; -} - -static void GetArgsAndEnv(char ***argv, char ***envp) { - uptr nargv; - uptr nenv; - int argvmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV}; - int envmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ENV}; - if (internal_sysctl(argvmib, 4, NULL, &nargv, NULL, 0) == -1) { - Printf("sysctl KERN_PROC_NARGV failed\n"); - Die(); - } - if (internal_sysctl(envmib, 4, NULL, &nenv, NULL, 0) == -1) { - Printf("sysctl KERN_PROC_NENV failed\n"); - Die(); - } - if (internal_sysctl(argvmib, 4, &argv, &nargv, NULL, 0) == -1) { - Printf("sysctl KERN_PROC_ARGV failed\n"); - Die(); - } - if (internal_sysctl(envmib, 4, &envp, &nenv, NULL, 0) == -1) { - Printf("sysctl KERN_PROC_ENV failed\n"); - Die(); - } -} - -char **GetArgv() { - char **argv, **envp; - GetArgsAndEnv(&argv, &envp); - return argv; -} - -char **GetEnviron() { - char **argv, **envp; - GetArgsAndEnv(&argv, &envp); - return envp; -} - -void ReExec() { - UNIMPLEMENTED(); -} - -} // namespace __sanitizer - -#endif // SANITIZER_OPENBSD diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.cpp b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.cpp deleted file mode 100644 index 1ca0375b8a5..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.cpp +++ /dev/null @@ -1,18 +0,0 @@ -//===-- sanitizer_persistent_allocator.cpp ----------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is shared between AddressSanitizer and ThreadSanitizer -// run-time libraries. -//===----------------------------------------------------------------------===// -#include "sanitizer_persistent_allocator.h" - -namespace __sanitizer { - -PersistentAllocator thePersistentAllocator; - -} // namespace __sanitizer diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h deleted file mode 100644 index de4fb6ebc3c..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h +++ /dev/null @@ -1,71 +0,0 @@ -//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// A fast memory allocator that does not support free() nor realloc(). -// All allocations are forever. -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_PERSISTENT_ALLOCATOR_H -#define SANITIZER_PERSISTENT_ALLOCATOR_H - -#include "sanitizer_internal_defs.h" -#include "sanitizer_mutex.h" -#include "sanitizer_atomic.h" -#include "sanitizer_common.h" - -namespace __sanitizer { - -class PersistentAllocator { - public: - void *alloc(uptr size); - - private: - void *tryAlloc(uptr size); - StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator. - atomic_uintptr_t region_pos; // Region allocator for Node's. - atomic_uintptr_t region_end; -}; - -inline void *PersistentAllocator::tryAlloc(uptr size) { - // Optimisic lock-free allocation, essentially try to bump the region ptr. - for (;;) { - uptr cmp = atomic_load(®ion_pos, memory_order_acquire); - uptr end = atomic_load(®ion_end, memory_order_acquire); - if (cmp == 0 || cmp + size > end) return nullptr; - if (atomic_compare_exchange_weak(®ion_pos, &cmp, cmp + size, - memory_order_acquire)) - return (void *)cmp; - } -} - -inline void *PersistentAllocator::alloc(uptr size) { - // First, try to allocate optimisitically. - void *s = tryAlloc(size); - if (s) return s; - // If failed, lock, retry and alloc new superblock. - SpinMutexLock l(&mtx); - for (;;) { - s = tryAlloc(size); - if (s) return s; - atomic_store(®ion_pos, 0, memory_order_relaxed); - uptr allocsz = 64 * 1024; - if (allocsz < size) allocsz = size; - uptr mem = (uptr)MmapOrDie(allocsz, "stack depot"); - atomic_store(®ion_end, mem + allocsz, memory_order_release); - atomic_store(®ion_pos, mem, memory_order_release); - } -} - -extern PersistentAllocator thePersistentAllocator; -inline void *PersistentAlloc(uptr sz) { - return thePersistentAllocator.alloc(sz); -} - -} // namespace __sanitizer - -#endif // SANITIZER_PERSISTENT_ALLOCATOR_H diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_platform.h index 64dcaaded83..b56bd491365 100644 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_platform.h +++ b/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_platform.h @@ -13,9 +13,8 @@ #define SANITIZER_PLATFORM_H #if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \ - !defined(__OpenBSD__) && \ !defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) && \ - !(defined(__sun__) && defined(__svr4__)) + !(defined(__sun__) && defined(__svr4__)) && !defined(__OpenBSD__) # error "This operating system is not supported" #endif @@ -23,109 +22,129 @@ // function declarations into a .S file which doesn't compile. // https://crbug.com/1162741 #if __has_include(<features.h>) && !defined(__ANDROID__) -#include <features.h> +# include <features.h> #endif #if defined(__linux__) -# define SANITIZER_LINUX 1 +# define SANITIZER_LINUX 1 #else -# define SANITIZER_LINUX 0 +# define SANITIZER_LINUX 0 #endif #if defined(__GLIBC__) -# define SANITIZER_GLIBC 1 +# define SANITIZER_GLIBC 1 #else -# define SANITIZER_GLIBC 0 +# define SANITIZER_GLIBC 0 #endif #if defined(__FreeBSD__) -# define SANITIZER_FREEBSD 1 +# define SANITIZER_FREEBSD 1 #else -# define SANITIZER_FREEBSD 0 +# define SANITIZER_FREEBSD 0 #endif #if defined(__NetBSD__) -# define SANITIZER_NETBSD 1 +# define SANITIZER_NETBSD 1 #else -# define SANITIZER_NETBSD 0 -#endif - -#if defined(__OpenBSD__) -# define SANITIZER_OPENBSD 1 -#else -# define SANITIZER_OPENBSD 0 +# define SANITIZER_NETBSD 0 #endif #if defined(__sun__) && defined(__svr4__) -# define SANITIZER_SOLARIS 1 -#else -# define SANITIZER_SOLARIS 0 -#endif - -#if defined(__APPLE__) -# define SANITIZER_MAC 1 -# include <TargetConditionals.h> -# if TARGET_OS_OSX -# define SANITIZER_OSX 1 -# else -# define SANITIZER_OSX 0 -# endif -# if TARGET_OS_IPHONE -# define SANITIZER_IOS 1 -# else -# define SANITIZER_IOS 0 -# endif -# if TARGET_OS_SIMULATOR -# define SANITIZER_IOSSIM 1 -# else -# define SANITIZER_IOSSIM 0 -# endif +# define SANITIZER_SOLARIS 1 #else -# define SANITIZER_MAC 0 -# define SANITIZER_IOS 0 -# define SANITIZER_IOSSIM 0 -# define SANITIZER_OSX 0 +# define SANITIZER_SOLARIS 0 #endif -#if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_WATCH -# define SANITIZER_WATCHOS 1 +#if defined(__OpenBSD__) +# define SANITIZER_OPENBSD 1 #else -# define SANITIZER_WATCHOS 0 +# define SANITIZER_OPENBSD 0 #endif -#if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_TV -# define SANITIZER_TVOS 1 +// - SANITIZER_APPLE: all Apple code +// - TARGET_OS_OSX: macOS +// - SANITIZER_IOS: devices (iOS and iOS-like) +// - SANITIZER_WATCHOS +// - SANITIZER_TVOS +// - SANITIZER_IOSSIM: simulators (iOS and iOS-like) +// - SANITIZER_DRIVERKIT +#if defined(__APPLE__) +# define SANITIZER_APPLE 1 +# include <TargetConditionals.h> +# if TARGET_OS_OSX +# define SANITIZER_OSX 1 +# else +# define SANITIZER_OSX 0 +# endif +# if TARGET_OS_IPHONE +# define SANITIZER_IOS 1 +# else +# define SANITIZER_IOS 0 +# endif +# if TARGET_OS_WATCH +# define SANITIZER_WATCHOS 1 +# else +# define SANITIZER_WATCHOS 0 +# endif +# if TARGET_OS_TV +# define SANITIZER_TVOS 1 +# else +# define SANITIZER_TVOS 0 +# endif +# if TARGET_OS_SIMULATOR +# define SANITIZER_IOSSIM 1 +# else +# define SANITIZER_IOSSIM 0 +# endif +# if defined(TARGET_OS_DRIVERKIT) && TARGET_OS_DRIVERKIT +# define SANITIZER_DRIVERKIT 1 +# else +# define SANITIZER_DRIVERKIT 0 +# endif #else -# define SANITIZER_TVOS 0 +# define SANITIZER_APPLE 0 +# define SANITIZER_OSX 0 +# define SANITIZER_IOS 0 +# define SANITIZER_WATCHOS 0 +# define SANITIZER_TVOS 0 +# define SANITIZER_IOSSIM 0 +# define SANITIZER_DRIVERKIT 0 #endif #if defined(_WIN32) -# define SANITIZER_WINDOWS 1 +# define SANITIZER_WINDOWS 1 #else -# define SANITIZER_WINDOWS 0 +# define SANITIZER_WINDOWS 0 #endif #if defined(_WIN64) -# define SANITIZER_WINDOWS64 1 +# define SANITIZER_WINDOWS64 1 #else -# define SANITIZER_WINDOWS64 0 +# define SANITIZER_WINDOWS64 0 #endif #if defined(__ANDROID__) -# define SANITIZER_ANDROID 1 +# define SANITIZER_ANDROID 1 #else -# define SANITIZER_ANDROID 0 +# define SANITIZER_ANDROID 0 #endif #if defined(__Fuchsia__) -# define SANITIZER_FUCHSIA 1 +# define SANITIZER_FUCHSIA 1 +#else +# define SANITIZER_FUCHSIA 0 +#endif + +// Assume linux that is not glibc or android is musl libc. +#if SANITIZER_LINUX && !SANITIZER_GLIBC && !SANITIZER_ANDROID +# define SANITIZER_MUSL 1 #else -# define SANITIZER_FUCHSIA 0 +# define SANITIZER_MUSL 0 #endif -#define SANITIZER_POSIX \ - (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || \ - SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_SOLARIS) +#define SANITIZER_POSIX \ + (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_APPLE || \ + SANITIZER_NETBSD || SANITIZER_SOLARIS || SANITIZER_OPENBSD) #if __LP64__ || defined(_WIN64) # define SANITIZER_WORDSIZE 64 @@ -134,58 +153,79 @@ #endif #if SANITIZER_WORDSIZE == 64 -# define FIRST_32_SECOND_64(a, b) (b) +# define FIRST_32_SECOND_64(a, b) (b) #else -# define FIRST_32_SECOND_64(a, b) (a) +# define FIRST_32_SECOND_64(a, b) (a) #endif #if defined(__x86_64__) && !defined(_LP64) -# define SANITIZER_X32 1 +# define SANITIZER_X32 1 +#else +# define SANITIZER_X32 0 +#endif + +#if defined(__x86_64__) || defined(_M_X64) +# define SANITIZER_X64 1 #else -# define SANITIZER_X32 0 +# define SANITIZER_X64 0 #endif #if defined(__i386__) || defined(_M_IX86) -# define SANITIZER_I386 1 +# define SANITIZER_I386 1 #else -# define SANITIZER_I386 0 +# define SANITIZER_I386 0 #endif #if defined(__mips__) -# define SANITIZER_MIPS 1 -# if defined(__mips64) +# define SANITIZER_MIPS 1 +# if defined(__mips64) && _MIPS_SIM == _ABI64 +# define SANITIZER_MIPS32 0 +# define SANITIZER_MIPS64 1 +# else +# define SANITIZER_MIPS32 1 +# define SANITIZER_MIPS64 0 +# endif +#else +# define SANITIZER_MIPS 0 # define SANITIZER_MIPS32 0 -# define SANITIZER_MIPS64 1 -# else -# define SANITIZER_MIPS32 1 # define SANITIZER_MIPS64 0 -# endif -#else -# define SANITIZER_MIPS 0 -# define SANITIZER_MIPS32 0 -# define SANITIZER_MIPS64 0 #endif #if defined(__s390__) -# define SANITIZER_S390 1 -# if defined(__s390x__) +# define SANITIZER_S390 1 +# if defined(__s390x__) +# define SANITIZER_S390_31 0 +# define SANITIZER_S390_64 1 +# else +# define SANITIZER_S390_31 1 +# define SANITIZER_S390_64 0 +# endif +#else +# define SANITIZER_S390 0 # define SANITIZER_S390_31 0 -# define SANITIZER_S390_64 1 -# else -# define SANITIZER_S390_31 1 # define SANITIZER_S390_64 0 -# endif +#endif + +#if defined(__sparc__) +# define SANITIZER_SPARC 1 +# if defined(__arch64__) +# define SANITIZER_SPARC32 0 +# define SANITIZER_SPARC64 1 +# else +# define SANITIZER_SPARC32 1 +# define SANITIZER_SPARC64 0 +# endif #else -# define SANITIZER_S390 0 -# define SANITIZER_S390_31 0 -# define SANITIZER_S390_64 0 +# define SANITIZER_SPARC 0 +# define SANITIZER_SPARC32 0 +# define SANITIZER_SPARC64 0 #endif #if defined(__powerpc__) -# define SANITIZER_PPC 1 -# if defined(__powerpc64__) -# define SANITIZER_PPC32 0 -# define SANITIZER_PPC64 1 +# define SANITIZER_PPC 1 +# if defined(__powerpc64__) +# define SANITIZER_PPC32 0 +# define SANITIZER_PPC64 1 // 64-bit PPC has two ABIs (v1 and v2). The old powerpc64 target is // big-endian, and uses v1 ABI (known for its function descriptors), // while the new powerpc64le target is little-endian and uses v2. @@ -193,106 +233,109 @@ // (eg. big-endian v2), but you won't find such combinations in the wild // (it'd require bootstrapping a whole system, which would be quite painful // - there's no target triple for that). LLVM doesn't support them either. -# if _CALL_ELF == 2 -# define SANITIZER_PPC64V1 0 -# define SANITIZER_PPC64V2 1 +# if _CALL_ELF == 2 +# define SANITIZER_PPC64V1 0 +# define SANITIZER_PPC64V2 1 +# else +# define SANITIZER_PPC64V1 1 +# define SANITIZER_PPC64V2 0 +# endif # else -# define SANITIZER_PPC64V1 1 -# define SANITIZER_PPC64V2 0 +# define SANITIZER_PPC32 1 +# define SANITIZER_PPC64 0 +# define SANITIZER_PPC64V1 0 +# define SANITIZER_PPC64V2 0 # endif -# else -# define SANITIZER_PPC32 1 +#else +# define SANITIZER_PPC 0 +# define SANITIZER_PPC32 0 # define SANITIZER_PPC64 0 # define SANITIZER_PPC64V1 0 # define SANITIZER_PPC64V2 0 -# endif +#endif + +#if defined(__arm__) || defined(_M_ARM) +# define SANITIZER_ARM 1 #else -# define SANITIZER_PPC 0 -# define SANITIZER_PPC32 0 -# define SANITIZER_PPC64 0 -# define SANITIZER_PPC64V1 0 -# define SANITIZER_PPC64V2 0 +# define SANITIZER_ARM 0 #endif -#if defined(__arm__) -# define SANITIZER_ARM 1 +#if defined(__aarch64__) || defined(_M_ARM64) +# define SANITIZER_ARM64 1 #else -# define SANITIZER_ARM 0 +# define SANITIZER_ARM64 0 #endif #if SANITIZER_SOLARIS && SANITIZER_WORDSIZE == 32 -# define SANITIZER_SOLARIS32 1 +# define SANITIZER_SOLARIS32 1 #else -# define SANITIZER_SOLARIS32 0 +# define SANITIZER_SOLARIS32 0 #endif #if defined(__riscv) && (__riscv_xlen == 64) -#define SANITIZER_RISCV64 1 +# define SANITIZER_RISCV64 1 +#else +# define SANITIZER_RISCV64 0 +#endif + +#if defined(__loongarch_lp64) +# define SANITIZER_LOONGARCH64 1 #else -#define SANITIZER_RISCV64 0 +# define SANITIZER_LOONGARCH64 0 #endif // By default we allow to use SizeClassAllocator64 on 64-bit platform. -// But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64 -// does not work well and we need to fallback to SizeClassAllocator32. +// But in some cases SizeClassAllocator64 does not work well and we need to +// fallback to SizeClassAllocator32. // For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or // change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here. #ifndef SANITIZER_CAN_USE_ALLOCATOR64 -# if (SANITIZER_ANDROID && defined(__aarch64__)) || SANITIZER_FUCHSIA -# define SANITIZER_CAN_USE_ALLOCATOR64 1 -# elif defined(__mips64) || defined(__aarch64__) -# define SANITIZER_CAN_USE_ALLOCATOR64 0 -# else -# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64) -# endif +# if SANITIZER_RISCV64 || SANITIZER_IOS +# define SANITIZER_CAN_USE_ALLOCATOR64 0 +# elif defined(__mips64) || defined(__hexagon__) +# define SANITIZER_CAN_USE_ALLOCATOR64 0 +# else +# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64) +# endif #endif // The range of addresses which can be returned my mmap. // FIXME: this value should be different on different platforms. Larger values // will still work but will consume more memory for TwoLevelByteMap. #if defined(__mips__) -#if SANITIZER_GO && defined(__mips64) -#define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) -#else -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40) -#endif +# if SANITIZER_GO && defined(__mips64) +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +# else +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40) +# endif #elif SANITIZER_RISCV64 -#define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 38) +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 38) #elif defined(__aarch64__) -# if SANITIZER_MAC -# if SANITIZER_OSX || SANITIZER_IOSSIM -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +# if SANITIZER_APPLE +# if SANITIZER_OSX || SANITIZER_IOSSIM +# define SANITIZER_MMAP_RANGE_SIZE \ + FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +# else +// Darwin iOS/ARM64 has a 36-bit VMA, 64GiB VM +# define SANITIZER_MMAP_RANGE_SIZE \ + FIRST_32_SECOND_64(1ULL << 32, 1ULL << 36) +# endif # else - // Darwin iOS/ARM64 has a 36-bit VMA, 64GiB VM -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 36) +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48) # endif -# else -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48) -# endif #elif defined(__sparc__) -#define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 52) +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 52) #else -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) #endif // Whether the addresses are sign-extended from the VMA range to the word. // The SPARC64 Linux port implements this to split the VMA space into two // non-contiguous halves with a huge hole in the middle. #if defined(__sparc__) && SANITIZER_WORDSIZE == 64 -#define SANITIZER_SIGN_EXTENDED_ADDRESSES 1 +# define SANITIZER_SIGN_EXTENDED_ADDRESSES 1 #else -#define SANITIZER_SIGN_EXTENDED_ADDRESSES 0 -#endif - -// The AArch64 and RISC-V linux ports use the canonical syscall set as -// mandated by the upstream linux community for all new ports. Other ports -// may still use legacy syscalls. -#ifndef SANITIZER_USES_CANONICAL_LINUX_SYSCALLS -# if (defined(__aarch64__) || defined(__riscv)) && SANITIZER_LINUX -# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 1 -# else -# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 0 -# endif +# define SANITIZER_SIGN_EXTENDED_ADDRESSES 0 #endif // udi16 syscalls can only be used when the following conditions are @@ -303,15 +346,15 @@ // Since we don't want to include libc headers here, we check the // target only. #if defined(__arm__) || SANITIZER_X32 || defined(__sparc__) -#define SANITIZER_USES_UID16_SYSCALLS 1 +# define SANITIZER_USES_UID16_SYSCALLS 1 #else -#define SANITIZER_USES_UID16_SYSCALLS 0 +# define SANITIZER_USES_UID16_SYSCALLS 0 #endif #if defined(__mips__) -# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10) +# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10) #else -# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) +# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) #endif /// \macro MSC_PREREQ @@ -320,15 +363,15 @@ /// * 1800: Microsoft Visual Studio 2013 / 12.0 /// * 1900: Microsoft Visual Studio 2015 / 14.0 #ifdef _MSC_VER -# define MSC_PREREQ(version) (_MSC_VER >= (version)) +# define MSC_PREREQ(version) (_MSC_VER >= (version)) #else -# define MSC_PREREQ(version) 0 +# define MSC_PREREQ(version) 0 #endif -#if SANITIZER_MAC && !(defined(__arm64__) && SANITIZER_IOS) -# define SANITIZER_NON_UNIQUE_TYPEINFO 0 +#if SANITIZER_APPLE && defined(__x86_64__) +# define SANITIZER_NON_UNIQUE_TYPEINFO 0 #else -# define SANITIZER_NON_UNIQUE_TYPEINFO 1 +# define SANITIZER_NON_UNIQUE_TYPEINFO 1 #endif // On linux, some architectures had an ABI transition from 64-bit long double @@ -336,11 +379,11 @@ // involving long doubles come in two versions, and we need to pass the // correct one to dlvsym when intercepting them. #if SANITIZER_LINUX && (SANITIZER_S390 || SANITIZER_PPC32 || SANITIZER_PPC64V1) -#define SANITIZER_NLDBL_VERSION "GLIBC_2.4" +# define SANITIZER_NLDBL_VERSION "GLIBC_2.4" #endif #if SANITIZER_GO == 0 -# define SANITIZER_GO 0 +# define SANITIZER_GO 0 #endif // On PowerPC and ARM Thumb, calling pthread_exit() causes LSan to detect leaks. @@ -348,40 +391,64 @@ // dlopen mallocs "libgcc_s.so" string which confuses LSan, it fails to realize // that this allocation happens in dynamic linker and should be ignored. #if SANITIZER_PPC || defined(__thumb__) -# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 1 +# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 1 #else -# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 0 +# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 0 #endif -#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD || \ - SANITIZER_SOLARIS -# define SANITIZER_MADVISE_DONTNEED MADV_FREE +#if SANITIZER_FREEBSD || SANITIZER_APPLE || SANITIZER_NETBSD || SANITIZER_SOLARIS +# define SANITIZER_MADVISE_DONTNEED MADV_FREE #else -# define SANITIZER_MADVISE_DONTNEED MADV_DONTNEED +# define SANITIZER_MADVISE_DONTNEED MADV_DONTNEED #endif // Older gcc have issues aligning to a constexpr, and require an integer. // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56859 among others. #if defined(__powerpc__) || defined(__powerpc64__) -# define SANITIZER_CACHE_LINE_SIZE 128 +# define SANITIZER_CACHE_LINE_SIZE 128 #else -# define SANITIZER_CACHE_LINE_SIZE 64 +# define SANITIZER_CACHE_LINE_SIZE 64 #endif // Enable offline markup symbolizer for Fuchsia. #if SANITIZER_FUCHSIA # define SANITIZER_SYMBOLIZER_MARKUP 1 #else -#define SANITIZER_SYMBOLIZER_MARKUP 0 +# define SANITIZER_SYMBOLIZER_MARKUP 0 #endif // Enable ability to support sanitizer initialization that is // compatible with the sanitizer library being loaded via // `dlopen()`. -#if SANITIZER_MAC -#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1 +#if SANITIZER_APPLE +# define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1 +#else +# define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0 +#endif + +// SANITIZER_SUPPORTS_THREADLOCAL +// 1 - THREADLOCAL macro is supported by target +// 0 - THREADLOCAL macro is not supported by target +#ifndef __has_feature +// TODO: Support other compilers here +# define SANITIZER_SUPPORTS_THREADLOCAL 1 +#else +# if __has_feature(tls) +# define SANITIZER_SUPPORTS_THREADLOCAL 1 +# else +# define SANITIZER_SUPPORTS_THREADLOCAL 0 +# endif +#endif + +#if defined(__thumb__) && defined(__linux__) +// Workaround for +// https://lab.llvm.org/buildbot/#/builders/clang-thumbv7-full-2stage +// or +// https://lab.llvm.org/staging/#/builders/clang-thumbv7-full-2stage +// It fails *rss_limit_mb_test* without meaningful errors. +# define SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL 1 #else -#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0 +# define SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL 0 #endif -#endif // SANITIZER_PLATFORM_H +#endif // SANITIZER_PLATFORM_H diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh b/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh deleted file mode 100755 index 4a2febab461..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/sh - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -if [ "${COMPILER_RT}" = "" ]; then - COMPILER_RT=$(readlink -f $SCRIPT_DIR/../../..) -fi - -# python tools setup -CPPLINT=${SCRIPT_DIR}/cpplint.py -LITLINT=${SCRIPT_DIR}/litlint.py -if [ "${PYTHON_EXECUTABLE}" != "" ]; then - CPPLINT="${PYTHON_EXECUTABLE} ${CPPLINT}" - LITLINT="${PYTHON_EXECUTABLE} ${LITLINT}" -fi - -# Filters -# TODO: remove some of these filters -COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ --build/namespaces,-build/c++11,-runtime/int - -COMMON_LIT_TEST_LINT_FILTER=-whitespace/indent,-whitespace/line_length,-runtime/arrays - -ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} -ASAN_TEST_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/printf,-runtime/threadsafe_fn -ASAN_LIT_TEST_LINT_FILTER=${ASAN_TEST_LINT_FILTER},${COMMON_LIT_TEST_LINT_FILTER} - -TSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-readability/braces -TSAN_TEST_LINT_FILTER=${TSAN_RTL_LINT_FILTER},-runtime/threadsafe_fn -TSAN_LIT_TEST_LINT_FILTER=${TSAN_TEST_LINT_FILTER},${COMMON_LIT_TEST_LINT_FILTER} - -MSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} - -LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} -LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},${COMMON_LIT_TEST_LINT_FILTER} - -DFSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} -SCUDO_RTL_LINT_FILTER=${COMMON_LINT_FILTER} - -COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER} - -SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER} - -MKTEMP_DIR=$(mktemp -qd /tmp/check_lint.XXXXXXXXXX) -MKTEMP="mktemp -q ${MKTEMP_DIR}/tmp.XXXXXXXXXX" -cleanup() { - rm -rf $MKTEMP_DIR -} -trap cleanup EXIT - -EXITSTATUS=0 -ERROR_LOG=$(${MKTEMP}) - -run_lint() { - FILTER=$1 - shift - TASK_LOG=$(${MKTEMP}) - ${CPPLINT} --filter=${FILTER} "$@" > $TASK_LOG 2>&1 - if [ "$?" != "0" ]; then - cat $TASK_LOG | grep -v "Done processing" | grep -v "Total errors found" \ - | grep -v "Skipping input" >> $ERROR_LOG - fi - if [ "${SILENT}" != "1" ]; then - cat $TASK_LOG - fi - ${LITLINT} "$@" 2>>$ERROR_LOG -} - -LIT_TESTS=${COMPILER_RT}/test -# Headers -SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer -FUZZER_INCLUDES=${COMPILER_RT}/include/fuzzer -run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h \ - ${FUZZER_INCLUDES}/*.h & - -# Sanitizer_common -COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common -run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.cpp \ - ${COMMON_RTL}/*.h \ - ${COMMON_RTL}/tests/*.cpp & - -# Interception -INTERCEPTION=${COMPILER_RT}/lib/interception -run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.cpp \ - ${INTERCEPTION}/*.h & - -# ASan -ASAN_RTL=${COMPILER_RT}/lib/asan -run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.cpp \ - ${ASAN_RTL}/*.h & -run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.cpp \ - ${ASAN_RTL}/tests/*.h & -run_lint ${ASAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/asan/*/*.cpp & - -# TSan -TSAN_RTL=${COMPILER_RT}/lib/tsan -run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.cpp \ - ${TSAN_RTL}/rtl/*.h & -run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.cpp \ - ${TSAN_RTL}/tests/rtl/*.h \ - ${TSAN_RTL}/tests/unit/*.cpp & -run_lint ${TSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/tsan/*.cpp & - -# MSan -MSAN_RTL=${COMPILER_RT}/lib/msan -run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.cpp \ - ${MSAN_RTL}/*.h & - -# LSan -LSAN_RTL=${COMPILER_RT}/lib/lsan -run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.cpp \ - ${LSAN_RTL}/*.h & -run_lint ${LSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/lsan/*/*.cpp & - -# DFSan -DFSAN_RTL=${COMPILER_RT}/lib/dfsan -run_lint ${DFSAN_RTL_LINT_FILTER} ${DFSAN_RTL}/*.cpp \ - ${DFSAN_RTL}/*.h & -${DFSAN_RTL}/scripts/check_custom_wrappers.sh >> $ERROR_LOG - -# Scudo -SCUDO_RTL=${COMPILER_RT}/lib/scudo -run_lint ${SCUDO_RTL_LINT_FILTER} ${SCUDO_RTL}/*.cpp \ - ${SCUDO_RTL}/*.h & - -# Misc files -( -rsync -a --prune-empty-dirs --exclude='*/profile/*' --exclude='*/builtins/*' --exclude='*/xray/*' --include='*/' --include='*.inc' --exclude='*' "${COMPILER_RT}/" "${MKTEMP_DIR}/" -find ${MKTEMP_DIR} -type f -name '*.inc' -exec mv {} {}.cpp \; -( ERROR_LOG=${ERROR_LOG}.inc run_lint ${COMMON_RTL_INC_LINT_FILTER} $(find ${MKTEMP_DIR} -type f -name '*.inc.cpp') ) -sed "s|${MKTEMP_DIR}|${COMPILER_RT}|g" ${ERROR_LOG}.inc | sed "s|.inc.cpp|.inc|g" >> ${ERROR_LOG} -) & - -wait - -if [ -s $ERROR_LOG ]; then - cat $ERROR_LOG - exit 1 -fi - -exit 0 diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/cpplint.py b/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/cpplint.py deleted file mode 100755 index 65baa6cc160..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/cpplint.py +++ /dev/null @@ -1,6244 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata -import sysconfig - -try: - xrange # Python 2 -except NameError: - xrange = range # Python 3 - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] [--root=subdir] - [--linelength=digits] [--headers=x,y,...] - [--quiet] - <file> [file] ... - - The style guidelines this tries to follow are those in - https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the - extensions with the --extensions flag. - - Flags: - - output=vs7 - By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - - quiet - Don't print anything if no errors are found. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuming that top/src/.git exists (and cwd=top/src), the header guard - CPP variables for top/src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ - --root=.. => SRC_CHROME_BROWSER_UI_BROWSER_H_ - - linelength=digits - This is the allowed line length for the project. The default value is - 80 characters. - - Examples: - --linelength=120 - - extensions=extension,extension,... - The allowed file extensions that cpplint will check - - Examples: - --extensions=hpp,cpp - - headers=x,y,... - The header extensions that cpplint will treat as .h in checks. Values are - automatically added to --extensions list. - - Examples: - --headers=hpp,hxx - --headers=hpp - - cpplint.py supports per-directory configurations specified in CPPLINT.cfg - files. CPPLINT.cfg file can contain a number of key=value pairs. - Currently the following options are supported: - - set noparent - filter=+filter1,-filter2,... - exclude_files=regex - linelength=80 - root=subdir - headers=x,y,... - - "set noparent" option prevents cpplint from traversing directory tree - upwards looking for more .cfg files in parent directories. This option - is usually placed in the top-level project directory. - - The "filter" option is similar in function to --filter flag. It specifies - message filters in addition to the |_DEFAULT_FILTERS| and those specified - through --filter command-line flag. - - "exclude_files" allows to specify a regular expression to be matched against - a file name. If the expression matches, the file is skipped and not run - through liner. - - "linelength" allows to specify the allowed line length for the project. - - The "root" option is similar in function to the --root flag (see example - above). Paths are relative to the directory of the CPPLINT.cfg. - - The "headers" option is similar in function to the --headers flag - (see example above). - - CPPLINT.cfg has an effect on files in the same directory and all - sub-directories, unless overridden by a nested configuration file. - - Example file: - filter=-build/include_order,+build/include_alpha - exclude_files=.*\.cc - - The above example disables build/include_order warning and enables - build/include_alpha as well as excludes all .cc from being - processed by linter, in the current directory (where the .cfg - file is located) and all sub-directories. -""" - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -_ERROR_CATEGORIES = [ - 'build/class', - 'build/c++11', - 'build/c++14', - 'build/c++tr1', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/inheritance', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/nul', - 'readability/strings', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/indentation_namespace', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/string', - 'runtime/threadsafe_fn', - 'runtime/vlog', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_conditional_body', - 'whitespace/empty_if_body', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo', - ] - -# These error categories are no longer enforced by cpplint, but for backwards- -# compatibility they may still appear in NOLINT comments. -_LEGACY_ERROR_CATEGORIES = [ - 'readability/streams', - 'readability/function', - ] - -# The default state of the category filter. This is overridden by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# The default list of categories suppressed for C (not C++) files. -_DEFAULT_C_SUPPRESSED_CATEGORIES = [ - 'readability/casting', - ] - -# The default list of categories suppressed for Linux Kernel files. -_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [ - 'whitespace/tab', - ] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# C++ headers -_CPP_HEADERS = frozenset([ - # Legacy - 'algobase.h', - 'algo.h', - 'alloc.h', - 'builtinbuf.h', - 'bvector.h', - 'complex.h', - 'defalloc.h', - 'deque.h', - 'editbuf.h', - 'fstream.h', - 'function.h', - 'hash_map', - 'hash_map.h', - 'hash_set', - 'hash_set.h', - 'hashtable.h', - 'heap.h', - 'indstream.h', - 'iomanip.h', - 'iostream.h', - 'istream.h', - 'iterator.h', - 'list.h', - 'map.h', - 'multimap.h', - 'multiset.h', - 'ostream.h', - 'pair.h', - 'parsestream.h', - 'pfstream.h', - 'procbuf.h', - 'pthread_alloc', - 'pthread_alloc.h', - 'rope', - 'rope.h', - 'ropeimpl.h', - 'set.h', - 'slist', - 'slist.h', - 'stack.h', - 'stdiostream.h', - 'stl_alloc.h', - 'stl_relops.h', - 'streambuf.h', - 'stream.h', - 'strfile.h', - 'strstream.h', - 'tempbuf.h', - 'tree.h', - 'type_traits.h', - 'vector.h', - # 17.6.1.2 C++ library headers - 'algorithm', - 'array', - 'atomic', - 'bitset', - 'chrono', - 'codecvt', - 'complex', - 'condition_variable', - 'deque', - 'exception', - 'forward_list', - 'fstream', - 'functional', - 'future', - 'initializer_list', - 'iomanip', - 'ios', - 'iosfwd', - 'iostream', - 'istream', - 'iterator', - 'limits', - 'list', - 'locale', - 'map', - 'memory', - 'mutex', - 'new', - 'numeric', - 'ostream', - 'queue', - 'random', - 'ratio', - 'regex', - 'scoped_allocator', - 'set', - 'sstream', - 'stack', - 'stdexcept', - 'streambuf', - 'string', - 'strstream', - 'system_error', - 'thread', - 'tuple', - 'typeindex', - 'typeinfo', - 'type_traits', - 'unordered_map', - 'unordered_set', - 'utility', - 'valarray', - 'vector', - # 17.6.1.2 C++ headers for C library facilities - 'cassert', - 'ccomplex', - 'cctype', - 'cerrno', - 'cfenv', - 'cfloat', - 'cinttypes', - 'ciso646', - 'climits', - 'clocale', - 'cmath', - 'csetjmp', - 'csignal', - 'cstdalign', - 'cstdarg', - 'cstdbool', - 'cstddef', - 'cstdint', - 'cstdio', - 'cstdlib', - 'cstring', - 'ctgmath', - 'ctime', - 'cuchar', - 'cwchar', - 'cwctype', - ]) - -# Type names -_TYPES = re.compile( - r'^(?:' - # [dcl.type.simple] - r'(char(16_t|32_t)?)|wchar_t|' - r'bool|short|int|long|signed|unsigned|float|double|' - # [support.types] - r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|' - # [cstdint.syn] - r'(u?int(_fast|_least)?(8|16|32|64)_t)|' - r'(u?int(max|ptr)_t)|' - r')$') - - -# These headers are excluded from [build/include] and [build/include_order] -# checks: -# - Anything not following google file name conventions (containing an -# uppercase character, such as Python.h or nsStringAPI.h, for example). -# - Lua headers. -_THIRD_PARTY_HEADERS_PATTERN = re.compile( - r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') - -# Pattern for matching FileInfo.BaseName() against test file name -_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$' - -# Pattern that matches only complete whitespace, possibly across multiple lines. -_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) - -# Assertion macros. These are defined in base/logging.h and -# testing/base/public/gunit.h. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE', 'ASSERT_TRUE', - 'EXPECT_FALSE', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments and multi-line strings -# but those have always been troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - -# Match strings that indicate we're working on a C (not C++) file. -_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' - r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))') - -# Match string that indicates we're working on a Linux Kernel file. -_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') - -_regexp_compile_cache = {} - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None -_root_debug = False - -# The allowed line length of files. -# This is set by --linelength flag. -_line_length = 80 - -# The allowed extensions for file names -# This is set by --extensions flag. -_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) - -# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc. -# This is set by --headers flag. -_hpp_headers = set(['h']) - -# {str, bool}: a map from error categories to booleans which indicate if the -# category should be suppressed for every line. -_global_error_suppressions = {} - -def ProcessHppHeadersOption(val): - global _hpp_headers - try: - _hpp_headers = set(val.split(',')) - # Automatically append to extensions list so it does not have to be set 2 times - _valid_extensions.update(_hpp_headers) - except ValueError: - PrintUsage('Header extensions must be comma separated list.') - -def IsHeaderExtension(file_extension): - return file_extension in _hpp_headers - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of line error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) - if matched: - if matched.group(1): - suppressed_line = linenum + 1 - else: - suppressed_line = linenum - category = matched.group(2) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(suppressed_line) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(suppressed_line) - elif category not in _LEGACY_ERROR_CATEGORIES: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ProcessGlobalSuppresions(lines): - """Updates the list of global error suppressions. - - Parses any lint directives in the file that have global effect. - - Args: - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - """ - for line in lines: - if _SEARCH_C_FILE.search(line): - for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True - if _SEARCH_KERNEL_FILE.search(line): - for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True - - -def ResetNolintSuppressions(): - """Resets the set of NOLINT suppressions to empty.""" - _error_suppressions.clear() - _global_error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment or - global suppression. - """ - return (_global_error_suppressions.get(category, False) or - linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def ReplaceAll(pattern, rep, s): - """Replaces instances of pattern in a string with a replacement. - - The compiled regex is kept in a cache shared by Match and Search. - - Args: - pattern: regex pattern - rep: replacement text - s: search string - - Returns: - string with replacements made (or original string if no replacements) - """ - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].sub(rep, s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -def _IsSourceExtension(s): - """File extension (excluding dot) matches a source file extension.""" - return s in ('c', 'cc', 'cpp', 'cxx') - - -class _IncludeState(object): - """Tracks line numbers for includes, and the order in which includes appear. - - include_list contains list of lists of (header, line number) pairs. - It's a lists of lists rather than just one flat list to make it - easier to update across preprocessor boundaries. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - self.include_list = [[]] - self.ResetSection('') - - def FindHeader(self, header): - """Check if a header has already been included. - - Args: - header: header to check. - Returns: - Line number of previous occurrence, or -1 if the header has not - been seen before. - """ - for section_list in self.include_list: - for f in section_list: - if f[0] == header: - return f[1] - return -1 - - def ResetSection(self, directive): - """Reset section checking for preprocessor directive. - - Args: - directive: preprocessor directive (e.g. "if", "else"). - """ - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - # Update list of includes. Note that we never pop from the - # include list. - if directive in ('if', 'ifdef', 'ifndef'): - self.include_list.append([]) - elif directive in ('else', 'elif'): - self.include_list[-1] = [] - - def SetLastHeader(self, header_path): - self._last_header = header_path - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - header_path: Canonicalized header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - # If previous section is different from current section, _last_header will - # be reset to empty string, so it's always less than current header. - # - # If previous line was a blank line, assume that the headers are - # intentionally sorted the way they are. - if (self._last_header > header_path and - Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): - return False - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - # backup of filter list. Used to restore the state after each file. - self._filters_backup = self.filters[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - self.quiet = False # Suppress non-error messagess? - - # output format: - # "emacs" - format that emacs can parse (default) - # "vs7" - format that Microsoft Visual Studio 7 can parse - self.output_format = 'emacs' - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - def SetQuiet(self, quiet): - """Sets the module's quiet settings, and returns the previous setting.""" - last_quiet = self.quiet - self.quiet = quiet - return last_quiet - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - self.AddFilters(filters) - - def AddFilters(self, filters): - """ Adds more filters to the existing list of error-message filters. """ - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def BackupFilters(self): - """ Saves the current filter list to backup storage.""" - self._filters_backup = self.filters[:] - - def RestoreFilters(self): - """ Restores filters previously backed up.""" - self.filters = self._filters_backup[:] - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stdout.write('Total errors found: %d\n' % self.error_count) - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - -def _Quiet(): - """Return's the module's quiet setting.""" - return _cpplint_state.quiet - -def _SetQuiet(quiet): - """Set the module's quiet status, and return previous setting.""" - return _cpplint_state.SetQuiet(quiet) - - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - -def _AddFilters(filters): - """Adds more filter overrides. - - Unlike _SetFilters, this function does not reset the current list of filters - available. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.AddFilters(filters) - -def _BackupFilters(): - """ Saves the current filter list to backup storage.""" - _cpplint_state.BackupFilters() - -def _RestoreFilters(): - """ Restores filters previously backed up.""" - _cpplint_state.RestoreFilters() - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - if not self.in_a_function: - return - - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo(object): - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - """FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = current_dir = os.path.dirname(fullname) - while current_dir != os.path.dirname(current_dir): - if (os.path.exists(os.path.join(current_dir, ".git")) or - os.path.exists(os.path.join(current_dir, ".hg")) or - os.path.exists(os.path.join(current_dir, ".svn"))): - root_dir = current_dir - current_dir = os.path.dirname(current_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return _IsSourceExtension(self.Extension()[1:]) - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): error cpplint: [%s] %s [%d]\n' % ( - filename, linenum, category, message, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - - -# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Match a single C style comment on the same line. -_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' -# Matches multi-line C style comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + - _RE_PATTERN_C_COMMENTS + r'\s+|' + - r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + - _RE_PATTERN_C_COMMENTS + r')') - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def CleanseRawStrings(raw_lines): - """Removes C++11 raw strings from lines. - - Before: - static const char kData[] = R"( - multi-line string - )"; - - After: - static const char kData[] = "" - (replaced by blank line) - ""; - - Args: - raw_lines: list of raw lines. - - Returns: - list of lines with C++11 raw strings replaced by empty strings. - """ - - delimiter = None - lines_without_raw_strings = [] - for line in raw_lines: - if delimiter: - # Inside a raw string, look for the end - end = line.find(delimiter) - if end >= 0: - # Found the end of the string, match leading space for this - # line and resume copying the original lines, and also insert - # a "" on the last line. - leading_space = Match(r'^(\s*)\S', line) - line = leading_space.group(1) + '""' + line[end + len(delimiter):] - delimiter = None - else: - # Haven't found the end yet, append a blank line. - line = '""' - - # Look for beginning of a raw string, and replace them with - # empty strings. This is done in a loop to handle multiple raw - # strings on the same line. - while delimiter is None: - # Look for beginning of a raw string. - # See 2.14.15 [lex.string] for syntax. - # - # Once we have matched a raw string, we check the prefix of the - # line to make sure that the line is not part of a single line - # comment. It's done this way because we remove raw strings - # before removing comments as opposed to removing comments - # before removing raw strings. This is because there are some - # cpplint checks that requires the comments to be preserved, but - # we don't want to check comments that are inside raw strings. - matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) - if (matched and - not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', - matched.group(1))): - delimiter = ')' + matched.group(2) + '"' - - end = matched.group(3).find(delimiter) - if end >= 0: - # Raw string ended on same line - line = (matched.group(1) + '""' + - matched.group(3)[end + len(delimiter):]) - delimiter = None - else: - # Start of a multi-line raw string - line = matched.group(1) + '""' - else: - break - - lines_without_raw_strings.append(line) - - # TODO(unknown): if delimiter is not None here, we might want to - # emit a warning for unterminated string. - return lines_without_raw_strings - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '/**/' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 4 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments. - 2) lines member contains lines without comments. - 3) raw_lines member contains all the lines without processing. - 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw - strings removed. - All these members are of <type 'list'>, and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - self.lines_without_raw_strings = CleanseRawStrings(lines) - for linenum in range(len(self.lines_without_raw_strings)): - self.lines.append(CleanseComments( - self.lines_without_raw_strings[linenum])) - elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if _RE_PATTERN_INCLUDE.match(elided): - return elided - - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - - # Replace quoted strings and digit separators. Both single quotes - # and double quotes are processed in the same loop, otherwise - # nested quotes wouldn't work. - collapsed = '' - while True: - # Find the first quote character - match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) - if not match: - collapsed += elided - break - head, quote, tail = match.groups() - - if quote == '"': - # Collapse double quoted strings - second_quote = tail.find('"') - if second_quote >= 0: - collapsed += head + '""' - elided = tail[second_quote + 1:] - else: - # Unmatched double quote, don't bother processing the rest - # of the line since this is probably a multiline string. - collapsed += elided - break - else: - # Found single quote, check nearby text to eliminate digit separators. - # - # There is no special handling for floating point here, because - # the integer/fractional/exponent parts would all be parsed - # correctly as long as there are digits on both sides of the - # separator. So we are fine as long as we don't see something - # like "0.'3" (gcc 4.9.0 will not allow this literal). - if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): - match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) - collapsed += head + match_literal.group(1).replace("'", '') - elided = match_literal.group(2) - else: - second_quote = tail.find('\'') - if second_quote >= 0: - collapsed += head + "''" - elided = tail[second_quote + 1:] - else: - # Unmatched single quote - collapsed += elided - break - - return collapsed - - -def FindEndOfExpressionInLine(line, startpos, stack): - """Find the position just after the end of current parenthesized expression. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - stack: nesting stack at startpos. - - Returns: - On finding matching end: (index just after matching end, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at end of this line) - """ - for i in xrange(startpos, len(line)): - char = line[i] - if char in '([{': - # Found start of parenthesized expression, push to expression stack - stack.append(char) - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - if stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - elif i > 0 and Search(r'\boperator\s*$', line[0:i]): - # operator<, don't add to stack - continue - else: - # Tentative start of template argument list - stack.append('<') - elif char in ')]}': - # Found end of parenthesized expression. - # - # If we are currently expecting a matching '>', the pending '<' - # must have been an operator. Remove them from expression stack. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - if ((stack[-1] == '(' and char == ')') or - (stack[-1] == '[' and char == ']') or - (stack[-1] == '{' and char == '}')): - stack.pop() - if not stack: - return (i + 1, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == '>': - # Found potential end of template argument list. - - # Ignore "->" and operator functions - if (i > 0 and - (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): - continue - - # Pop the stack if there is a matching '<'. Otherwise, ignore - # this '>' since it must be an operator. - if stack: - if stack[-1] == '<': - stack.pop() - if not stack: - return (i + 1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '>', the matching '<' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - - # Did not find end of expression or unbalanced parentheses on this line - return (-1, stack) - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [ or <, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the - linenum/pos that correspond to the closing of the expression. - - TODO(unknown): cpplint spends a fair bit of time matching parentheses. - Ideally we would want to index all opening and closing parentheses once - and have CloseExpression be just a simple lookup, but due to preprocessor - tricks, this is not so easy. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): - return (line, clean_lines.NumLines(), -1) - - # Check first line - (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) - if end_pos > -1: - return (line, linenum, end_pos) - - # Continue scanning forward - while stack and linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) - if end_pos > -1: - return (line, linenum, end_pos) - - # Did not find end of expression before end of file, give up - return (line, clean_lines.NumLines(), -1) - - -def FindStartOfExpressionInLine(line, endpos, stack): - """Find position at the matching start of current expression. - - This is almost the reverse of FindEndOfExpressionInLine, but note - that the input position and returned position differs by 1. - - Args: - line: a CleansedLines line. - endpos: start searching at this position. - stack: nesting stack at endpos. - - Returns: - On finding matching start: (index at matching start, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at beginning of this line) - """ - i = endpos - while i >= 0: - char = line[i] - if char in ')]}': - # Found end of expression, push to expression stack - stack.append(char) - elif char == '>': - # Found potential end of template argument list. - # - # Ignore it if it's a "->" or ">=" or "operator>" - if (i > 0 and - (line[i - 1] == '-' or - Match(r'\s>=\s', line[i - 1:]) or - Search(r'\boperator\s*$', line[0:i]))): - i -= 1 - else: - stack.append('>') - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - i -= 1 - else: - # If there is a matching '>', we can pop the expression stack. - # Otherwise, ignore this '<' since it must be an operator. - if stack and stack[-1] == '>': - stack.pop() - if not stack: - return (i, None) - elif char in '([{': - # Found start of expression. - # - # If there are any unmatched '>' on the stack, they must be - # operators. Remove those. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - if ((char == '(' and stack[-1] == ')') or - (char == '[' and stack[-1] == ']') or - (char == '{' and stack[-1] == '}')): - stack.pop() - if not stack: - return (i, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '<', the matching '>' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - - i -= 1 - - return (-1, stack) - - -def ReverseCloseExpression(clean_lines, linenum, pos): - """If input points to ) or } or ] or >, finds the position that opens it. - - If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the - linenum/pos that correspond to the opening of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *at* the opening brace, or - (line, 0, -1) if we never find the matching opening brace. Note - we ignore strings and comments when matching; and the line we - return is the 'cleansed' line at linenum. - """ - line = clean_lines.elided[linenum] - if line[pos] not in ')}]>': - return (line, 0, -1) - - # Check last line - (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) - if start_pos > -1: - return (line, linenum, start_pos) - - # Continue scanning backward - while stack and linenum > 0: - linenum -= 1 - line = clean_lines.elided[linenum] - (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) - if start_pos > -1: - return (line, linenum, start_pos) - - # Did not find start of expression before beginning of file, give up - return (line, 0, -1) - - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] <Copyright Owner>"') - - -def GetIndentLevel(line): - """Return the number of leading spaces in line. - - Args: - line: A string to check. - - Returns: - An integer count of leading spaces, possibly zero. - """ - indent = Match(r'^( *)\S', line) - if indent: - return len(indent.group(1)) - else: - return 0 - -def PathSplitToList(path): - """Returns the path split into a list by the separator. - - Args: - path: An absolute or relative path (e.g. '/a/b/c/' or '../a') - - Returns: - A list of path components (e.g. ['a', 'b', 'c]). - """ - lst = [] - while True: - (head, tail) = os.path.split(path) - if head == path: # absolute paths end - lst.append(head) - break - if tail == path: # relative paths end - lst.append(tail) - break - - path = head - lst.append(tail) - - lst.reverse() - return lst - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - # Replace 'c++' with 'cpp'. - filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - - def FixupPathFromRoot(): - if _root_debug: - sys.stderr.write("\n_root fixup, _root = '%s', repository name = '%s'\n" - %(_root, fileinfo.RepositoryName())) - - # Process the file path with the --root flag if it was set. - if not _root: - if _root_debug: - sys.stderr.write("_root unspecified\n") - return file_path_from_root - - def StripListPrefix(lst, prefix): - # f(['x', 'y'], ['w, z']) -> None (not a valid prefix) - if lst[:len(prefix)] != prefix: - return None - # f(['a, 'b', 'c', 'd'], ['a', 'b']) -> ['c', 'd'] - return lst[(len(prefix)):] - - # root behavior: - # --root=subdir , lstrips subdir from the header guard - maybe_path = StripListPrefix(PathSplitToList(file_path_from_root), - PathSplitToList(_root)) - - if _root_debug: - sys.stderr.write(("_root lstrip (maybe_path=%s, file_path_from_root=%s," + - " _root=%s)\n") %(maybe_path, file_path_from_root, _root)) - - if maybe_path: - return os.path.join(*maybe_path) - - # --root=.. , will prepend the outer directory to the header guard - full_path = fileinfo.FullName() - root_abspath = os.path.abspath(_root) - - maybe_path = StripListPrefix(PathSplitToList(full_path), - PathSplitToList(root_abspath)) - - if _root_debug: - sys.stderr.write(("_root prepend (maybe_path=%s, full_path=%s, " + - "root_abspath=%s)\n") %(maybe_path, full_path, root_abspath)) - - if maybe_path: - return os.path.join(*maybe_path) - - if _root_debug: - sys.stderr.write("_root ignore, returning %s\n" %(file_path_from_root)) - - # --root=FAKE_DIR is ignored - return file_path_from_root - - file_path_from_root = FixupPathFromRoot() - return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, clean_lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - clean_lines: A CleansedLines instance containing the file. - error: The function to call with any errors found. - """ - - # Don't check for header guards if there are error suppression - # comments somewhere in this file. - # - # Because this is silencing a warning for a nonexistent line, we - # only support the very specific NOLINT(build/header_guard) syntax, - # and not the general NOLINT or NOLINT(*) syntax. - raw_lines = clean_lines.lines_without_raw_strings - for i in raw_lines: - if Search(r'//\s*NOLINT\(build/header_guard\)', i): - return - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = '' - ifndef_linenum = 0 - define = '' - endif = '' - endif_linenum = 0 - for linenum, line in enumerate(raw_lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef or not define or ifndef != define: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - # Check for "//" comments on endif line. - ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, - error) - match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) - if match: - if match.group(1) == '_': - # Issue low severity warning for deprecated double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif // %s"' % cppvar) - return - - # Didn't find the corresponding "//" comment. If this file does not - # contain any "//" comments at all, it could be that the compiler - # only wants "/**/" comments, look for those instead. - no_single_line_comments = True - for i in xrange(1, len(raw_lines) - 1): - line = raw_lines[i] - if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): - no_single_line_comments = False - break - - if no_single_line_comments: - match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) - if match: - if match.group(1) == '_': - # Low severity warning for double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif /* %s */"' % cppvar) - return - - # Didn't find anything - error(filename, endif_linenum, 'build/header_guard', 5, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckHeaderFileIncluded(filename, include_state, error): - """Logs an error if a .cc file does not include its header.""" - - # Do not check test files - fileinfo = FileInfo(filename) - if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): - return - - headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h' - if not os.path.exists(headerfile): - return - headername = FileInfo(headerfile).RepositoryName() - first_include = 0 - for section_list in include_state.include_list: - for f in section_list: - if headername in f[0] or f[0] in headername: - return - if not first_include: - first_include = f[1] - - error(filename, first_include, 'build/include', 5, - '%s should include its header file %s' % (fileinfo.RepositoryName(), - headername)) - - -def CheckForBadCharacters(filename, lines, error): - """Logs an error for each line containing bad characters. - - Two kinds of bad characters: - - 1. Unicode replacement characters: These indicate that either the file - contained invalid UTF-8 (likely) or Unicode replacement characters (which - it shouldn't). Note that it's possible for this to throw off line - numbering if the invalid UTF-8 occurred adjacent to a newline. - - 2. NUL bytes. These are problematic for some tools. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if u'\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - if '\0' in line: - error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. ' - 'Use C++11 raw strings or concatenation instead.') - - -# (non-threadsafe name, thread-safe alternative, validation pattern) -# -# The validation pattern is used to eliminate false positives such as: -# _rand(); // false positive due to substring match. -# ->rand(); // some member function rand(). -# ACMRandom rand(seed); // some variable named rand. -# ISAACRandom rand(); // another variable named rand. -# -# Basically we require the return value of these functions to be used -# in some expression context on the same line by matching on some -# operator before the function name. This eliminates constructors and -# member function calls. -_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' -_THREADING_LIST = ( - ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), - ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), - ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), - ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), - ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), - ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), - ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), - ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), - ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), - ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), - ('strtok(', 'strtok_r(', - _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), - ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: - # Additional pattern matching check to confirm that this is the - # function we are looking for - if Search(pattern, line): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_func + - '...) instead of ' + single_thread_func + - '...) for improved thread safety.') - - -def CheckVlogArguments(filename, clean_lines, linenum, error): - """Checks that VLOG() is only used for defining a logging level. - - For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and - VLOG(FATAL) are not. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): - error(filename, linenum, 'runtime/vlog', 5, - 'VLOG() should be used with numeric verbosity level. ' - 'Use LOG() if you want symbolic severity levels.') - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -def IsMacroDefinition(clean_lines, linenum): - if Search(r'^#define', clean_lines[linenum]): - return True - - if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): - return True - - return False - - -def IsForwardClassDeclaration(clean_lines, linenum): - return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, linenum, seen_open_brace): - self.starting_linenum = linenum - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - self.check_namespace_indentation = False - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def IsBlockInfo(self): - """Returns true if this block is a _BlockInfo. - - This is convenient for verifying that an object is an instance of - a _BlockInfo, but not an instance of any of the derived classes. - - Returns: - True for this class, False for derived classes. - """ - return self.__class__ == _BlockInfo - - -class _ExternCInfo(_BlockInfo): - """Stores information about an 'extern "C"' block.""" - - def __init__(self, linenum): - _BlockInfo.__init__(self, linenum, True) - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, linenum, False) - self.name = name - self.is_derived = False - self.check_namespace_indentation = True - if class_or_struct == 'struct': - self.access = 'public' - self.is_struct = True - else: - self.access = 'private' - self.is_struct = False - - # Remember initial indentation level for this class. Using raw_lines here - # instead of elided to account for leading comments. - self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - # If there is a DISALLOW macro, it should appear near the end of - # the class. - seen_last_thing_in_class = False - for i in xrange(linenum - 1, self.starting_linenum, -1): - match = Search( - r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + - self.name + r'\)', - clean_lines.elided[i]) - if match: - if seen_last_thing_in_class: - error(filename, i, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - break - - if not Match(r'^\s*$', clean_lines.elided[i]): - seen_last_thing_in_class = True - - # Check that closing brace is aligned with beginning of the class. - # Only do this if the closing brace is indented by only whitespaces. - # This means we will not check single-line class definitions. - indent = Match(r'^( *)\}', clean_lines.elided[linenum]) - if indent and len(indent.group(1)) != self.class_indent: - if self.is_struct: - parent = 'struct ' + self.name - else: - parent = 'class ' + self.name - error(filename, linenum, 'whitespace/indent', 3, - 'Closing brace should be aligned with beginning of %s' % parent) - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, linenum, False) - self.name = name or '' - self.check_namespace_indentation = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. - # - # We also accept stuff like "// end of namespace <name>." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. - if self.name: - # Named namespace - if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + - re.escape(self.name) + r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - # If "// namespace anonymous" or "// anonymous namespace (more text)", - # mention "// anonymous namespace" as an acceptable form - if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"' - ' or "// anonymous namespace"') - else: - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Top of the previous stack before each Update(). - # - # Because the nesting_stack is updated at the end of each line, we - # had to do some convoluted checks to find out what is the current - # scope at the beginning of the line. This check is simplified by - # saving the previous top of nesting stack. - # - # We could save the full stack, but we only need the top. Copying - # the full nesting stack would slow down cpplint by ~10%. - self.previous_stack_top = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def InExternC(self): - """Check if we are currently one level inside an 'extern "C"' block. - - Returns: - True if top of the stack is an extern block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ExternCInfo) - - def InClassDeclaration(self): - """Check if we are currently one level inside a class or struct declaration. - - Returns: - True if top of the stack is a class/struct, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ClassInfo) - - def InAsmBlock(self): - """Check if we are currently one level inside an inline ASM block. - - Returns: - True if the top of the stack is a block containing inline ASM. - """ - return self.stack and self.stack[-1].inline_asm != _NO_ASM - - def InTemplateArgumentList(self, clean_lines, linenum, pos): - """Check if current position is inside template argument list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: position just after the suspected template argument. - Returns: - True if (linenum, pos) is inside template arguments. - """ - while linenum < clean_lines.NumLines(): - # Find the earliest character that might indicate a template argument - line = clean_lines.elided[linenum] - match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) - if not match: - linenum += 1 - pos = 0 - continue - token = match.group(1) - pos += len(match.group(0)) - - # These things do not look like template argument list: - # class Suspect { - # class Suspect x; } - if token in ('{', '}', ';'): return False - - # These things look like template argument list: - # template <class Suspect> - # template <class Suspect = default_value> - # template <class Suspect[]> - # template <class Suspect...> - if token in ('>', '=', '[', ']', '.'): return True - - # Check if token is an unmatched '<'. - # If not, move on to the next character. - if token != '<': - pos += 1 - if pos >= len(line): - linenum += 1 - pos = 0 - continue - - # We can't be sure if we just find a single '<', and need to - # find the matching '>'. - (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) - if end_pos < 0: - # Not sure if template argument list or syntax error in file - return False - linenum = end_line - pos = end_pos - return False - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - # TODO(unknown): Update() is too long, but we will refactor later. - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remember top of the previous nesting stack. - # - # The stack is always pushed/popped and not modified in place, so - # we can just do a shallow copy instead of copy.deepcopy. Using - # deepcopy would slow down cpplint by ~28%. - if self.stack: - self.previous_stack_top = self.stack[-1] - else: - self.previous_stack_top = None - - # Update pp_stack - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - class_decl_match = Match( - r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' - r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' - r'(.*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - # We do not want to accept classes that are actually template arguments: - # template <class Ignore1, - # class Ignore2 = Default<Args>, - # template <Args> class Ignore3> - # void Function() {}; - # - # To avoid template argument cases, we scan forward and look for - # an unmatched '>'. If we see one, assume we are inside a - # template argument list. - end_declaration = len(class_decl_match.group(1)) - if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): - self.stack.append(_ClassInfo( - class_decl_match.group(3), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(4) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - classinfo = self.stack[-1] - access_match = Match( - r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' - r':(?:[^:]|$)', - line) - if access_match: - classinfo.access = access_match.group(2) - - # Check that access keywords are indented +1 space. Skip this - # check if the keywords are not preceded by whitespaces. - indent = access_match.group(1) - if (len(indent) != classinfo.class_indent + 1 and - Match(r'^\s*$', indent)): - if classinfo.is_struct: - parent = 'struct ' + classinfo.name - else: - parent = 'class ' + classinfo.name - slots = '' - if access_match.group(3): - slots = access_match.group(3) - error(filename, linenum, 'whitespace/indent', 3, - '%s%s: should be indented +1 space inside %s' % ( - access_match.group(2), slots, parent)) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - elif Match(r'^extern\s*"[^"]*"\s*\{', line): - self.stack.append(_ExternCInfo(linenum)) - else: - self.stack.append(_BlockInfo(linenum, True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckCompletedBlocks(self, filename, error): - """Checks that all classes and namespaces have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - elif isinstance(obj, _NamespaceInfo): - error(filename, obj.starting_linenum, 'build/namespaces', 5, - 'Failed to find complete declaration of namespace %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and <? operators, and their >?= and <?= cousins. - - Additionally, check for constructor/destructor style violations and reference - members, as it is very convenient to do so while checking for - gcc-2 compliance. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - """ - - # Remove comments from the line, but leave in strings for now. - line = clean_lines.lines[linenum] - - if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): - error(filename, linenum, 'runtime/printf_format', 3, - '%q in format strings is deprecated. Use %ll instead.') - - if Search(r'printf\s*\(.*".*%\d+\$', line): - error(filename, linenum, 'runtime/printf_format', 2, - '%N$ formats are unconventional. Try rewriting to avoid them.') - - # Remove escaped backslashes before looking for undefined escapes. - line = line.replace('\\\\', '') - - if Search(r'("|\').*\\(%|\[|\(|{)', line): - error(filename, linenum, 'build/printf_format', 3, - '%, [, (, and { are undefined character escapes. Unescape them.') - - # For the rest, work with both comments and strings removed. - line = clean_lines.elided[linenum] - - if Search(r'\b(const|volatile|void|char|short|int|long' - r'|float|double|signed|unsigned' - r'|schar|u?int8|u?int16|u?int32|u?int64)' - r'\s+(register|static|extern|typedef)\b', - line): - error(filename, linenum, 'build/storage_class', 5, - 'Storage-class specifier (static, extern, typedef, etc) should be ' - 'at the beginning of the declaration.') - - if Match(r'\s*#\s*endif\s*[^/\s]+', line): - error(filename, linenum, 'build/endif_comment', 5, - 'Uncommented text after #endif is non-standard. Use a comment.') - - if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line): - error(filename, linenum, 'build/forward_decl', 5, - 'Inner-style forward declarations are invalid. Remove this line.') - - if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and <? (max and min) operators are non-standard and deprecated.') - - if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line): - # TODO(unknown): Could it be expanded safely to arbitrary references, - # without triggering too many false positives? The first - # attempt triggered 5 warnings for mostly benign code in the regtest, hence - # the restriction. - # Here's the original regexp, for the reference: - # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. - explicit_constructor_match = Match( - r'\s+(?:(?:inline|constexpr)\s+)*(explicit\s+)?' - r'(?:(?:inline|constexpr)\s+)*%s\s*' - r'\(((?:[^()]|\([^()]*\))*)\)' - % re.escape(base_classname), - line) - - if explicit_constructor_match: - is_marked_explicit = explicit_constructor_match.group(1) - - if not explicit_constructor_match.group(2): - constructor_args = [] - else: - constructor_args = explicit_constructor_match.group(2).split(',') - - # collapse arguments so that commas in template parameter lists and function - # argument parameter lists don't split arguments in two - i = 0 - while i < len(constructor_args): - constructor_arg = constructor_args[i] - while (constructor_arg.count('<') > constructor_arg.count('>') or - constructor_arg.count('(') > constructor_arg.count(')')): - constructor_arg += ',' + constructor_args[i + 1] - del constructor_args[i + 1] - constructor_args[i] = constructor_arg - i += 1 - - defaulted_args = [arg for arg in constructor_args if '=' in arg] - noarg_constructor = (not constructor_args or # empty arg list - # 'void' arg specifier - (len(constructor_args) == 1 and - constructor_args[0].strip() == 'void')) - onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg - not noarg_constructor) or - # all but at most one arg defaulted - (len(constructor_args) >= 1 and - not noarg_constructor and - len(defaulted_args) >= len(constructor_args) - 1)) - initializer_list_constructor = bool( - onearg_constructor and - Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) - copy_constructor = bool( - onearg_constructor and - Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' - % re.escape(base_classname), constructor_args[0].strip())) - - if (not is_marked_explicit and - onearg_constructor and - not initializer_list_constructor and - not copy_constructor): - if defaulted_args: - error(filename, linenum, 'runtime/explicit', 5, - 'Constructors callable with one argument ' - 'should be marked explicit.') - else: - error(filename, linenum, 'runtime/explicit', 5, - 'Single-parameter constructors should be marked explicit.') - elif is_marked_explicit and not onearg_constructor: - if noarg_constructor: - error(filename, linenum, 'runtime/explicit', 5, - 'Zero-parameter constructors should not be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', - fncall) and - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and - not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and - not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and - not Search(r'\bcase\s+\(', fncall)): - # TODO(unknown): Space after an operator function seem to be a common - # error, silence those for now by restricting them to highest verbosity. - if Search(r'\boperator_*\b', line): - error(filename, linenum, 'whitespace/parens', 0, - 'Extra space before ( in function call') - else: - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error): - is_namespace_indent_item = ( - len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and - nesting_state.previous_stack_top == nesting_state.stack[-2]) - - if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - clean_lines.elided, line): - CheckItemIndentationInNamespace(filename, clean_lines.elided, - line, error) - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(line, filename, linenum, next_line_start, error): - """Checks for common mistakes in comments. - - Args: - line: The line in question. - filename: The name of the current file. - linenum: The number of the line to check. - next_line_start: The first non-whitespace column of the next line. - error: The function to call with any errors found. - """ - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: - # Allow one space for new scopes, two spaces otherwise: - if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - - # Checks for common mistakes in TODO comments. - comment = line[commentpos:] - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - - # If the comment contains an alphanumeric character, there - # should be a space somewhere between it and the // unless - # it's a /// or //! Doxygen comment. - if (Match(r'//[^ ]*\w', comment) and - not Match(r'(///|//\!)(\s+|$)', comment)): - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw = clean_lines.lines_without_raw_strings - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - # - # Also skip blank line checks for 'extern "C"' blocks, which are formatted - # like namespaces. - if (IsBlankLine(line) and - not nesting_state.InNamespaceBody() and - not nesting_state.InExternC()): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Redundant blank line at the start of a code block ' - 'should be deleted.') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Redundant blank line at the end of a code block ' - 'should be deleted.') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, check comments - next_line_start = 0 - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - next_line_start = len(next_line) - len(next_line.lstrip()) - CheckComment(line, filename, linenum, next_line_start, error) - - # get rid of comments and strings - line = clean_lines.elided[linenum] - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'return []() {};' - if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search(r'for *\(.*[^:]:[^: ]', line) or - Search(r'for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckOperatorSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around operators. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Don't try to do spacing checks for operator methods. Do this by - # replacing the troublesome characters with something else, - # preserving column position for all other characters. - # - # The replacement is done repeatedly to avoid false positives from - # operators that call operators. - while True: - match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) - if match: - line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) - else: - break - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if ((Search(r'[\w.]=', line) or - Search(r'=[\w.]', line)) - and not Search(r'\b(if|while|for) ', line) - # Operators taken from [lex.operators] in C++11 standard. - and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) - and not Search(r'operator=', line)): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - # - # If the operator is followed by a comma, assume it's be used in a - # macro context and don't do any checks. This avoids false - # positives. - # - # Note that && is not included here. This is because there are too - # many false positives due to RValue references. - match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - elif not Match(r'#.*include', line): - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Match(r'^(.*[^\s<])<[^\s=<,]', line) - if match: - (_, _, end_pos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if end_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) - if match: - (_, _, start_pos) = ReverseCloseExpression( - clean_lines, linenum, len(match.group(1))) - if start_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - # - # We also allow operators following an opening parenthesis, since - # those tend to be macros that deal with operators. - match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line) - if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and - not (match.group(1) == 'operator' and match.group(2) == ';')): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type<type<type>> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - -def CheckParenthesisSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around parentheses. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # No spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if len(match.group(2)) not in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - -def CheckCommaSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing near commas and semicolons. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.lines_without_raw_strings - line = clean_lines.elided[linenum] - - # You should always have a space after a comma (either as fn arg or operator) - # - # This does not apply when the non-space character following the - # comma is another comma, since the only time when that happens is - # for empty macro arguments. - # - # We run this check in two passes: first pass on elided lines to - # verify that lines contain missing whitespaces, second pass on raw - # lines to confirm that those missing whitespaces are not due to - # elided comments. - if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and - Search(r',[^,\s]', raw[linenum])): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - -def _IsType(clean_lines, nesting_state, expr): - """Check if expression looks like a type name, returns true if so. - - Args: - clean_lines: A CleansedLines instance containing the file. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - expr: The expression to check. - Returns: - True, if token looks like a type. - """ - # Keep only the last token in the expression - last_word = Match(r'^.*(\b\S+)$', expr) - if last_word: - token = last_word.group(1) - else: - token = expr - - # Match native types and stdint types - if _TYPES.match(token): - return True - - # Try a bit harder to match templated types. Walk up the nesting - # stack until we find something that resembles a typename - # declaration for what we are looking for. - typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) + - r'\b') - block_index = len(nesting_state.stack) - 1 - while block_index >= 0: - if isinstance(nesting_state.stack[block_index], _NamespaceInfo): - return False - - # Found where the opening brace is. We want to scan from this - # line up to the beginning of the function, minus a few lines. - # template <typename Type1, // stop scanning here - # ...> - # class C - # : public ... { // start scanning here - last_line = nesting_state.stack[block_index].starting_linenum - - next_block_start = 0 - if block_index > 0: - next_block_start = nesting_state.stack[block_index - 1].starting_linenum - first_line = last_line - while first_line >= next_block_start: - if clean_lines.elided[first_line].find('template') >= 0: - break - first_line -= 1 - if first_line < next_block_start: - # Didn't find any "template" keyword before reaching the next block, - # there are probably no template things to check for this block - block_index -= 1 - continue - - # Look for typename in the specified range - for i in xrange(first_line, last_line + 1, 1): - if Search(typename_pattern, clean_lines.elided[i]): - return True - block_index -= 1 - - return False - - -def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for horizontal spacing near commas. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces when they are delimiting blocks, classes, namespaces etc. - # And since you should never have braces at the beginning of a line, - # this is an easy test. Except that braces used for initialization don't - # follow the same rule; we often don't want spaces before those. - match = Match(r'^(.*[^ ({>]){', line) - - if match: - # Try a bit harder to check for brace initialization. This - # happens in one of the following forms: - # Constructor() : initializer_list_{} { ... } - # Constructor{}.MemberFunction() - # Type variable{}; - # FunctionCall(type{}, ...); - # LastArgument(..., type{}); - # LOG(INFO) << type{} << " ..."; - # map_of_type[{...}] = ...; - # ternary = expr ? new type{} : nullptr; - # OuterTemplate<InnerTemplateConstructor<Type>{}> - # - # We check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<>]:". - # - # To account for nested initializer list, we allow any number of - # closing braces up to "{;,)<". We can't simply silence the - # warning on first sight of closing brace, because that would - # cause false negatives for things that are not initializer lists. - # Silence this: But not this: - # Outer{ if (...) { - # Inner{...} if (...){ // Missing space before { - # }; } - # - # There is a false negative with this approach if people inserted - # spurious semicolons, e.g. "if (cond){};", but we will catch the - # spurious semicolon with a separate check. - leading_text = match.group(1) - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - trailing_text = '' - if endpos > -1: - trailing_text = endline[endpos:] - for offset in xrange(endlinenum + 1, - min(endlinenum + 3, clean_lines.NumLines() - 1)): - trailing_text += clean_lines.elided[offset] - # We also suppress warnings for `uint64_t{expression}` etc., as the style - # guide recommends brace initialization for integral types to avoid - # overflow/truncation. - if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) - and not _IsType(clean_lines, nesting_state, leading_text)): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - -def IsDecltype(clean_lines, linenum, column): - """Check if the token ending on (linenum, column) is decltype(). - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: the number of the line to check. - column: end column of the token to check. - Returns: - True if this token is decltype() expression, False otherwise. - """ - (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) - if start_col < 0: - return False - if Search(r'\bdecltype\s*$', text[0:start_col]): - return True - return False - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone is using - # braces in a block to explicitly create a new scope, which is commonly used - # to control the lifetime of stack-allocated variables. Braces are also - # used for brace initializers inside function calls. We don't detect this - # perfectly: we just don't complain if the last non-whitespace character on - # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the - # previous line starts a preprocessor block. We also allow a brace on the - # following line if it is part of an array initialization and would not fit - # within the 80 character limit of the preceding line. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline) and - not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'else if\s*\(', line): # could be multi-line if - brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - brace_on_right = endline[endpos:].find('{') != -1 - if brace_on_left != brace_on_right: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Check single-line if/else bodies. The style guide says 'curly braces are not - # required for single-line statements'. We additionally allow multi-line, - # single statements, but we reject anything with more than one semicolon in - # it. This means that the first semicolon after the if should be at the end of - # its line, and the line after that should have an indent level equal to or - # lower than the if. We also check for ambiguous if/else nesting without - # braces. - if_else_match = Search(r'\b(if\s*\(|else\b)', line) - if if_else_match and not Match(r'\s*#', line): - if_indent = GetIndentLevel(line) - endline, endlinenum, endpos = line, linenum, if_else_match.end() - if_match = Search(r'\bif\s*\(', line) - if if_match: - # This could be a multiline if condition, so find the end first. - pos = if_match.end() - 1 - (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) - # Check for an opening brace, either directly after the if or on the next - # line. If found, this isn't a single-statement conditional. - if (not Match(r'\s*{', endline[endpos:]) - and not (Match(r'\s*$', endline[endpos:]) - and endlinenum < (len(clean_lines.elided) - 1) - and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): - while (endlinenum < len(clean_lines.elided) - and ';' not in clean_lines.elided[endlinenum][endpos:]): - endlinenum += 1 - endpos = 0 - if endlinenum < len(clean_lines.elided): - endline = clean_lines.elided[endlinenum] - # We allow a mix of whitespace and closing braces (e.g. for one-liner - # methods) and a single \ after the semicolon (for macros) - endpos = endline.find(';') - if not Match(r';[\s}]*(\\?)$', endline[endpos:]): - # Semicolon isn't the last character, there's something trailing. - # Output a warning if the semicolon is not contained inside - # a lambda expression. - if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', - endline): - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - elif endlinenum < len(clean_lines.elided) - 1: - # Make sure the next line is dedented - next_line = clean_lines.elided[endlinenum + 1] - next_indent = GetIndentLevel(next_line) - # With ambiguous nested if statements, this will error out on the - # if that *doesn't* match the else, regardless of whether it's the - # inner one or outer one. - if (if_match and Match(r'\s*else\b', next_line) - and next_indent != if_indent): - error(filename, linenum, 'readability/braces', 4, - 'Else clause should be indented at the same level as if. ' - 'Ambiguous nested if/else chains require braces.') - elif next_indent > if_indent: - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - - -def CheckTrailingSemicolon(filename, clean_lines, linenum, error): - """Looks for redundant trailing semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] - - # Block bodies should not be followed by a semicolon. Due to C++11 - # brace initialization, there are more places where semicolons are - # required than not, so we use a whitelist approach to check these - # rather than a blacklist. These are the places where "};" should - # be replaced by just "}": - # 1. Some flavor of block following closing parenthesis: - # for (;;) {}; - # while (...) {}; - # switch (...) {}; - # Function(...) {}; - # if (...) {}; - # if (...) else if (...) {}; - # - # 2. else block: - # if (...) else {}; - # - # 3. const member function: - # Function(...) const {}; - # - # 4. Block following some statement: - # x = 42; - # {}; - # - # 5. Block at the beginning of a function: - # Function(...) { - # {}; - # } - # - # Note that naively checking for the preceding "{" will also match - # braces inside multi-dimensional arrays, but this is fine since - # that expression will not contain semicolons. - # - # 6. Block following another block: - # while (true) {} - # {}; - # - # 7. End of namespaces: - # namespace {}; - # - # These semicolons seems far more common than other kinds of - # redundant semicolons, possibly due to people converting classes - # to namespaces. For now we do not warn for this case. - # - # Try matching case 1 first. - match = Match(r'^(.*\)\s*)\{', line) - if match: - # Matched closing parenthesis (case 1). Check the token before the - # matching opening parenthesis, and don't warn if it looks like a - # macro. This avoids these false positives: - # - macro that defines a base class - # - multi-line macro that defines a base class - # - macro that defines the whole class-head - # - # But we still issue warnings for macros that we know are safe to - # warn, specifically: - # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P - # - TYPED_TEST - # - INTERFACE_DEF - # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: - # - # We implement a whitelist of safe macros instead of a blacklist of - # unsafe macros, even though the latter appears less frequently in - # google code and would have been easier to implement. This is because - # the downside for getting the whitelist wrong means some extra - # semicolons, while the downside for getting the blacklist wrong - # would result in compile errors. - # - # In addition to macros, we also don't want to warn on - # - Compound literals - # - Lambdas - # - alignas specifier with anonymous structs - # - decltype - closing_brace_pos = match.group(1).rfind(')') - opening_parenthesis = ReverseCloseExpression( - clean_lines, linenum, closing_brace_pos) - if opening_parenthesis[2] > -1: - line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] - macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) - func = Match(r'^(.*\])\s*$', line_prefix) - if ((macro and - macro.group(1) not in ( - 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', - 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', - 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or - (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or - Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or - Search(r'\bdecltype$', line_prefix) or - Search(r'\s+=\s*$', line_prefix)): - match = None - if (match and - opening_parenthesis[1] > 1 and - Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): - # Multi-line lambda-expression - match = None - - else: - # Try matching cases 2-3. - match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) - if not match: - # Try matching cases 4-6. These are always matched on separate lines. - # - # Note that we can't simply concatenate the previous line to the - # current line and do a single match, otherwise we may output - # duplicate warnings for the blank line case: - # if (cond) { - # // blank line - # } - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if prevline and Search(r'[;{}]\s*$', prevline): - match = Match(r'^(\s*)\{', line) - - # Check matching closing brace - if match: - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if endpos > -1 and Match(r'^\s*;', endline[endpos:]): - # Current {} pair is eligible for semicolon check, and we have found - # the redundant semicolon, output warning here. - # - # Note: because we are scanning forward for opening braces, and - # outputting warnings for the matching closing brace, if there are - # nested blocks with trailing semicolons, we will get the error - # messages in reversed order. - - # We need to check the line forward for NOLINT - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1, - error) - ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum, - error) - - error(filename, endlinenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyBlockBody(filename, clean_lines, linenum, error): - """Look for empty loop/conditional body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - # - # We also check "if" blocks here, since an empty conditional block - # is likely an error. - line = clean_lines.elided[linenum] - matched = Match(r'\s*(for|while|if)\s*\(', line) - if matched: - # Find the end of the conditional expression. - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - if matched.group(1) == 'if': - error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, - 'Empty conditional bodies should use {}') - else: - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - # Check for if statements that have completely empty bodies (no comments) - # and no else clauses. - if end_pos >= 0 and matched.group(1) == 'if': - # Find the position of the opening { for the if statement. - # Return without logging an error if it has no brackets. - opening_linenum = end_linenum - opening_line_fragment = end_line[end_pos:] - # Loop until EOF or find anything that's not whitespace or opening {. - while not Search(r'^\s*\{', opening_line_fragment): - if Search(r'^(?!\s*$)', opening_line_fragment): - # Conditional has no brackets. - return - opening_linenum += 1 - if opening_linenum == len(clean_lines.elided): - # Couldn't find conditional's opening { or any code before EOF. - return - opening_line_fragment = clean_lines.elided[opening_linenum] - # Set opening_line (opening_line_fragment may not be entire opening line). - opening_line = clean_lines.elided[opening_linenum] - - # Find the position of the closing }. - opening_pos = opening_line_fragment.find('{') - if opening_linenum == end_linenum: - # We need to make opening_pos relative to the start of the entire line. - opening_pos += end_pos - (closing_line, closing_linenum, closing_pos) = CloseExpression( - clean_lines, opening_linenum, opening_pos) - if closing_pos < 0: - return - - # Now construct the body of the conditional. This consists of the portion - # of the opening line after the {, all lines until the closing line, - # and the portion of the closing line before the }. - if (clean_lines.raw_lines[opening_linenum] != - CleanseComments(clean_lines.raw_lines[opening_linenum])): - # Opening line ends with a comment, so conditional isn't empty. - return - if closing_linenum > opening_linenum: - # Opening line after the {. Ignore comments here since we checked above. - body = list(opening_line[opening_pos+1:]) - # All lines until closing line, excluding closing line, with comments. - body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) - # Closing line before the }. Won't (and can't) have comments. - body.append(clean_lines.elided[closing_linenum][:closing_pos-1]) - body = '\n'.join(body) - else: - # If statement has brackets and fits on a single line. - body = opening_line[opening_pos+1:closing_pos-1] - - # Check if the body is empty - if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body): - return - # The body is empty. Now make sure there's not an else clause. - current_linenum = closing_linenum - current_line_fragment = closing_line[closing_pos:] - # Loop until EOF or find anything that's not whitespace or else clause. - while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): - if Search(r'^(?=\s*else)', current_line_fragment): - # Found an else clause, so don't log an error. - return - current_linenum += 1 - if current_linenum == len(clean_lines.elided): - break - current_line_fragment = clean_lines.elided[current_linenum] - - # The body is empty and there's no else clause until EOF or other code. - error(filename, end_linenum, 'whitespace/empty_if_body', 4, - ('If statement had no body and no else clause')) - - -def FindCheckMacro(line): - """Find a replaceable CHECK-like macro. - - Args: - line: line to search on. - Returns: - (macro name, start position), or (None, -1) if no replaceable - macro is found. - """ - for macro in _CHECK_MACROS: - i = line.find(macro) - if i >= 0: - # Find opening parenthesis. Do a regular expression match here - # to make sure that we are matching the expected CHECK macro, as - # opposed to some other macro that happens to contain the CHECK - # substring. - matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) - if not matched: - continue - return (macro, len(matched.group(1))) - return (None, -1) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - lines = clean_lines.elided - (check_macro, start_pos) = FindCheckMacro(lines[linenum]) - if not check_macro: - return - - # Find end of the boolean expression by matching parentheses - (last_line, end_line, end_pos) = CloseExpression( - clean_lines, linenum, start_pos) - if end_pos < 0: - return - - # If the check macro is followed by something other than a - # semicolon, assume users will log their own custom error messages - # and don't suggest any replacements. - if not Match(r'\s*;', last_line[end_pos:]): - return - - if linenum == end_line: - expression = lines[linenum][start_pos + 1:end_pos - 1] - else: - expression = lines[linenum][start_pos + 1:] - for i in xrange(linenum + 1, end_line): - expression += lines[i] - expression += last_line[0:end_pos - 1] - - # Parse expression so that we can take parentheses into account. - # This avoids false positives for inputs like "CHECK((a < 4) == b)", - # which is not replaceable by CHECK_LE. - lhs = '' - rhs = '' - operator = None - while expression: - matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' - r'==|!=|>=|>|<=|<|\()(.*)$', expression) - if matched: - token = matched.group(1) - if token == '(': - # Parenthesized operand - expression = matched.group(2) - (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) - if end < 0: - return # Unmatched parenthesis - lhs += '(' + expression[0:end] - expression = expression[end:] - elif token in ('&&', '||'): - # Logical and/or operators. This means the expression - # contains more than one term, for example: - # CHECK(42 < a && a < b); - # - # These are not replaceable with CHECK_LE, so bail out early. - return - elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): - # Non-relational operator - lhs += token - expression = matched.group(2) - else: - # Relational operator - operator = token - rhs = matched.group(2) - break - else: - # Unparenthesized operand. Instead of appending to lhs one character - # at a time, we do another regular expression match to consume several - # characters at once if possible. Trivial benchmark shows that this - # is more efficient when the operands are longer than a single - # character, which is generally the case. - matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) - if not matched: - matched = Match(r'^(\s*\S)(.*)$', expression) - if not matched: - break - lhs += matched.group(1) - expression = matched.group(2) - - # Only apply checks if we got all parts of the boolean expression - if not (lhs and operator and rhs): - return - - # Check that rhs do not contain logical operators. We already know - # that lhs is fine since the loop above parses out && and ||. - if rhs.find('&&') > -1 or rhs.find('||') > -1: - return - - # At least one of the operands must be a constant literal. This is - # to avoid suggesting replacements for unprintable things like - # CHECK(variable != iterator) - # - # The following pattern matches decimal, hex integers, strings, and - # characters (in that order). - lhs = lhs.strip() - rhs = rhs.strip() - match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' - if Match(match_constant, lhs) or Match(match_constant, rhs): - # Note: since we know both lhs and rhs, we can provide a more - # descriptive error message like: - # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) - # Instead of: - # Consider using CHECK_EQ instead of CHECK(a == b) - # - # We are still keeping the less descriptive message because if lhs - # or rhs gets long, the error message might become unreadable. - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[check_macro][operator], - check_macro, operator)) - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - # Issue 337 - # https://mail.python.org/pipermail/python-list/2012-August/628809.html - if (sys.version_info.major, sys.version_info.minor) <= (3, 2): - # https://github.com/python/cpython/blob/2.7/Include/unicodeobject.h#L81 - is_wide_build = sysconfig.get_config_var("Py_UNICODE_SIZE") >= 4 - # https://github.com/python/cpython/blob/2.7/Objects/unicodeobject.c#L564 - is_low_surrogate = 0xDC00 <= ord(uc) <= 0xDFFF - if not is_wide_build and is_low_surrogate: - width -= 1 - - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw_lines = clean_lines.lines_without_raw_strings - line = raw_lines[linenum] - prev = raw_lines[linenum - 1] if linenum > 0 else '' - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' - classinfo = nesting_state.InnermostClass() - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - # There are certain situations we allow one space, notably for - # section labels, and also lines containing multi-line raw strings. - # We also don't check for lines that look like continuation lines - # (of lines ending in double quotes, commas, equals, or angle brackets) - # because the rules for how to indent those are non-trivial. - if (not Search(r'[",=><] *$', prev) and - (initial_spaces == 1 or initial_spaces == 3) and - not Match(scope_or_label_pattern, cleansed_line) and - not (clean_lines.raw_lines[linenum] != line and - Match(r'^\s*""', line))): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - - # Check if the line is a header guard. - is_header_guard = False - if IsHeaderExtension(file_extension): - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^\s*//\s*[^\s]*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - if line_width > _line_length: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= %i characters long' % _line_length) - - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckTrailingSemicolon(filename, clean_lines, linenum, error) - CheckEmptyBlockBody(filename, clean_lines, linenum, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckOperatorSpacing(filename, clean_lines, linenum, error) - CheckParenthesisSpacing(filename, clean_lines, linenum, error) - CheckCommaSpacing(filename, clean_lines, linenum, error) - CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_cpp_h = include in _CPP_HEADERS - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - if target_base == include_base and ( - include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - # Only do this check if the included header follows google naming - # conventions. If not, assume that it's a 3rd party API that - # requires special include conventions. - # - # We also make an exception for Lua headers, which follow google - # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - duplicate_line = include_state.FindHeader(include) - if duplicate_line >= 0: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, duplicate_line)) - elif (include.endswith('.cc') and - os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): - error(filename, linenum, 'build/include', 4, - 'Do not include .cc files from other packages') - elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): - include_state.include_list[-1].append((include, linenum)) - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) - if not include_state.IsInAlphabeticalOrder( - clean_lines, linenum, canonical_include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - include_state.SetLastHeader(canonical_include) - - - -def _GetTextInside(text, start_pattern): - r"""Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(unknown): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -# Patterns for matching call-by-reference parameters. -# -# Supports nested templates up to 2 levels deep using this messy pattern: -# < (?: < (?: < [^<>]* -# > -# | [^<>] )* -# > -# | [^<>] )* -# > -_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* -_RE_PATTERN_TYPE = ( - r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' - r'(?:\w|' - r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' - r'::)+') -# A call-by-reference parameter ends with '& identifier'. -_RE_PATTERN_REF_PARAM = re.compile( - r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' - r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') -# A call-by-const-reference parameter either ends with 'const& identifier' -# or looks like 'const type& identifier' when 'type' is atomic. -_RE_PATTERN_CONST_REF_PARAM = ( - r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + - r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') -# Stream types. -_RE_PATTERN_REF_STREAM_PARAM = ( - r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')') - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, - include_state, nesting_state, error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Reset include state across preprocessor directives. This is meant - # to silence warnings for conditional includes. - match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) - if match: - include_state.ResetSection(match.group(1)) - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # Perform other checks now that we are sure that this is not an include line - CheckCasts(filename, clean_lines, linenum, error) - CheckGlobalStatic(filename, clean_lines, linenum, error) - CheckPrintf(filename, clean_lines, linenum, error) - - if IsHeaderExtension(file_extension): - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes declare or disable copy/assign - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(unknown): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (IsHeaderExtension(file_extension) - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckGlobalStatic(filename, clean_lines, linenum, error): - """Check for unsafe global or static objects. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Match two lines at a time to support multiline declarations - if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): - line += clean_lines.elided[linenum + 1].strip() - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access, and - # also because globals can be destroyed when some threads are still running. - # TODO(unknown): Generalize this to also find static unique_ptr instances. - # TODO(unknown): File bugs for clang-tidy to find these. - match = Match( - r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' - r'([a-zA-Z0-9_:]+)\b(.*)', - line) - - # Remove false positives: - # - String pointers (as opposed to values). - # string *pointer - # const string *pointer - # string const *pointer - # string *const pointer - # - # - Functions and template specializations. - # string Function<Type>(... - # string Class<Type>::Method(... - # - # - Operators. These are matched separately because operator names - # cross non-word boundaries, and trying to match both operators - # and functions at the same time would decrease accuracy of - # matching identifiers. - # string Class::operator*() - if (match and - not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and - not Search(r'\boperator\W', line) and - not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): - if Search(r'\bconst\b', line): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string ' - 'instead: "%schar%s %s[]".' % - (match.group(1), match.group(2) or '', match.group(3))) - else: - error(filename, linenum, 'runtime/string', 4, - 'Static/global string variables are not permitted.') - - if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or - Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - -def CheckPrintf(filename, clean_lines, linenum, error): - """Check for printf related issues. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\s*\(', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\s*\(', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - -def IsDerivedFunction(clean_lines, linenum): - """Check if current line contains an inherited function. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains a function with "override" - virt-specifier. - """ - # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) - if match: - # Look for "override" after the matching closing parenthesis - line, _, closing_paren = CloseExpression( - clean_lines, i, len(match.group(1))) - return (closing_paren >= 0 and - Search(r'\boverride\b', line[closing_paren:])) - return False - - -def IsOutOfLineMethodDefinition(clean_lines, linenum): - """Check if current line contains an out-of-line method definition. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains an out-of-line method definition. - """ - # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): - return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None - return False - - -def IsInitializerList(clean_lines, linenum): - """Check if current line is inside constructor initializer list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line appears to be inside constructor initializer - list, False otherwise. - """ - for i in xrange(linenum, 1, -1): - line = clean_lines.elided[i] - if i == linenum: - remove_function_body = Match(r'^(.*)\{\s*$', line) - if remove_function_body: - line = remove_function_body.group(1) - - if Search(r'\s:\s*\w+[({]', line): - # A lone colon tend to indicate the start of a constructor - # initializer list. It could also be a ternary operator, which - # also tend to appear in constructor initializer lists as - # opposed to parameter lists. - return True - if Search(r'\}\s*,\s*$', line): - # A closing brace followed by a comma is probably the end of a - # brace-initialized member in constructor initializer list. - return True - if Search(r'[{};]\s*$', line): - # Found one of the following: - # - A closing brace or semicolon, probably the end of the previous - # function. - # - An opening brace, probably the start of current class or namespace. - # - # Current line is probably not inside an initializer list since - # we saw one of those things without seeing the starting colon. - return False - - # Got to the beginning of the file without seeing the start of - # constructor initializer list. - return False - - -def CheckForNonConstReference(filename, clean_lines, linenum, - nesting_state, error): - """Check for non-const references. - - Separate from CheckLanguage since it scans backwards from current - line, instead of scanning forward. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # Do nothing if there is no '&' on current line. - line = clean_lines.elided[linenum] - if '&' not in line: - return - - # If a function is inherited, current function doesn't have much of - # a choice, so any non-const references should not be blamed on - # derived function. - if IsDerivedFunction(clean_lines, linenum): - return - - # Don't warn on out-of-line method definitions, as we would warn on the - # in-line declaration, if it isn't marked with 'override'. - if IsOutOfLineMethodDefinition(clean_lines, linenum): - return - - # Long type names may be broken across multiple lines, usually in one - # of these forms: - # LongType - # ::LongTypeContinued &identifier - # LongType:: - # LongTypeContinued &identifier - # LongType< - # ...>::LongTypeContinued &identifier - # - # If we detected a type split across two lines, join the previous - # line to current line so that we can match const references - # accordingly. - # - # Note that this only scans back one line, since scanning back - # arbitrary number of lines would be expensive. If you have a type - # that spans more than 2 lines, please use a typedef. - if linenum > 1: - previous = None - if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): - # previous_line\n + ::current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', - clean_lines.elided[linenum - 1]) - elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): - # previous_line::\n + current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', - clean_lines.elided[linenum - 1]) - if previous: - line = previous.group(1) + line.lstrip() - else: - # Check for templated parameter that is split across multiple lines - endpos = line.rfind('>') - if endpos > -1: - (_, startline, startpos) = ReverseCloseExpression( - clean_lines, linenum, endpos) - if startpos > -1 and startline < linenum: - # Found the matching < on an earlier line, collect all - # pieces up to current line. - line = '' - for i in xrange(startline, linenum + 1): - line += clean_lines.elided[i].strip() - - # Check for non-const references in function parameters. A single '&' may - # found in the following places: - # inside expression: binary & for bitwise AND - # inside expression: unary & for taking the address of something - # inside declarators: reference parameter - # We will exclude the first two cases by checking that we are not inside a - # function body, including one that was just introduced by a trailing '{'. - # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. - if (nesting_state.previous_stack_top and - not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or - isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): - # Not at toplevel, not within a class, and not within a namespace - return - - # Avoid initializer lists. We only need to scan back from the - # current line for something that starts with ':'. - # - # We don't need to check the current line, since the '&' would - # appear inside the second set of parentheses on the current line as - # opposed to the first set. - if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 10), -1): - previous_line = clean_lines.elided[i] - if not Search(r'[),]\s*$', previous_line): - break - if Match(r'^\s*:\s+\S', previous_line): - return - - # Avoid preprocessors - if Search(r'\\\s*$', line): - return - - # Avoid constructor initializer lists - if IsInitializerList(clean_lines, linenum): - return - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". Do not check - # those function parameters. - # - # We also accept & in static_assert, which looks like a function but - # it's actually a declaration expression. - whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' - r'operator\s*[<>][<>]|' - r'static_assert|COMPILE_ASSERT' - r')\s*\(') - if Search(whitelisted_functions, line): - return - elif not Search(r'\S+\([^)]*$', line): - # Don't see a whitelisted function on this line. Actually we - # didn't see any function name on this line, so this is likely a - # multi-line parameter list. Try a bit harder to catch this case. - for i in xrange(2): - if (linenum > i and - Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): - return - - decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body - for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): - if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and - not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer: ' + - ReplaceAll(' *<', '<', parameter)) - - -def CheckCasts(filename, clean_lines, linenum, error): - """Various cast related checks. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' - r'(int|float|double|bool|char|int32|uint32|int64|uint64)' - r'(\([^)].*)', line) - expecting_function = ExpectingFunctionArgs(clean_lines, linenum) - if match and not expecting_function: - matched_type = match.group(2) - - # matched_new_or_template is used to silence two false positives: - # - New operators - # - Template arguments with function types - # - # For template arguments, we match on types immediately following - # an opening bracket without any spaces. This is a fast way to - # silence the common case where the function type is the first - # template argument. False negative with less-than comparison is - # avoided because those operators are usually followed by a space. - # - # function<double(double)> // bracket + no space = false positive - # value < double(42) // bracket + space = true positive - matched_new_or_template = match.group(1) - - # Avoid arrays by looking for brackets that come after the closing - # parenthesis. - if Match(r'\([^()]+\)\s*\[', match.group(3)): - return - - # Other things to ignore: - # - Function pointers - # - Casts to pointer types - # - Placement new - # - Alias declarations - matched_funcptr = match.group(3) - if (matched_new_or_template is None and - not (matched_funcptr and - (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', - matched_funcptr) or - matched_funcptr.startswith('(*)'))) and - not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and - not Search(r'new\(\S+\)\s*' + matched_type, line)): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - matched_type) - - if not expecting_function: - CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', - r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', - r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - # - # Some non-identifier character is required before the '&' for the - # expression to be recognized as a cast. These are casts: - # expression = &static_cast<int*>(temporary()); - # function(&(int*)(temporary())); - # - # This is not a cast: - # reference_type&(int* function_param); - match = Search( - r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' - r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) - if match: - # Try a better error message when the & is bound to something - # dereferenced by the casted pointer, as opposed to the casted - # pointer itself. - parenthesis_error = False - match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) - if match: - _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) - if x1 >= 0 and clean_lines.elided[y1][x1] == '(': - _, y2, x2 = CloseExpression(clean_lines, y1, x1) - if x2 >= 0: - extended_line = clean_lines.elided[y2][x2:] - if y2 < clean_lines.NumLines() - 1: - extended_line += clean_lines.elided[y2 + 1] - if Match(r'\s*(?:->|\[)', extended_line): - parenthesis_error = True - - if parenthesis_error: - error(filename, linenum, 'readability/casting', 4, - ('Are you taking an address of something dereferenced ' - 'from a cast? Wrapping the dereferenced expression in ' - 'parentheses will make the binding more obvious')) - else: - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - -def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): - """Checks for a C-style cast by looking for the pattern. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - line = clean_lines.elided[linenum] - match = Search(pattern, line) - if not match: - return False - - # Exclude lines with keywords that tend to look like casts - context = line[0:match.start(1) - 1] - if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): - return False - - # Try expanding current context to see if we one level of - # parentheses inside a macro. - if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 5), -1): - context = clean_lines.elided[i] + context - if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): - return False - - # operator++(int) and operator--(int) - if context.endswith(' operator++') or context.endswith(' operator--'): - return False - - # A single unnamed argument for a function tends to look like old style cast. - # If we see those, don't issue warnings for deprecated casts. - remainder = line[match.end(0):] - if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', - remainder): - return False - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -def ExpectingFunctionArgs(clean_lines, linenum): - """Checks whether where function type arguments are expected. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - - Returns: - True if the line at 'linenum' is inside something that expects arguments - of function types. - """ - line = clean_lines.elided[linenum] - return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - (linenum >= 2 and - (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', - clean_lines.elided[linenum - 1]) or - Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', - clean_lines.elided[linenum - 2]) or - Search(r'\bstd::m?function\s*\<\s*$', - clean_lines.elided[linenum - 1])))) - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('<deque>', ('deque',)), - ('<functional>', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('<limits>', ('numeric_limits',)), - ('<list>', ('list',)), - ('<map>', ('map', 'multimap',)), - ('<memory>', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', - 'unique_ptr', 'weak_ptr')), - ('<queue>', ('queue', 'priority_queue',)), - ('<set>', ('set', 'multiset',)), - ('<stack>', ('stack',)), - ('<string>', ('char_traits', 'basic_string',)), - ('<tuple>', ('tuple',)), - ('<unordered_map>', ('unordered_map', 'unordered_multimap')), - ('<unordered_set>', ('unordered_set', 'unordered_multiset')), - ('<utility>', ('pair',)), - ('<vector>', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('<hash_map>', ('hash_map', 'hash_multimap',)), - ('<hash_set>', ('hash_set', 'hash_multiset',)), - ('<slist>', ('slist',)), - ) - -_HEADERS_MAYBE_TEMPLATES = ( - ('<algorithm>', ('copy', 'max', 'min', 'min_element', 'sort', - 'transform', - )), - ('<utility>', ('forward', 'make_pair', 'move', 'swap')), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_headers_maybe_templates = [] -for _header, _templates in _HEADERS_MAYBE_TEMPLATES: - for _template in _templates: - # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_headers_maybe_templates.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - _header)) - -# Other scripts may reach in and modify this pattern. -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the .cc file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - - fileinfo = FileInfo(filename_cc) - if not fileinfo.IsSource(): - return (False, '') - filename_cc = filename_cc[:-len(fileinfo.Extension())] - matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()) - if matched_test_suffix: - filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_dict, io=codecs): - """Fill up the include_dict with new includes found from the file. - - Args: - filename: the name of the header to read. - include_dict: a dictionary in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was successfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - include_dict.setdefault(include, linenum) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the <functional>. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '<functional>': (1219, 'less<>') } - - for linenum in xrange(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required['<string>'] = (linenum, 'string') - - for pattern, template, header in _re_pattern_headers_maybe_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - matched = pattern.search(line) - if matched: - # Don't warn about IWYU in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's flatten the include_state include_list and copy it into a dictionary. - include_dict = dict([item for sublist in include_state.include_list - for item in sublist]) - - # Did we find the header for this file (if any) and successfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_dict is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_dict.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_dict, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_dict: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def CheckRedundantVirtual(filename, clean_lines, linenum, error): - """Check if line contains a redundant "virtual" function-specifier. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Look for "virtual" on current line. - line = clean_lines.elided[linenum] - virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) - if not virtual: return - - # Ignore "virtual" keywords that are near access-specifiers. These - # are only used in class base-specifier and do not apply to member - # functions. - if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or - Match(r'^\s+(public|protected|private)\b', virtual.group(3))): - return - - # Ignore the "virtual" keyword from virtual base classes. Usually - # there is a column on the same line in these cases (virtual base - # classes are rare in google3 because multiple inheritance is rare). - if Match(r'^.*[^:]:[^:].*$', line): return - - # Look for the next opening parenthesis. This is the start of the - # parameter list (possibly on the next line shortly after virtual). - # TODO(unknown): doesn't work if there are virtual functions with - # decltype() or other things that use parentheses, but csearch suggests - # that this is rare. - end_col = -1 - end_line = -1 - start_col = len(virtual.group(2)) - for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): - line = clean_lines.elided[start_line][start_col:] - parameter_list = Match(r'^([^(]*)\(', line) - if parameter_list: - # Match parentheses to find the end of the parameter list - (_, end_line, end_col) = CloseExpression( - clean_lines, start_line, start_col + len(parameter_list.group(1))) - break - start_col = 0 - - if end_col < 0: - return # Couldn't find end of parameter list, give up - - # Look for "override" or "final" after the parameter list - # (possibly on the next few lines). - for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): - line = clean_lines.elided[i][end_col:] - match = Search(r'\b(override|final)\b', line) - if match: - error(filename, linenum, 'readability/inheritance', 4, - ('"virtual" is redundant since function is ' - 'already declared as "%s"' % match.group(1))) - - # Set end_col to check whole lines after we are done with the - # first line. - end_col = 0 - if Search(r'[^\w]\s*$', line): - break - - -def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): - """Check if line contains a redundant "override" or "final" virt-specifier. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Look for closing parenthesis nearby. We need one to confirm where - # the declarator ends and where the virt-specifier starts to avoid - # false positives. - line = clean_lines.elided[linenum] - declarator_end = line.rfind(')') - if declarator_end >= 0: - fragment = line[declarator_end:] - else: - if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: - fragment = line - else: - return - - # Check that at most one of "override" or "final" is present, not both - if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): - error(filename, linenum, 'readability/inheritance', 4, - ('"override" is redundant since function is ' - 'already declared as "final"')) - - - - -# Returns true if we are at a new block, and it is directly -# inside of a namespace. -def IsBlockInNameSpace(nesting_state, is_forward_declaration): - """Checks that the new block is directly in a namespace. - - Args: - nesting_state: The _NestingState object that contains info about our state. - is_forward_declaration: If the class is a forward declared class. - Returns: - Whether or not the new block is directly in a namespace. - """ - if is_forward_declaration: - if len(nesting_state.stack) >= 1 and ( - isinstance(nesting_state.stack[-1], _NamespaceInfo)): - return True - else: - return False - - return (len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.stack[-2], _NamespaceInfo)) - - -def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - raw_lines_no_comments, linenum): - """This method determines if we should apply our namespace indentation check. - - Args: - nesting_state: The current nesting state. - is_namespace_indent_item: If we just put a new class on the stack, True. - If the top of the stack is not a class, or we did not recently - add the class, False. - raw_lines_no_comments: The lines without the comments. - linenum: The current line number we are processing. - - Returns: - True if we should apply our namespace indentation check. Currently, it - only works for classes and namespaces inside of a namespace. - """ - - is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, - linenum) - - if not (is_namespace_indent_item or is_forward_declaration): - return False - - # If we are in a macro, we do not want to check the namespace indentation. - if IsMacroDefinition(raw_lines_no_comments, linenum): - return False - - return IsBlockInNameSpace(nesting_state, is_forward_declaration) - - -# Call this method if the line is directly inside of a namespace. -# If the line above is blank (excluding comments) or the start of -# an inner namespace, it cannot be indented. -def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, - error): - line = raw_lines_no_comments[linenum] - if Match(r'^\s+', line): - error(filename, linenum, 'runtime/indentation_namespace', 4, - 'Do not indent within a namespace') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error) - if nesting_state.InAsmBlock(): return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - nesting_state, error) - CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckVlogArguments(filename, clean_lines, line, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - CheckRedundantVirtual(filename, clean_lines, line, error) - CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def FlagCxx11Features(filename, clean_lines, linenum, error): - """Flag those c++11 features that we only allow in certain places. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++ TR1 headers. - if include and include.group(1).startswith('tr1/'): - error(filename, linenum, 'build/c++tr1', 5, - ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) - - # Flag unapproved C++11 headers. - if include and include.group(1) in ('cfenv', - 'condition_variable', - 'fenv.h', - 'future', - 'mutex', - 'thread', - 'chrono', - 'ratio', - 'regex', - 'system_error', - ): - error(filename, linenum, 'build/c++11', 5, - ('<%s> is an unapproved C++11 header.') % include.group(1)) - - # The only place where we need to worry about C++11 keywords and library - # features in preprocessor directives is in macro definitions. - if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return - - # These are classes and free functions. The classes are always - # mentioned as std::*, but we only catch the free functions if - # they're not found by ADL. They're alphabetical by header. - for top_name in ( - # type_traits - 'alignment_of', - 'aligned_union', - ): - if Search(r'\bstd::%s\b' % top_name, line): - error(filename, linenum, 'build/c++11', 5, - ('std::%s is an unapproved C++11 class or function. Send c-style ' - 'an example of where it would make your code more readable, and ' - 'they may let you use it.') % top_name) - - -def FlagCxx14Features(filename, clean_lines, linenum, error): - """Flag those C++14 features that we restrict. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++14 headers. - if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): - error(filename, linenum, 'build/c++14', 5, - ('<%s> is an unapproved C++14 header.') % include.group(1)) - - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - ProcessGlobalSuppresions(lines) - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - - if IsHeaderExtension(file_extension): - CheckForHeaderGuard(filename, clean_lines, error) - - for line in xrange(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - FlagCxx11Features(filename, clean_lines, line, error) - nesting_state.CheckCompletedBlocks(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # Check that the .cc file has included its header if it exists. - if _IsSourceExtension(file_extension): - CheckHeaderFileIncluded(filename, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForBadCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessConfigOverrides(filename): - """ Loads the configuration files and processes the config overrides. - - Args: - filename: The name of the file being processed by the linter. - - Returns: - False if the current |filename| should not be processed further. - """ - - abs_filename = os.path.abspath(filename) - cfg_filters = [] - keep_looking = True - while keep_looking: - abs_path, base_name = os.path.split(abs_filename) - if not base_name: - break # Reached the root directory. - - cfg_file = os.path.join(abs_path, "CPPLINT.cfg") - abs_filename = abs_path - if not os.path.isfile(cfg_file): - continue - - try: - with open(cfg_file) as file_handle: - for line in file_handle: - line, _, _ = line.partition('#') # Remove comments. - if not line.strip(): - continue - - name, _, val = line.partition('=') - name = name.strip() - val = val.strip() - if name == 'set noparent': - keep_looking = False - elif name == 'filter': - cfg_filters.append(val) - elif name == 'exclude_files': - # When matching exclude_files pattern, use the base_name of - # the current file name or the directory name we are processing. - # For example, if we are checking for lint errors in /foo/bar/baz.cc - # and we found the .cfg file at /foo/CPPLINT.cfg, then the config - # file's "exclude_files" filter is meant to be checked against "bar" - # and not "baz" nor "bar/baz.cc". - if base_name: - pattern = re.compile(val) - if pattern.match(base_name): - if _cpplint_state.quiet: - # Suppress "Ignoring file" warning when using --quiet. - return False - sys.stderr.write('Ignoring "%s": file excluded by "%s". ' - 'File path component "%s" matches ' - 'pattern "%s"\n' % - (filename, cfg_file, base_name, val)) - return False - elif name == 'linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - sys.stderr.write('Line length must be numeric.') - elif name == 'root': - global _root - # root directories are specified relative to CPPLINT.cfg dir. - _root = os.path.join(os.path.dirname(cfg_file), val) - elif name == 'headers': - ProcessHppHeadersOption(val) - else: - sys.stderr.write( - 'Invalid configuration option (%s) in file %s\n' % - (name, cfg_file)) - - except IOError: - sys.stderr.write( - "Skipping config file '%s': Can't open for reading\n" % cfg_file) - keep_looking = False - - # Apply all the accumulated filters in reverse order (top-level directory - # config options having the least priority). - for filter in reversed(cfg_filters): - _AddFilters(filter) - - return True - - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) - _BackupFilters() - old_errors = _cpplint_state.error_count - - if not ProcessConfigOverrides(filename): - _RestoreFilters() - return - - lf_lines = [] - crlf_lines = [] - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - # Remove trailing '\r'. - # The -1 accounts for the extra trailing blank line we get from split() - for linenum in range(len(lines) - 1): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - crlf_lines.append(linenum + 1) - else: - lf_lines.append(linenum + 1) - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - _RestoreFilters() - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if filename != '-' and file_extension not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - - # If end-of-line sequences are a mix of LF and CR-LF, issue - # warnings on the lines with CR. - # - # Don't issue any warnings if all lines are uniformly LF or CR-LF, - # since critique can handle these just fine, and the style guide - # doesn't dictate a particular end of line sequence. - # - # We can't depend on os.linesep to determine what the desired - # end-of-line sequence should be, since that will return the - # server-side end-of-line sequence. - if lf_lines and crlf_lines: - # Warn on every line with CR. An alternative approach might be to - # check whether the file is mostly CRLF or just LF, and warn on the - # minority, we bias toward LF here since most tools prefer LF. - for linenum in crlf_lines: - Error(filename, linenum, 'whitespace/newline', 1, - 'Unexpected \\r (^M) found; better to use only \\n') - - # Suppress printing anything if --quiet was passed unless the error - # count has increased after processing this file. - if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count: - sys.stdout.write('Done processing %s\n' % filename) - _RestoreFilters() - - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'counting=', - 'filter=', - 'root=', - 'linelength=', - 'extensions=', - 'headers=', - 'quiet']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - filters = '' - quiet = _Quiet() - counting_style = '' - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--output': - if val not in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - output_format = val - elif opt == '--quiet': - quiet = True - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - elif opt == '--linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - PrintUsage('Line length must be digits.') - elif opt == '--extensions': - global _valid_extensions - try: - _valid_extensions = set(val.split(',')) - except ValueError: - PrintUsage('Extensions must be comma separated list.') - elif opt == '--headers': - ProcessHppHeadersOption(val) - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetQuiet(quiet) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - - return filenames - - -def main(): - filenames = ParseArguments(sys.argv[1:]) - - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - # If --quiet is passed, suppress printing error count unless there are errors. - if not _cpplint_state.quiet or _cpplint_state.error_count > 0: - _cpplint_state.PrintErrorCounts() - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/litlint.py b/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/litlint.py deleted file mode 100755 index 81b89c21443..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/litlint.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -# litlint -# -# Ensure RUN commands in lit tests are free of common errors. -# -# If any errors are detected, litlint returns a nonzero exit code. -# - -import optparse -import re -import sys - -# Compile regex once for all files -runRegex = re.compile(r'(?<!-o)(?<!%run) %t\s') - -def LintLine(s): - """ Validate a line - - Args: - s: str, the line to validate - - Returns: - Returns an error message and a 1-based column number if an error was - detected, otherwise (None, None). - """ - - # Check that RUN command can be executed with an emulator - m = runRegex.search(s) - if m: - start, end = m.span() - return ('missing %run before %t', start + 2) - - # No errors - return (None, None) - - -def LintFile(p): - """ Check that each RUN command can be executed with an emulator - - Args: - p: str, valid path to a file - - Returns: - The number of errors detected. - """ - errs = 0 - with open(p, 'r') as f: - for i, s in enumerate(f.readlines(), start=1): - msg, col = LintLine(s) - if msg != None: - errs += 1 - errorMsg = 'litlint: {}:{}:{}: error: {}.\n{}{}\n' - arrow = (col-1) * ' ' + '^' - sys.stderr.write(errorMsg.format(p, i, col, msg, s, arrow)) - return errs - - -if __name__ == "__main__": - # Parse args - parser = optparse.OptionParser() - parser.add_option('--filter') # ignored - (options, filenames) = parser.parse_args() - - # Lint each file - errs = 0 - for p in filenames: - errs += LintFile(p) - - # If errors, return nonzero - if errs > 0: - sys.exit(1) diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/litlint_test.py b/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/litlint_test.py deleted file mode 100755 index 3ce482d7044..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/scripts/litlint_test.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python - -# Tests for litlint.py -# -# Usage: python litlint_test.py -# -# Returns nonzero if any test fails - -import litlint -import unittest - -class TestLintLine(unittest.TestCase): - def test_missing_run(self): - f = litlint.LintLine - self.assertEqual(f(' %t '), ('missing %run before %t', 2)) - self.assertEqual(f(' %t\n'), ('missing %run before %t', 2)) - self.assertEqual(f(' %t.so '), (None, None)) - self.assertEqual(f(' %t.o '), (None, None)) - self.assertEqual(f('%run %t '), (None, None)) - self.assertEqual(f('-o %t '), (None, None)) - -if __name__ == '__main__': - unittest.main() diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/symbolizer/scripts/ar_to_bc.sh b/gnu/llvm/compiler-rt/lib/sanitizer_common/symbolizer/scripts/ar_to_bc.sh deleted file mode 100755 index 5c77bea8329..00000000000 --- a/gnu/llvm/compiler-rt/lib/sanitizer_common/symbolizer/scripts/ar_to_bc.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -function usage() { - echo "Usage: $0 INPUT... OUTPUT" - exit 1 -} - -if [ "$#" -le 1 ]; then - usage -fi - -[[ $AR == /* ]] || AR=$PWD/$AR -[[ $LINK == /* ]] || LINK=$PWD/$LINK - -INPUTS= -OUTPUT= -for ARG in $@; do - INPUTS="$INPUTS $OUTPUT" - OUTPUT=$(readlink -f $ARG) -done - -echo Inputs: $INPUTS -echo Output: $OUTPUT - -SCRATCH_DIR=$(mktemp -d) -ln -s $INPUTS $SCRATCH_DIR/ - -pushd $SCRATCH_DIR - -for INPUT in *; do - for OBJ in $($AR t $INPUT); do - $AR x $INPUT $OBJ - mv -f $OBJ $(basename $INPUT).$OBJ - done -done - -$LINK *.o -o $OUTPUT - -rm -rf $SCRATCH_DIR diff --git a/gnu/llvm/compiler-rt/lib/scudo/CMakeLists.txt b/gnu/llvm/compiler-rt/lib/scudo/CMakeLists.txt deleted file mode 100644 index 2a560b8fcb7..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/CMakeLists.txt +++ /dev/null @@ -1,154 +0,0 @@ -add_compiler_rt_component(scudo) - -include_directories(..) - -set(SCUDO_CFLAGS ${SANITIZER_COMMON_CFLAGS}) -# SANITIZER_COMMON_CFLAGS include -fno-builtin, but we actually want builtins! -list(APPEND SCUDO_CFLAGS -fbuiltin) -append_rtti_flag(OFF SCUDO_CFLAGS) - -set(SCUDO_MINIMAL_DYNAMIC_LIBS ${SANITIZER_COMMON_LINK_LIBS}) -append_list_if(COMPILER_RT_HAS_LIBDL dl SCUDO_MINIMAL_DYNAMIC_LIBS) -append_list_if(COMPILER_RT_HAS_LIBRT rt SCUDO_MINIMAL_DYNAMIC_LIBS) -append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread SCUDO_MINIMAL_DYNAMIC_LIBS) -append_list_if(COMPILER_RT_HAS_LIBLOG log SCUDO_MINIMAL_DYNAMIC_LIBS) -append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer - SCUDO_CFLAGS) - -set(SCUDO_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS}) -# Use gc-sections by default to avoid unused code being pulled in. -list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,--gc-sections) - -if(ANDROID) -# Put most Sanitizer shared libraries in the global group. For more details, see -# android-changes-for-ndk-developers.md#changes-to-library-search-order - if (COMPILER_RT_HAS_Z_GLOBAL) - list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,-z,global) - endif() -endif() - -# The minimal Scudo runtime does not inlude the UBSan runtime. -set(SCUDO_MINIMAL_OBJECT_LIBS - RTSanitizerCommonNoTermination - RTSanitizerCommonLibc - RTInterception) - -if (COMPILER_RT_HAS_GWP_ASAN) - # Currently, Scudo uses the GwpAsan flag parser. This backs onto the flag - # parsing mechanism of sanitizer_common. Once Scudo has its own flag parsing, - # and parses GwpAsan options, you can remove this dependency. - list(APPEND SCUDO_MINIMAL_OBJECT_LIBS RTGwpAsan RTGwpAsanOptionsParser - RTGwpAsanBacktraceLibc) - list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS) -endif() - -set(SCUDO_OBJECT_LIBS ${SCUDO_MINIMAL_OBJECT_LIBS}) -set(SCUDO_DYNAMIC_LIBS ${SCUDO_MINIMAL_DYNAMIC_LIBS}) - -if (FUCHSIA) - list(APPEND SCUDO_CFLAGS -nostdinc++) - list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -nostdlib++) -else() - list(APPEND SCUDO_DYNAMIC_LIBS ${SANITIZER_CXX_ABI_LIBRARIES}) - list(APPEND SCUDO_OBJECT_LIBS - RTSanitizerCommonCoverage - RTSanitizerCommonSymbolizer - RTUbsan) -endif() - -set(SCUDO_SOURCES - scudo_allocator.cpp - scudo_crc32.cpp - scudo_errors.cpp - scudo_flags.cpp - scudo_malloc.cpp - scudo_termination.cpp - scudo_tsd_exclusive.cpp - scudo_tsd_shared.cpp - scudo_utils.cpp) - -set(SCUDO_CXX_SOURCES - scudo_new_delete.cpp) - -set(SCUDO_HEADERS - scudo_allocator.h - scudo_allocator_combined.h - scudo_allocator_secondary.h - scudo_crc32.h - scudo_errors.h - scudo_flags.h - scudo_flags.inc - scudo_interface_internal.h - scudo_platform.h - scudo_tsd.h - scudo_tsd_exclusive.inc - scudo_tsd_shared.inc - scudo_utils.h) - -# Enable the SSE 4.2 instruction set for scudo_crc32.cpp, if available. -if (COMPILER_RT_HAS_MSSE4_2_FLAG) - set_source_files_properties(scudo_crc32.cpp PROPERTIES COMPILE_FLAGS -msse4.2) -endif() - -# Enable the AArch64 CRC32 feature for scudo_crc32.cpp, if available. -# Note that it is enabled by default starting with armv8.1-a. -if (COMPILER_RT_HAS_MCRC_FLAG) - set_source_files_properties(scudo_crc32.cpp PROPERTIES COMPILE_FLAGS -mcrc) -endif() - -if(COMPILER_RT_HAS_SCUDO) - add_compiler_rt_runtime(clang_rt.scudo_minimal - STATIC - ARCHS ${SCUDO_SUPPORTED_ARCH} - SOURCES ${SCUDO_SOURCES} - ADDITIONAL_HEADERS ${SCUDO_HEADERS} - OBJECT_LIBS ${SCUDO_MINIMAL_OBJECT_LIBS} - CFLAGS ${SCUDO_CFLAGS} - PARENT_TARGET scudo) - add_compiler_rt_runtime(clang_rt.scudo_cxx_minimal - STATIC - ARCHS ${SCUDO_SUPPORTED_ARCH} - SOURCES ${SCUDO_CXX_SOURCES} - ADDITIONAL_HEADERS ${SCUDO_HEADERS} - CFLAGS ${SCUDO_CFLAGS} - PARENT_TARGET scudo) - - add_compiler_rt_runtime(clang_rt.scudo - STATIC - ARCHS ${SCUDO_SUPPORTED_ARCH} - SOURCES ${SCUDO_SOURCES} - ADDITIONAL_HEADERS ${SCUDO_HEADERS} - OBJECT_LIBS ${SCUDO_OBJECT_LIBS} - CFLAGS ${SCUDO_CFLAGS} - PARENT_TARGET scudo) - add_compiler_rt_runtime(clang_rt.scudo_cxx - STATIC - ARCHS ${SCUDO_SUPPORTED_ARCH} - SOURCES ${SCUDO_CXX_SOURCES} - ADDITIONAL_HEADERS ${SCUDO_HEADERS} - OBJECT_LIBS RTUbsan_cxx - CFLAGS ${SCUDO_CFLAGS} - PARENT_TARGET scudo) - - add_compiler_rt_runtime(clang_rt.scudo_minimal - SHARED - ARCHS ${SCUDO_SUPPORTED_ARCH} - SOURCES ${SCUDO_SOURCES} ${SCUDO_CXX_SOURCES} - ADDITIONAL_HEADERS ${SCUDO_HEADERS} - OBJECT_LIBS ${SCUDO_MINIMAL_OBJECT_LIBS} - CFLAGS ${SCUDO_CFLAGS} - LINK_FLAGS ${SCUDO_DYNAMIC_LINK_FLAGS} - LINK_LIBS ${SCUDO_MINIMAL_DYNAMIC_LIBS} - PARENT_TARGET scudo) - - add_compiler_rt_runtime(clang_rt.scudo - SHARED - ARCHS ${SCUDO_SUPPORTED_ARCH} - SOURCES ${SCUDO_SOURCES} ${SCUDO_CXX_SOURCES} - ADDITIONAL_HEADERS ${SCUDO_HEADERS} - OBJECT_LIBS ${SCUDO_OBJECT_LIBS} - CFLAGS ${SCUDO_CFLAGS} - LINK_FLAGS ${SCUDO_DYNAMIC_LINK_FLAGS} - LINK_LIBS ${SCUDO_DYNAMIC_LIBS} - PARENT_TARGET scudo) -endif() diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator.cpp deleted file mode 100644 index b2ebc970593..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator.cpp +++ /dev/null @@ -1,820 +0,0 @@ -//===-- scudo_allocator.cpp -------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo Hardened Allocator implementation. -/// It uses the sanitizer_common allocator as a base and aims at mitigating -/// heap corruption vulnerabilities. It provides a checksum-guarded chunk -/// header, a delayed free list, and additional sanity checks. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_allocator.h" -#include "scudo_crc32.h" -#include "scudo_errors.h" -#include "scudo_flags.h" -#include "scudo_interface_internal.h" -#include "scudo_tsd.h" -#include "scudo_utils.h" - -#include "sanitizer_common/sanitizer_allocator_checks.h" -#include "sanitizer_common/sanitizer_allocator_interface.h" -#include "sanitizer_common/sanitizer_quarantine.h" - -#ifdef GWP_ASAN_HOOKS -# include "gwp_asan/guarded_pool_allocator.h" -# include "gwp_asan/optional/backtrace.h" -# include "gwp_asan/optional/options_parser.h" -#endif // GWP_ASAN_HOOKS - -#include <errno.h> -#include <string.h> - -namespace __scudo { - -// Global static cookie, initialized at start-up. -static u32 Cookie; - -// We default to software CRC32 if the alternatives are not supported, either -// at compilation or at runtime. -static atomic_uint8_t HashAlgorithm = { CRC32Software }; - -INLINE u32 computeCRC32(u32 Crc, uptr Value, uptr *Array, uptr ArraySize) { - // If the hardware CRC32 feature is defined here, it was enabled everywhere, - // as opposed to only for scudo_crc32.cpp. This means that other hardware - // specific instructions were likely emitted at other places, and as a - // result there is no reason to not use it here. -#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) - Crc = CRC32_INTRINSIC(Crc, Value); - for (uptr i = 0; i < ArraySize; i++) - Crc = CRC32_INTRINSIC(Crc, Array[i]); - return Crc; -#else - if (atomic_load_relaxed(&HashAlgorithm) == CRC32Hardware) { - Crc = computeHardwareCRC32(Crc, Value); - for (uptr i = 0; i < ArraySize; i++) - Crc = computeHardwareCRC32(Crc, Array[i]); - return Crc; - } - Crc = computeSoftwareCRC32(Crc, Value); - for (uptr i = 0; i < ArraySize; i++) - Crc = computeSoftwareCRC32(Crc, Array[i]); - return Crc; -#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) -} - -static BackendT &getBackend(); - -namespace Chunk { - static INLINE AtomicPackedHeader *getAtomicHeader(void *Ptr) { - return reinterpret_cast<AtomicPackedHeader *>(reinterpret_cast<uptr>(Ptr) - - getHeaderSize()); - } - static INLINE - const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) { - return reinterpret_cast<const AtomicPackedHeader *>( - reinterpret_cast<uptr>(Ptr) - getHeaderSize()); - } - - static INLINE bool isAligned(const void *Ptr) { - return IsAligned(reinterpret_cast<uptr>(Ptr), MinAlignment); - } - - // We can't use the offset member of the chunk itself, as we would double - // fetch it without any warranty that it wouldn't have been tampered. To - // prevent this, we work with a local copy of the header. - static INLINE void *getBackendPtr(const void *Ptr, UnpackedHeader *Header) { - return reinterpret_cast<void *>(reinterpret_cast<uptr>(Ptr) - - getHeaderSize() - (Header->Offset << MinAlignmentLog)); - } - - // Returns the usable size for a chunk, meaning the amount of bytes from the - // beginning of the user data to the end of the backend allocated chunk. - static INLINE uptr getUsableSize(const void *Ptr, UnpackedHeader *Header) { - const uptr ClassId = Header->ClassId; - if (ClassId) - return PrimaryT::ClassIdToSize(ClassId) - getHeaderSize() - - (Header->Offset << MinAlignmentLog); - return SecondaryT::GetActuallyAllocatedSize( - getBackendPtr(Ptr, Header)) - getHeaderSize(); - } - - // Returns the size the user requested when allocating the chunk. - static INLINE uptr getSize(const void *Ptr, UnpackedHeader *Header) { - const uptr SizeOrUnusedBytes = Header->SizeOrUnusedBytes; - if (Header->ClassId) - return SizeOrUnusedBytes; - return SecondaryT::GetActuallyAllocatedSize( - getBackendPtr(Ptr, Header)) - getHeaderSize() - SizeOrUnusedBytes; - } - - // Compute the checksum of the chunk pointer and its header. - static INLINE u16 computeChecksum(const void *Ptr, UnpackedHeader *Header) { - UnpackedHeader ZeroChecksumHeader = *Header; - ZeroChecksumHeader.Checksum = 0; - uptr HeaderHolder[sizeof(UnpackedHeader) / sizeof(uptr)]; - memcpy(&HeaderHolder, &ZeroChecksumHeader, sizeof(HeaderHolder)); - const u32 Crc = computeCRC32(Cookie, reinterpret_cast<uptr>(Ptr), - HeaderHolder, ARRAY_SIZE(HeaderHolder)); - return static_cast<u16>(Crc); - } - - // Checks the validity of a chunk by verifying its checksum. It doesn't - // incur termination in the event of an invalid chunk. - static INLINE bool isValid(const void *Ptr) { - PackedHeader NewPackedHeader = - atomic_load_relaxed(getConstAtomicHeader(Ptr)); - UnpackedHeader NewUnpackedHeader = - bit_cast<UnpackedHeader>(NewPackedHeader); - return (NewUnpackedHeader.Checksum == - computeChecksum(Ptr, &NewUnpackedHeader)); - } - - // Ensure that ChunkAvailable is 0, so that if a 0 checksum is ever valid - // for a fully nulled out header, its state will be available anyway. - COMPILER_CHECK(ChunkAvailable == 0); - - // Loads and unpacks the header, verifying the checksum in the process. - static INLINE - void loadHeader(const void *Ptr, UnpackedHeader *NewUnpackedHeader) { - PackedHeader NewPackedHeader = - atomic_load_relaxed(getConstAtomicHeader(Ptr)); - *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader); - if (UNLIKELY(NewUnpackedHeader->Checksum != - computeChecksum(Ptr, NewUnpackedHeader))) - dieWithMessage("corrupted chunk header at address %p\n", Ptr); - } - - // Packs and stores the header, computing the checksum in the process. - static INLINE void storeHeader(void *Ptr, UnpackedHeader *NewUnpackedHeader) { - NewUnpackedHeader->Checksum = computeChecksum(Ptr, NewUnpackedHeader); - PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader); - atomic_store_relaxed(getAtomicHeader(Ptr), NewPackedHeader); - } - - // Packs and stores the header, computing the checksum in the process. We - // compare the current header with the expected provided one to ensure that - // we are not being raced by a corruption occurring in another thread. - static INLINE void compareExchangeHeader(void *Ptr, - UnpackedHeader *NewUnpackedHeader, - UnpackedHeader *OldUnpackedHeader) { - NewUnpackedHeader->Checksum = computeChecksum(Ptr, NewUnpackedHeader); - PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader); - PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader); - if (UNLIKELY(!atomic_compare_exchange_strong( - getAtomicHeader(Ptr), &OldPackedHeader, NewPackedHeader, - memory_order_relaxed))) - dieWithMessage("race on chunk header at address %p\n", Ptr); - } -} // namespace Chunk - -struct QuarantineCallback { - explicit QuarantineCallback(AllocatorCacheT *Cache) - : Cache_(Cache) {} - - // Chunk recycling function, returns a quarantined chunk to the backend, - // first making sure it hasn't been tampered with. - void Recycle(void *Ptr) { - UnpackedHeader Header; - Chunk::loadHeader(Ptr, &Header); - if (UNLIKELY(Header.State != ChunkQuarantine)) - dieWithMessage("invalid chunk state when recycling address %p\n", Ptr); - UnpackedHeader NewHeader = Header; - NewHeader.State = ChunkAvailable; - Chunk::compareExchangeHeader(Ptr, &NewHeader, &Header); - void *BackendPtr = Chunk::getBackendPtr(Ptr, &Header); - if (Header.ClassId) - getBackend().deallocatePrimary(Cache_, BackendPtr, Header.ClassId); - else - getBackend().deallocateSecondary(BackendPtr); - } - - // Internal quarantine allocation and deallocation functions. We first check - // that the batches are indeed serviced by the Primary. - // TODO(kostyak): figure out the best way to protect the batches. - void *Allocate(uptr Size) { - const uptr BatchClassId = SizeClassMap::ClassID(sizeof(QuarantineBatch)); - return getBackend().allocatePrimary(Cache_, BatchClassId); - } - - void Deallocate(void *Ptr) { - const uptr BatchClassId = SizeClassMap::ClassID(sizeof(QuarantineBatch)); - getBackend().deallocatePrimary(Cache_, Ptr, BatchClassId); - } - - AllocatorCacheT *Cache_; - COMPILER_CHECK(sizeof(QuarantineBatch) < SizeClassMap::kMaxSize); -}; - -typedef Quarantine<QuarantineCallback, void> QuarantineT; -typedef QuarantineT::Cache QuarantineCacheT; -COMPILER_CHECK(sizeof(QuarantineCacheT) <= - sizeof(ScudoTSD::QuarantineCachePlaceHolder)); - -QuarantineCacheT *getQuarantineCache(ScudoTSD *TSD) { - return reinterpret_cast<QuarantineCacheT *>(TSD->QuarantineCachePlaceHolder); -} - -#ifdef GWP_ASAN_HOOKS -static gwp_asan::GuardedPoolAllocator GuardedAlloc; -#endif // GWP_ASAN_HOOKS - -struct Allocator { - static const uptr MaxAllowedMallocSize = - FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); - - BackendT Backend; - QuarantineT Quarantine; - - u32 QuarantineChunksUpToSize; - - bool DeallocationTypeMismatch; - bool ZeroContents; - bool DeleteSizeMismatch; - - bool CheckRssLimit; - uptr HardRssLimitMb; - uptr SoftRssLimitMb; - atomic_uint8_t RssLimitExceeded; - atomic_uint64_t RssLastCheckedAtNS; - - explicit Allocator(LinkerInitialized) - : Quarantine(LINKER_INITIALIZED) {} - - NOINLINE void performSanityChecks(); - - void init() { - SanitizerToolName = "Scudo"; - PrimaryAllocatorName = "ScudoPrimary"; - SecondaryAllocatorName = "ScudoSecondary"; - - initFlags(); - - performSanityChecks(); - - // Check if hardware CRC32 is supported in the binary and by the platform, - // if so, opt for the CRC32 hardware version of the checksum. - if (&computeHardwareCRC32 && hasHardwareCRC32()) - atomic_store_relaxed(&HashAlgorithm, CRC32Hardware); - - SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); - Backend.init(common_flags()->allocator_release_to_os_interval_ms); - HardRssLimitMb = common_flags()->hard_rss_limit_mb; - SoftRssLimitMb = common_flags()->soft_rss_limit_mb; - Quarantine.Init( - static_cast<uptr>(getFlags()->QuarantineSizeKb) << 10, - static_cast<uptr>(getFlags()->ThreadLocalQuarantineSizeKb) << 10); - QuarantineChunksUpToSize = (Quarantine.GetCacheSize() == 0) ? 0 : - getFlags()->QuarantineChunksUpToSize; - DeallocationTypeMismatch = getFlags()->DeallocationTypeMismatch; - DeleteSizeMismatch = getFlags()->DeleteSizeMismatch; - ZeroContents = getFlags()->ZeroContents; - - if (UNLIKELY(!GetRandom(reinterpret_cast<void *>(&Cookie), sizeof(Cookie), - /*blocking=*/false))) { - Cookie = static_cast<u32>((NanoTime() >> 12) ^ - (reinterpret_cast<uptr>(this) >> 4)); - } - - CheckRssLimit = HardRssLimitMb || SoftRssLimitMb; - if (CheckRssLimit) - atomic_store_relaxed(&RssLastCheckedAtNS, MonotonicNanoTime()); - } - - // Helper function that checks for a valid Scudo chunk. nullptr isn't. - bool isValidPointer(const void *Ptr) { - initThreadMaybe(); - if (UNLIKELY(!Ptr)) - return false; - if (!Chunk::isAligned(Ptr)) - return false; - return Chunk::isValid(Ptr); - } - - NOINLINE bool isRssLimitExceeded(); - - // Allocates a chunk. - void *allocate(uptr Size, uptr Alignment, AllocType Type, - bool ForceZeroContents = false) { - initThreadMaybe(); - -#ifdef GWP_ASAN_HOOKS - if (UNLIKELY(GuardedAlloc.shouldSample())) { - if (void *Ptr = GuardedAlloc.allocate(Size)) - return Ptr; - } -#endif // GWP_ASAN_HOOKS - - if (UNLIKELY(Alignment > MaxAlignment)) { - if (AllocatorMayReturnNull()) - return nullptr; - reportAllocationAlignmentTooBig(Alignment, MaxAlignment); - } - if (UNLIKELY(Alignment < MinAlignment)) - Alignment = MinAlignment; - - const uptr NeededSize = RoundUpTo(Size ? Size : 1, MinAlignment) + - Chunk::getHeaderSize(); - const uptr AlignedSize = (Alignment > MinAlignment) ? - NeededSize + (Alignment - Chunk::getHeaderSize()) : NeededSize; - if (UNLIKELY(Size >= MaxAllowedMallocSize) || - UNLIKELY(AlignedSize >= MaxAllowedMallocSize)) { - if (AllocatorMayReturnNull()) - return nullptr; - reportAllocationSizeTooBig(Size, AlignedSize, MaxAllowedMallocSize); - } - - if (CheckRssLimit && UNLIKELY(isRssLimitExceeded())) { - if (AllocatorMayReturnNull()) - return nullptr; - reportRssLimitExceeded(); - } - - // Primary and Secondary backed allocations have a different treatment. We - // deal with alignment requirements of Primary serviced allocations here, - // but the Secondary will take care of its own alignment needs. - void *BackendPtr; - uptr BackendSize; - u8 ClassId; - if (PrimaryT::CanAllocate(AlignedSize, MinAlignment)) { - BackendSize = AlignedSize; - ClassId = SizeClassMap::ClassID(BackendSize); - bool UnlockRequired; - ScudoTSD *TSD = getTSDAndLock(&UnlockRequired); - BackendPtr = Backend.allocatePrimary(&TSD->Cache, ClassId); - if (UnlockRequired) - TSD->unlock(); - } else { - BackendSize = NeededSize; - ClassId = 0; - BackendPtr = Backend.allocateSecondary(BackendSize, Alignment); - } - if (UNLIKELY(!BackendPtr)) { - SetAllocatorOutOfMemory(); - if (AllocatorMayReturnNull()) - return nullptr; - reportOutOfMemory(Size); - } - - // If requested, we will zero out the entire contents of the returned chunk. - if ((ForceZeroContents || ZeroContents) && ClassId) - memset(BackendPtr, 0, PrimaryT::ClassIdToSize(ClassId)); - - UnpackedHeader Header = {}; - uptr UserPtr = reinterpret_cast<uptr>(BackendPtr) + Chunk::getHeaderSize(); - if (UNLIKELY(!IsAligned(UserPtr, Alignment))) { - // Since the Secondary takes care of alignment, a non-aligned pointer - // means it is from the Primary. It is also the only case where the offset - // field of the header would be non-zero. - DCHECK(ClassId); - const uptr AlignedUserPtr = RoundUpTo(UserPtr, Alignment); - Header.Offset = (AlignedUserPtr - UserPtr) >> MinAlignmentLog; - UserPtr = AlignedUserPtr; - } - DCHECK_LE(UserPtr + Size, reinterpret_cast<uptr>(BackendPtr) + BackendSize); - Header.State = ChunkAllocated; - Header.AllocType = Type; - if (ClassId) { - Header.ClassId = ClassId; - Header.SizeOrUnusedBytes = Size; - } else { - // The secondary fits the allocations to a page, so the amount of unused - // bytes is the difference between the end of the user allocation and the - // next page boundary. - const uptr PageSize = GetPageSizeCached(); - const uptr TrailingBytes = (UserPtr + Size) & (PageSize - 1); - if (TrailingBytes) - Header.SizeOrUnusedBytes = PageSize - TrailingBytes; - } - void *Ptr = reinterpret_cast<void *>(UserPtr); - Chunk::storeHeader(Ptr, &Header); - if (SCUDO_CAN_USE_HOOKS && &__sanitizer_malloc_hook) - __sanitizer_malloc_hook(Ptr, Size); - return Ptr; - } - - // Place a chunk in the quarantine or directly deallocate it in the event of - // a zero-sized quarantine, or if the size of the chunk is greater than the - // quarantine chunk size threshold. - void quarantineOrDeallocateChunk(void *Ptr, UnpackedHeader *Header, - uptr Size) { - const bool BypassQuarantine = !Size || (Size > QuarantineChunksUpToSize); - if (BypassQuarantine) { - UnpackedHeader NewHeader = *Header; - NewHeader.State = ChunkAvailable; - Chunk::compareExchangeHeader(Ptr, &NewHeader, Header); - void *BackendPtr = Chunk::getBackendPtr(Ptr, Header); - if (Header->ClassId) { - bool UnlockRequired; - ScudoTSD *TSD = getTSDAndLock(&UnlockRequired); - getBackend().deallocatePrimary(&TSD->Cache, BackendPtr, - Header->ClassId); - if (UnlockRequired) - TSD->unlock(); - } else { - getBackend().deallocateSecondary(BackendPtr); - } - } else { - // If a small memory amount was allocated with a larger alignment, we want - // to take that into account. Otherwise the Quarantine would be filled - // with tiny chunks, taking a lot of VA memory. This is an approximation - // of the usable size, that allows us to not call - // GetActuallyAllocatedSize. - const uptr EstimatedSize = Size + (Header->Offset << MinAlignmentLog); - UnpackedHeader NewHeader = *Header; - NewHeader.State = ChunkQuarantine; - Chunk::compareExchangeHeader(Ptr, &NewHeader, Header); - bool UnlockRequired; - ScudoTSD *TSD = getTSDAndLock(&UnlockRequired); - Quarantine.Put(getQuarantineCache(TSD), QuarantineCallback(&TSD->Cache), - Ptr, EstimatedSize); - if (UnlockRequired) - TSD->unlock(); - } - } - - // Deallocates a Chunk, which means either adding it to the quarantine or - // directly returning it to the backend if criteria are met. - void deallocate(void *Ptr, uptr DeleteSize, uptr DeleteAlignment, - AllocType Type) { - // For a deallocation, we only ensure minimal initialization, meaning thread - // local data will be left uninitialized for now (when using ELF TLS). The - // fallback cache will be used instead. This is a workaround for a situation - // where the only heap operation performed in a thread would be a free past - // the TLS destructors, ending up in initialized thread specific data never - // being destroyed properly. Any other heap operation will do a full init. - initThreadMaybe(/*MinimalInit=*/true); - if (SCUDO_CAN_USE_HOOKS && &__sanitizer_free_hook) - __sanitizer_free_hook(Ptr); - if (UNLIKELY(!Ptr)) - return; - -#ifdef GWP_ASAN_HOOKS - if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) { - GuardedAlloc.deallocate(Ptr); - return; - } -#endif // GWP_ASAN_HOOKS - - if (UNLIKELY(!Chunk::isAligned(Ptr))) - dieWithMessage("misaligned pointer when deallocating address %p\n", Ptr); - UnpackedHeader Header; - Chunk::loadHeader(Ptr, &Header); - if (UNLIKELY(Header.State != ChunkAllocated)) - dieWithMessage("invalid chunk state when deallocating address %p\n", Ptr); - if (DeallocationTypeMismatch) { - // The deallocation type has to match the allocation one. - if (Header.AllocType != Type) { - // With the exception of memalign'd Chunks, that can be still be free'd. - if (Header.AllocType != FromMemalign || Type != FromMalloc) - dieWithMessage("allocation type mismatch when deallocating address " - "%p\n", Ptr); - } - } - const uptr Size = Chunk::getSize(Ptr, &Header); - if (DeleteSizeMismatch) { - if (DeleteSize && DeleteSize != Size) - dieWithMessage("invalid sized delete when deallocating address %p\n", - Ptr); - } - (void)DeleteAlignment; // TODO(kostyak): verify that the alignment matches. - quarantineOrDeallocateChunk(Ptr, &Header, Size); - } - - // Reallocates a chunk. We can save on a new allocation if the new requested - // size still fits in the chunk. - void *reallocate(void *OldPtr, uptr NewSize) { - initThreadMaybe(); - -#ifdef GWP_ASAN_HOOKS - if (UNLIKELY(GuardedAlloc.pointerIsMine(OldPtr))) { - size_t OldSize = GuardedAlloc.getSize(OldPtr); - void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); - if (NewPtr) - memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize); - GuardedAlloc.deallocate(OldPtr); - return NewPtr; - } -#endif // GWP_ASAN_HOOKS - - if (UNLIKELY(!Chunk::isAligned(OldPtr))) - dieWithMessage("misaligned address when reallocating address %p\n", - OldPtr); - UnpackedHeader OldHeader; - Chunk::loadHeader(OldPtr, &OldHeader); - if (UNLIKELY(OldHeader.State != ChunkAllocated)) - dieWithMessage("invalid chunk state when reallocating address %p\n", - OldPtr); - if (DeallocationTypeMismatch) { - if (UNLIKELY(OldHeader.AllocType != FromMalloc)) - dieWithMessage("allocation type mismatch when reallocating address " - "%p\n", OldPtr); - } - const uptr UsableSize = Chunk::getUsableSize(OldPtr, &OldHeader); - // The new size still fits in the current chunk, and the size difference - // is reasonable. - if (NewSize <= UsableSize && - (UsableSize - NewSize) < (SizeClassMap::kMaxSize / 2)) { - UnpackedHeader NewHeader = OldHeader; - NewHeader.SizeOrUnusedBytes = - OldHeader.ClassId ? NewSize : UsableSize - NewSize; - Chunk::compareExchangeHeader(OldPtr, &NewHeader, &OldHeader); - return OldPtr; - } - // Otherwise, we have to allocate a new chunk and copy the contents of the - // old one. - void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); - if (NewPtr) { - const uptr OldSize = OldHeader.ClassId ? OldHeader.SizeOrUnusedBytes : - UsableSize - OldHeader.SizeOrUnusedBytes; - memcpy(NewPtr, OldPtr, Min(NewSize, UsableSize)); - quarantineOrDeallocateChunk(OldPtr, &OldHeader, OldSize); - } - return NewPtr; - } - - // Helper function that returns the actual usable size of a chunk. - uptr getUsableSize(const void *Ptr) { - initThreadMaybe(); - if (UNLIKELY(!Ptr)) - return 0; - -#ifdef GWP_ASAN_HOOKS - if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) - return GuardedAlloc.getSize(Ptr); -#endif // GWP_ASAN_HOOKS - - UnpackedHeader Header; - Chunk::loadHeader(Ptr, &Header); - // Getting the usable size of a chunk only makes sense if it's allocated. - if (UNLIKELY(Header.State != ChunkAllocated)) - dieWithMessage("invalid chunk state when sizing address %p\n", Ptr); - return Chunk::getUsableSize(Ptr, &Header); - } - - void *calloc(uptr NMemB, uptr Size) { - initThreadMaybe(); - if (UNLIKELY(CheckForCallocOverflow(NMemB, Size))) { - if (AllocatorMayReturnNull()) - return nullptr; - reportCallocOverflow(NMemB, Size); - } - return allocate(NMemB * Size, MinAlignment, FromMalloc, true); - } - - void commitBack(ScudoTSD *TSD) { - Quarantine.Drain(getQuarantineCache(TSD), QuarantineCallback(&TSD->Cache)); - Backend.destroyCache(&TSD->Cache); - } - - uptr getStats(AllocatorStat StatType) { - initThreadMaybe(); - uptr stats[AllocatorStatCount]; - Backend.getStats(stats); - return stats[StatType]; - } - - bool canReturnNull() { - initThreadMaybe(); - return AllocatorMayReturnNull(); - } - - void setRssLimit(uptr LimitMb, bool HardLimit) { - if (HardLimit) - HardRssLimitMb = LimitMb; - else - SoftRssLimitMb = LimitMb; - CheckRssLimit = HardRssLimitMb || SoftRssLimitMb; - } - - void printStats() { - initThreadMaybe(); - Backend.printStats(); - } -}; - -NOINLINE void Allocator::performSanityChecks() { - // Verify that the header offset field can hold the maximum offset. In the - // case of the Secondary allocator, it takes care of alignment and the - // offset will always be 0. In the case of the Primary, the worst case - // scenario happens in the last size class, when the backend allocation - // would already be aligned on the requested alignment, which would happen - // to be the maximum alignment that would fit in that size class. As a - // result, the maximum offset will be at most the maximum alignment for the - // last size class minus the header size, in multiples of MinAlignment. - UnpackedHeader Header = {}; - const uptr MaxPrimaryAlignment = - 1 << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment); - const uptr MaxOffset = - (MaxPrimaryAlignment - Chunk::getHeaderSize()) >> MinAlignmentLog; - Header.Offset = MaxOffset; - if (Header.Offset != MaxOffset) - dieWithMessage("maximum possible offset doesn't fit in header\n"); - // Verify that we can fit the maximum size or amount of unused bytes in the - // header. Given that the Secondary fits the allocation to a page, the worst - // case scenario happens in the Primary. It will depend on the second to - // last and last class sizes, as well as the dynamic base for the Primary. - // The following is an over-approximation that works for our needs. - const uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1; - Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes; - if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes) - dieWithMessage("maximum possible unused bytes doesn't fit in header\n"); - - const uptr LargestClassId = SizeClassMap::kLargestClassID; - Header.ClassId = LargestClassId; - if (Header.ClassId != LargestClassId) - dieWithMessage("largest class ID doesn't fit in header\n"); -} - -// Opportunistic RSS limit check. This will update the RSS limit status, if -// it can, every 250ms, otherwise it will just return the current one. -NOINLINE bool Allocator::isRssLimitExceeded() { - u64 LastCheck = atomic_load_relaxed(&RssLastCheckedAtNS); - const u64 CurrentCheck = MonotonicNanoTime(); - if (LIKELY(CurrentCheck < LastCheck + (250ULL * 1000000ULL))) - return atomic_load_relaxed(&RssLimitExceeded); - if (!atomic_compare_exchange_weak(&RssLastCheckedAtNS, &LastCheck, - CurrentCheck, memory_order_relaxed)) - return atomic_load_relaxed(&RssLimitExceeded); - // TODO(kostyak): We currently use sanitizer_common's GetRSS which reads the - // RSS from /proc/self/statm by default. We might want to - // call getrusage directly, even if it's less accurate. - const uptr CurrentRssMb = GetRSS() >> 20; - if (HardRssLimitMb && UNLIKELY(HardRssLimitMb < CurrentRssMb)) - dieWithMessage("hard RSS limit exhausted (%zdMb vs %zdMb)\n", - HardRssLimitMb, CurrentRssMb); - if (SoftRssLimitMb) { - if (atomic_load_relaxed(&RssLimitExceeded)) { - if (CurrentRssMb <= SoftRssLimitMb) - atomic_store_relaxed(&RssLimitExceeded, false); - } else { - if (CurrentRssMb > SoftRssLimitMb) { - atomic_store_relaxed(&RssLimitExceeded, true); - Printf("Scudo INFO: soft RSS limit exhausted (%zdMb vs %zdMb)\n", - SoftRssLimitMb, CurrentRssMb); - } - } - } - return atomic_load_relaxed(&RssLimitExceeded); -} - -static Allocator Instance(LINKER_INITIALIZED); - -static BackendT &getBackend() { - return Instance.Backend; -} - -void initScudo() { - Instance.init(); -#ifdef GWP_ASAN_HOOKS - gwp_asan::options::initOptions(); - gwp_asan::options::Options &Opts = gwp_asan::options::getOptions(); - Opts.Backtrace = gwp_asan::options::getBacktraceFunction(); - Opts.PrintBacktrace = gwp_asan::options::getPrintBacktraceFunction(); - GuardedAlloc.init(Opts); -#endif // GWP_ASAN_HOOKS -} - -void ScudoTSD::init() { - getBackend().initCache(&Cache); - memset(QuarantineCachePlaceHolder, 0, sizeof(QuarantineCachePlaceHolder)); -} - -void ScudoTSD::commitBack() { - Instance.commitBack(this); -} - -void *scudoAllocate(uptr Size, uptr Alignment, AllocType Type) { - if (Alignment && UNLIKELY(!IsPowerOfTwo(Alignment))) { - errno = EINVAL; - if (Instance.canReturnNull()) - return nullptr; - reportAllocationAlignmentNotPowerOfTwo(Alignment); - } - return SetErrnoOnNull(Instance.allocate(Size, Alignment, Type)); -} - -void scudoDeallocate(void *Ptr, uptr Size, uptr Alignment, AllocType Type) { - Instance.deallocate(Ptr, Size, Alignment, Type); -} - -void *scudoRealloc(void *Ptr, uptr Size) { - if (!Ptr) - return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, FromMalloc)); - if (Size == 0) { - Instance.deallocate(Ptr, 0, 0, FromMalloc); - return nullptr; - } - return SetErrnoOnNull(Instance.reallocate(Ptr, Size)); -} - -void *scudoCalloc(uptr NMemB, uptr Size) { - return SetErrnoOnNull(Instance.calloc(NMemB, Size)); -} - -void *scudoValloc(uptr Size) { - return SetErrnoOnNull( - Instance.allocate(Size, GetPageSizeCached(), FromMemalign)); -} - -void *scudoPvalloc(uptr Size) { - const uptr PageSize = GetPageSizeCached(); - if (UNLIKELY(CheckForPvallocOverflow(Size, PageSize))) { - errno = ENOMEM; - if (Instance.canReturnNull()) - return nullptr; - reportPvallocOverflow(Size); - } - // pvalloc(0) should allocate one page. - Size = Size ? RoundUpTo(Size, PageSize) : PageSize; - return SetErrnoOnNull(Instance.allocate(Size, PageSize, FromMemalign)); -} - -int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { - if (UNLIKELY(!CheckPosixMemalignAlignment(Alignment))) { - if (!Instance.canReturnNull()) - reportInvalidPosixMemalignAlignment(Alignment); - return EINVAL; - } - void *Ptr = Instance.allocate(Size, Alignment, FromMemalign); - if (UNLIKELY(!Ptr)) - return ENOMEM; - *MemPtr = Ptr; - return 0; -} - -void *scudoAlignedAlloc(uptr Alignment, uptr Size) { - if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(Alignment, Size))) { - errno = EINVAL; - if (Instance.canReturnNull()) - return nullptr; - reportInvalidAlignedAllocAlignment(Size, Alignment); - } - return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMalloc)); -} - -uptr scudoMallocUsableSize(void *Ptr) { - return Instance.getUsableSize(Ptr); -} - -} // namespace __scudo - -using namespace __scudo; - -// MallocExtension helper functions - -uptr __sanitizer_get_current_allocated_bytes() { - return Instance.getStats(AllocatorStatAllocated); -} - -uptr __sanitizer_get_heap_size() { - return Instance.getStats(AllocatorStatMapped); -} - -uptr __sanitizer_get_free_bytes() { - return 1; -} - -uptr __sanitizer_get_unmapped_bytes() { - return 1; -} - -uptr __sanitizer_get_estimated_allocated_size(uptr Size) { - return Size; -} - -int __sanitizer_get_ownership(const void *Ptr) { - return Instance.isValidPointer(Ptr); -} - -uptr __sanitizer_get_allocated_size(const void *Ptr) { - return Instance.getUsableSize(Ptr); -} - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, - void *Ptr, uptr Size) { - (void)Ptr; - (void)Size; -} - -SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *Ptr) { - (void)Ptr; -} -#endif - -// Interface functions - -void __scudo_set_rss_limit(uptr LimitMb, s32 HardLimit) { - if (!SCUDO_CAN_USE_PUBLIC_INTERFACE) - return; - Instance.setRssLimit(LimitMb, !!HardLimit); -} - -void __scudo_print_stats() { - Instance.printStats(); -} diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator.h deleted file mode 100644 index 0efa5c52029..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator.h +++ /dev/null @@ -1,125 +0,0 @@ -//===-- scudo_allocator.h ---------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Header for scudo_allocator.cpp. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_ALLOCATOR_H_ -#define SCUDO_ALLOCATOR_H_ - -#include "scudo_platform.h" - -namespace __scudo { - -enum AllocType : u8 { - FromMalloc = 0, // Memory block came from malloc, realloc, calloc, etc. - FromNew = 1, // Memory block came from operator new. - FromNewArray = 2, // Memory block came from operator new []. - FromMemalign = 3, // Memory block came from memalign, posix_memalign, etc. -}; - -enum ChunkState : u8 { - ChunkAvailable = 0, - ChunkAllocated = 1, - ChunkQuarantine = 2 -}; - -// Our header requires 64 bits of storage. Having the offset saves us from -// using functions such as GetBlockBegin, that is fairly costly. Our first -// implementation used the MetaData as well, which offers the advantage of -// being stored away from the chunk itself, but accessing it was costly as -// well. The header will be atomically loaded and stored. -typedef u64 PackedHeader; -struct UnpackedHeader { - u64 Checksum : 16; - u64 ClassId : 8; - u64 SizeOrUnusedBytes : 20; // Size for Primary backed allocations, amount of - // unused bytes in the chunk for Secondary ones. - u64 State : 2; // available, allocated, or quarantined - u64 AllocType : 2; // malloc, new, new[], or memalign - u64 Offset : 16; // Offset from the beginning of the backend - // allocation to the beginning of the chunk - // itself, in multiples of MinAlignment. See - // comment about its maximum value and in init(). -}; - -typedef atomic_uint64_t AtomicPackedHeader; -COMPILER_CHECK(sizeof(UnpackedHeader) == sizeof(PackedHeader)); - -// Minimum alignment of 8 bytes for 32-bit, 16 for 64-bit -const uptr MinAlignmentLog = FIRST_32_SECOND_64(3, 4); -const uptr MaxAlignmentLog = 24; // 16 MB -const uptr MinAlignment = 1 << MinAlignmentLog; -const uptr MaxAlignment = 1 << MaxAlignmentLog; - -// constexpr version of __sanitizer::RoundUp without the extraneous CHECK. -// This way we can use it in constexpr variables and functions declarations. -constexpr uptr RoundUpTo(uptr Size, uptr Boundary) { - return (Size + Boundary - 1) & ~(Boundary - 1); -} - -namespace Chunk { - constexpr uptr getHeaderSize() { - return RoundUpTo(sizeof(PackedHeader), MinAlignment); - } -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -const uptr AllocatorSpace = ~0ULL; -struct AP64 { - static const uptr kSpaceBeg = AllocatorSpace; - static const uptr kSpaceSize = AllocatorSize; - static const uptr kMetadataSize = 0; - typedef __scudo::SizeClassMap SizeClassMap; - typedef NoOpMapUnmapCallback MapUnmapCallback; - static const uptr kFlags = - SizeClassAllocator64FlagMasks::kRandomShuffleChunks; - using AddressSpaceView = LocalAddressSpaceView; -}; -typedef SizeClassAllocator64<AP64> PrimaryT; -#else -struct AP32 { - static const uptr kSpaceBeg = 0; - static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE; - static const uptr kMetadataSize = 0; - typedef __scudo::SizeClassMap SizeClassMap; - static const uptr kRegionSizeLog = RegionSizeLog; - using AddressSpaceView = LocalAddressSpaceView; - typedef NoOpMapUnmapCallback MapUnmapCallback; - static const uptr kFlags = - SizeClassAllocator32FlagMasks::kRandomShuffleChunks | - SizeClassAllocator32FlagMasks::kUseSeparateSizeClassForBatch; -}; -typedef SizeClassAllocator32<AP32> PrimaryT; -#endif // SANITIZER_CAN_USE_ALLOCATOR64 - -#include "scudo_allocator_secondary.h" - -typedef LargeMmapAllocator SecondaryT; - -#include "scudo_allocator_combined.h" - -typedef CombinedAllocator BackendT; -typedef CombinedAllocator::AllocatorCache AllocatorCacheT; - -void initScudo(); - -void *scudoAllocate(uptr Size, uptr Alignment, AllocType Type); -void scudoDeallocate(void *Ptr, uptr Size, uptr Alignment, AllocType Type); -void *scudoRealloc(void *Ptr, uptr Size); -void *scudoCalloc(uptr NMemB, uptr Size); -void *scudoValloc(uptr Size); -void *scudoPvalloc(uptr Size); -int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size); -void *scudoAlignedAlloc(uptr Alignment, uptr Size); -uptr scudoMallocUsableSize(void *Ptr); - -} // namespace __scudo - -#endif // SCUDO_ALLOCATOR_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator_combined.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator_combined.h deleted file mode 100644 index d61cc9ec1a5..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator_combined.h +++ /dev/null @@ -1,75 +0,0 @@ -//===-- scudo_allocator_combined.h ------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo Combined Allocator, dispatches allocation & deallocation requests to -/// the Primary or the Secondary backend allocators. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_ALLOCATOR_COMBINED_H_ -#define SCUDO_ALLOCATOR_COMBINED_H_ - -#ifndef SCUDO_ALLOCATOR_H_ -# error "This file must be included inside scudo_allocator.h." -#endif - -class CombinedAllocator { - public: - using PrimaryAllocator = PrimaryT; - using SecondaryAllocator = SecondaryT; - using AllocatorCache = typename PrimaryAllocator::AllocatorCache; - void init(s32 ReleaseToOSIntervalMs) { - Primary.Init(ReleaseToOSIntervalMs); - Secondary.Init(); - Stats.Init(); - } - - // Primary allocations are always MinAlignment aligned, and as such do not - // require an Alignment parameter. - void *allocatePrimary(AllocatorCache *Cache, uptr ClassId) { - return Cache->Allocate(&Primary, ClassId); - } - - // Secondary allocations do not require a Cache, but do require an Alignment - // parameter. - void *allocateSecondary(uptr Size, uptr Alignment) { - return Secondary.Allocate(&Stats, Size, Alignment); - } - - void deallocatePrimary(AllocatorCache *Cache, void *Ptr, uptr ClassId) { - Cache->Deallocate(&Primary, ClassId, Ptr); - } - - void deallocateSecondary(void *Ptr) { - Secondary.Deallocate(&Stats, Ptr); - } - - void initCache(AllocatorCache *Cache) { - Cache->Init(&Stats); - } - - void destroyCache(AllocatorCache *Cache) { - Cache->Destroy(&Primary, &Stats); - } - - void getStats(AllocatorStatCounters StatType) const { - Stats.Get(StatType); - } - - void printStats() { - Primary.PrintStats(); - Secondary.PrintStats(); - } - - private: - PrimaryAllocator Primary; - SecondaryAllocator Secondary; - AllocatorGlobalStats Stats; -}; - -#endif // SCUDO_ALLOCATOR_COMBINED_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator_secondary.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator_secondary.h deleted file mode 100644 index 80198c4aebf..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_allocator_secondary.h +++ /dev/null @@ -1,192 +0,0 @@ -//===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo Secondary Allocator. -/// This services allocation that are too large to be serviced by the Primary -/// Allocator. It is directly backed by the memory mapping functions of the -/// operating system. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_ALLOCATOR_SECONDARY_H_ -#define SCUDO_ALLOCATOR_SECONDARY_H_ - -#ifndef SCUDO_ALLOCATOR_H_ -# error "This file must be included inside scudo_allocator.h." -#endif - -// Secondary backed allocations are standalone chunks that contain extra -// information stored in a LargeChunk::Header prior to the frontend's header. -// -// The secondary takes care of alignment requirements (so that it can release -// unnecessary pages in the rare event of larger alignments), and as such must -// know about the frontend's header size. -// -// Since Windows doesn't support partial releasing of a reserved memory region, -// we have to keep track of both the reserved and the committed memory. -// -// The resulting chunk resembles the following: -// -// +--------------------+ -// | Guard page(s) | -// +--------------------+ -// | Unused space* | -// +--------------------+ -// | LargeChunk::Header | -// +--------------------+ -// | {Unp,P}ackedHeader | -// +--------------------+ -// | Data (aligned) | -// +--------------------+ -// | Unused space** | -// +--------------------+ -// | Guard page(s) | -// +--------------------+ - -namespace LargeChunk { -struct Header { - ReservedAddressRange StoredRange; - uptr CommittedSize; - uptr Size; -}; -constexpr uptr getHeaderSize() { - return RoundUpTo(sizeof(Header), MinAlignment); -} -static Header *getHeader(uptr Ptr) { - return reinterpret_cast<Header *>(Ptr - getHeaderSize()); -} -static Header *getHeader(const void *Ptr) { - return getHeader(reinterpret_cast<uptr>(Ptr)); -} -} // namespace LargeChunk - -class LargeMmapAllocator { - public: - void Init() { - internal_memset(this, 0, sizeof(*this)); - } - - void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) { - const uptr UserSize = Size - Chunk::getHeaderSize(); - // The Scudo frontend prevents us from allocating more than - // MaxAllowedMallocSize, so integer overflow checks would be superfluous. - uptr ReservedSize = Size + LargeChunk::getHeaderSize(); - if (UNLIKELY(Alignment > MinAlignment)) - ReservedSize += Alignment; - const uptr PageSize = GetPageSizeCached(); - ReservedSize = RoundUpTo(ReservedSize, PageSize); - // Account for 2 guard pages, one before and one after the chunk. - ReservedSize += 2 * PageSize; - - ReservedAddressRange AddressRange; - uptr ReservedBeg = AddressRange.Init(ReservedSize, SecondaryAllocatorName); - if (UNLIKELY(ReservedBeg == ~static_cast<uptr>(0))) - return nullptr; - // A page-aligned pointer is assumed after that, so check it now. - DCHECK(IsAligned(ReservedBeg, PageSize)); - uptr ReservedEnd = ReservedBeg + ReservedSize; - // The beginning of the user area for that allocation comes after the - // initial guard page, and both headers. This is the pointer that has to - // abide by alignment requirements. - uptr CommittedBeg = ReservedBeg + PageSize; - uptr UserBeg = CommittedBeg + HeadersSize; - uptr UserEnd = UserBeg + UserSize; - uptr CommittedEnd = RoundUpTo(UserEnd, PageSize); - - // In the rare event of larger alignments, we will attempt to fit the mmap - // area better and unmap extraneous memory. This will also ensure that the - // offset and unused bytes field of the header stay small. - if (UNLIKELY(Alignment > MinAlignment)) { - if (!IsAligned(UserBeg, Alignment)) { - UserBeg = RoundUpTo(UserBeg, Alignment); - CommittedBeg = RoundDownTo(UserBeg - HeadersSize, PageSize); - const uptr NewReservedBeg = CommittedBeg - PageSize; - DCHECK_GE(NewReservedBeg, ReservedBeg); - if (!SANITIZER_WINDOWS && NewReservedBeg != ReservedBeg) { - AddressRange.Unmap(ReservedBeg, NewReservedBeg - ReservedBeg); - ReservedBeg = NewReservedBeg; - } - UserEnd = UserBeg + UserSize; - CommittedEnd = RoundUpTo(UserEnd, PageSize); - } - const uptr NewReservedEnd = CommittedEnd + PageSize; - DCHECK_LE(NewReservedEnd, ReservedEnd); - if (!SANITIZER_WINDOWS && NewReservedEnd != ReservedEnd) { - AddressRange.Unmap(NewReservedEnd, ReservedEnd - NewReservedEnd); - ReservedEnd = NewReservedEnd; - } - } - - DCHECK_LE(UserEnd, CommittedEnd); - const uptr CommittedSize = CommittedEnd - CommittedBeg; - // Actually mmap the memory, preserving the guard pages on either sides. - CHECK_EQ(CommittedBeg, AddressRange.Map(CommittedBeg, CommittedSize)); - const uptr Ptr = UserBeg - Chunk::getHeaderSize(); - LargeChunk::Header *H = LargeChunk::getHeader(Ptr); - H->StoredRange = AddressRange; - H->Size = CommittedEnd - Ptr; - H->CommittedSize = CommittedSize; - - // The primary adds the whole class size to the stats when allocating a - // chunk, so we will do something similar here. But we will not account for - // the guard pages. - { - SpinMutexLock l(&StatsMutex); - Stats->Add(AllocatorStatAllocated, CommittedSize); - Stats->Add(AllocatorStatMapped, CommittedSize); - AllocatedBytes += CommittedSize; - if (LargestSize < CommittedSize) - LargestSize = CommittedSize; - NumberOfAllocs++; - } - - return reinterpret_cast<void *>(Ptr); - } - - void Deallocate(AllocatorStats *Stats, void *Ptr) { - LargeChunk::Header *H = LargeChunk::getHeader(Ptr); - // Since we're unmapping the entirety of where the ReservedAddressRange - // actually is, copy onto the stack. - ReservedAddressRange AddressRange = H->StoredRange; - const uptr Size = H->CommittedSize; - { - SpinMutexLock l(&StatsMutex); - Stats->Sub(AllocatorStatAllocated, Size); - Stats->Sub(AllocatorStatMapped, Size); - FreedBytes += Size; - NumberOfFrees++; - } - AddressRange.Unmap(reinterpret_cast<uptr>(AddressRange.base()), - AddressRange.size()); - } - - static uptr GetActuallyAllocatedSize(void *Ptr) { - return LargeChunk::getHeader(Ptr)->Size; - } - - void PrintStats() { - Printf("Stats: LargeMmapAllocator: allocated %zd times (%zd K), " - "freed %zd times (%zd K), remains %zd (%zd K) max %zd M\n", - NumberOfAllocs, AllocatedBytes >> 10, NumberOfFrees, - FreedBytes >> 10, NumberOfAllocs - NumberOfFrees, - (AllocatedBytes - FreedBytes) >> 10, LargestSize >> 20); - } - - private: - static constexpr uptr HeadersSize = - LargeChunk::getHeaderSize() + Chunk::getHeaderSize(); - - StaticSpinMutex StatsMutex; - u32 NumberOfAllocs; - u32 NumberOfFrees; - uptr AllocatedBytes; - uptr FreedBytes; - uptr LargestSize; -}; - -#endif // SCUDO_ALLOCATOR_SECONDARY_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_crc32.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_crc32.cpp deleted file mode 100644 index 87473505fe7..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_crc32.cpp +++ /dev/null @@ -1,24 +0,0 @@ -//===-- scudo_crc32.cpp -----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// CRC32 function leveraging hardware specific instructions. This has to be -/// kept separated to restrict the use of compiler specific flags to this file. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_crc32.h" - -namespace __scudo { - -#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) -u32 computeHardwareCRC32(u32 Crc, uptr Data) { - return CRC32_INTRINSIC(Crc, Data); -} -#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) - -} // namespace __scudo diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_crc32.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_crc32.h deleted file mode 100644 index bad15a929a3..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_crc32.h +++ /dev/null @@ -1,100 +0,0 @@ -//===-- scudo_crc32.h -------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo chunk header checksum related definitions. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_CRC32_H_ -#define SCUDO_CRC32_H_ - -#include "sanitizer_common/sanitizer_internal_defs.h" - -// Hardware CRC32 is supported at compilation via the following: -// - for i386 & x86_64: -msse4.2 -// - for ARM & AArch64: -march=armv8-a+crc or -mcrc -// An additional check must be performed at runtime as well to make sure the -// emitted instructions are valid on the target host. - -#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) -# ifdef __SSE4_2__ -# include <smmintrin.h> -# define CRC32_INTRINSIC FIRST_32_SECOND_64(_mm_crc32_u32, _mm_crc32_u64) -# endif -# ifdef __ARM_FEATURE_CRC32 -# include <arm_acle.h> -# define CRC32_INTRINSIC FIRST_32_SECOND_64(__crc32cw, __crc32cd) -# endif -#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) - -namespace __scudo { - -enum : u8 { - CRC32Software = 0, - CRC32Hardware = 1, -}; - -static const u32 CRC32Table[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -INLINE u32 computeSoftwareCRC32(u32 Crc, uptr Data) { - for (uptr i = 0; i < sizeof(Data); i++) { - Crc = CRC32Table[(Crc ^ Data) & 0xff] ^ (Crc >> 8); - Data >>= 8; - } - return Crc; -} - -SANITIZER_WEAK_ATTRIBUTE u32 computeHardwareCRC32(u32 Crc, uptr Data); - -} // namespace __scudo - -#endif // SCUDO_CRC32_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_errors.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_errors.cpp deleted file mode 100644 index 4bea9ebc6ab..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_errors.cpp +++ /dev/null @@ -1,77 +0,0 @@ -//===-- scudo_errors.cpp ----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Verbose termination functions. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_utils.h" - -#include "sanitizer_common/sanitizer_flags.h" - -namespace __scudo { - -void NORETURN reportCallocOverflow(uptr Count, uptr Size) { - dieWithMessage("calloc parameters overflow: count * size (%zd * %zd) cannot " - "be represented with type size_t\n", Count, Size); -} - -void NORETURN reportPvallocOverflow(uptr Size) { - dieWithMessage("pvalloc parameters overflow: size 0x%zx rounded up to system " - "page size 0x%zx cannot be represented in type size_t\n", Size, - GetPageSizeCached()); -} - -void NORETURN reportAllocationAlignmentTooBig(uptr Alignment, - uptr MaxAlignment) { - dieWithMessage("invalid allocation alignment: %zd exceeds maximum supported " - "allocation of %zd\n", Alignment, MaxAlignment); -} - -void NORETURN reportAllocationAlignmentNotPowerOfTwo(uptr Alignment) { - dieWithMessage("invalid allocation alignment: %zd, alignment must be a power " - "of two\n", Alignment); -} - -void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) { - dieWithMessage( - "invalid alignment requested in posix_memalign: %zd, alignment" - " must be a power of two and a multiple of sizeof(void *) == %zd\n", - Alignment, sizeof(void *)); -} - -void NORETURN reportInvalidAlignedAllocAlignment(uptr Size, uptr Alignment) { -#if SANITIZER_POSIX - dieWithMessage("invalid alignment requested in aligned_alloc: %zd, alignment " - "must be a power of two and the requested size 0x%zx must be a multiple " - "of alignment\n", Alignment, Size); -#else - dieWithMessage("invalid alignment requested in aligned_alloc: %zd, the " - "requested size 0x%zx must be a multiple of alignment\n", Alignment, - Size); -#endif -} - -void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, - uptr MaxSize) { - dieWithMessage("requested allocation size 0x%zx (0x%zx after adjustments) " - "exceeds maximum supported size of 0x%zx\n", UserSize, TotalSize, - MaxSize); -} - -void NORETURN reportRssLimitExceeded() { - dieWithMessage("specified RSS limit exceeded, currently set to " - "soft_rss_limit_mb=%zd\n", common_flags()->soft_rss_limit_mb); -} - -void NORETURN reportOutOfMemory(uptr RequestedSize) { - dieWithMessage("allocator is out of memory trying to allocate 0x%zx bytes\n", - RequestedSize); -} - -} // namespace __scudo diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_errors.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_errors.h deleted file mode 100644 index 258695c2c02..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_errors.h +++ /dev/null @@ -1,34 +0,0 @@ -//===-- scudo_errors.h ------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Header for scudo_errors.cpp. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_ERRORS_H_ -#define SCUDO_ERRORS_H_ - -#include "sanitizer_common/sanitizer_internal_defs.h" - -namespace __scudo { - -void NORETURN reportCallocOverflow(uptr Count, uptr Size); -void NORETURN reportPvallocOverflow(uptr Size); -void NORETURN reportAllocationAlignmentTooBig(uptr Alignment, - uptr MaxAlignment); -void NORETURN reportAllocationAlignmentNotPowerOfTwo(uptr Alignment); -void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment); -void NORETURN reportInvalidAlignedAllocAlignment(uptr Size, uptr Alignment); -void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, - uptr MaxSize); -void NORETURN reportRssLimitExceeded(); -void NORETURN reportOutOfMemory(uptr RequestedSize); - -} // namespace __scudo - -#endif // SCUDO_ERRORS_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.cpp deleted file mode 100644 index c564e217b35..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.cpp +++ /dev/null @@ -1,136 +0,0 @@ -//===-- scudo_flags.cpp -----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Hardened Allocator flag parsing logic. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_flags.h" -#include "scudo_interface_internal.h" -#include "scudo_utils.h" - -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_flag_parser.h" - -namespace __scudo { - -static Flags ScudoFlags; // Use via getFlags(). - -void Flags::setDefaults() { -#define SCUDO_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; -#include "scudo_flags.inc" -#undef SCUDO_FLAG -} - -static void RegisterScudoFlags(FlagParser *parser, Flags *f) { -#define SCUDO_FLAG(Type, Name, DefaultValue, Description) \ - RegisterFlag(parser, #Name, Description, &f->Name); -#include "scudo_flags.inc" -#undef SCUDO_FLAG -} - -static const char *getCompileDefinitionScudoDefaultOptions() { -#ifdef SCUDO_DEFAULT_OPTIONS - return SANITIZER_STRINGIFY(SCUDO_DEFAULT_OPTIONS); -#else - return ""; -#endif -} - -static const char *getScudoDefaultOptions() { - return (&__scudo_default_options) ? __scudo_default_options() : ""; -} - -void initFlags() { - SetCommonFlagsDefaults(); - { - CommonFlags cf; - cf.CopyFrom(*common_flags()); - cf.exitcode = 1; - OverrideCommonFlags(cf); - } - Flags *f = getFlags(); - f->setDefaults(); - - FlagParser ScudoParser; - RegisterScudoFlags(&ScudoParser, f); - RegisterCommonFlags(&ScudoParser); - - // Override from compile definition. - ScudoParser.ParseString(getCompileDefinitionScudoDefaultOptions()); - - // Override from user-specified string. - ScudoParser.ParseString(getScudoDefaultOptions()); - - // Override from environment. - ScudoParser.ParseStringFromEnv("SCUDO_OPTIONS"); - - InitializeCommonFlags(); - - // Sanity checks and default settings for the Quarantine parameters. - - if (f->QuarantineSizeMb >= 0) { - // Backward compatible logic if QuarantineSizeMb is set. - if (f->QuarantineSizeKb >= 0) { - dieWithMessage("ERROR: please use either QuarantineSizeMb (deprecated) " - "or QuarantineSizeKb, but not both\n"); - } - if (f->QuarantineChunksUpToSize >= 0) { - dieWithMessage("ERROR: QuarantineChunksUpToSize cannot be used in " - " conjunction with the deprecated QuarantineSizeMb option\n"); - } - // If everything is in order, update QuarantineSizeKb accordingly. - f->QuarantineSizeKb = f->QuarantineSizeMb * 1024; - } else { - // Otherwise proceed with the new options. - if (f->QuarantineSizeKb < 0) { - const int DefaultQuarantineSizeKb = FIRST_32_SECOND_64(64, 256); - f->QuarantineSizeKb = DefaultQuarantineSizeKb; - } - if (f->QuarantineChunksUpToSize < 0) { - const int DefaultQuarantineChunksUpToSize = FIRST_32_SECOND_64(512, 2048); - f->QuarantineChunksUpToSize = DefaultQuarantineChunksUpToSize; - } - } - - // We enforce an upper limit for the chunk quarantine threshold of 4Mb. - if (f->QuarantineChunksUpToSize > (4 * 1024 * 1024)) { - dieWithMessage("ERROR: the chunk quarantine threshold is too large\n"); - } - - // We enforce an upper limit for the quarantine size of 32Mb. - if (f->QuarantineSizeKb > (32 * 1024)) { - dieWithMessage("ERROR: the quarantine size is too large\n"); - } - - if (f->ThreadLocalQuarantineSizeKb < 0) { - const int DefaultThreadLocalQuarantineSizeKb = FIRST_32_SECOND_64(16, 64); - f->ThreadLocalQuarantineSizeKb = DefaultThreadLocalQuarantineSizeKb; - } - // And an upper limit of 8Mb for the thread quarantine cache. - if (f->ThreadLocalQuarantineSizeKb > (8 * 1024)) { - dieWithMessage("ERROR: the per thread quarantine cache size is too " - "large\n"); - } - if (f->ThreadLocalQuarantineSizeKb == 0 && f->QuarantineSizeKb > 0) { - dieWithMessage("ERROR: ThreadLocalQuarantineSizeKb can be set to 0 only " - "when QuarantineSizeKb is set to 0\n"); - } -} - -Flags *getFlags() { - return &ScudoFlags; -} - -} // namespace __scudo - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -SANITIZER_INTERFACE_WEAK_DEF(const char*, __scudo_default_options, void) { - return ""; -} -#endif diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.h deleted file mode 100644 index 483c79621cb..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.h +++ /dev/null @@ -1,32 +0,0 @@ -//===-- scudo_flags.h -------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Header for scudo_flags.cpp. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_FLAGS_H_ -#define SCUDO_FLAGS_H_ - -namespace __scudo { - -struct Flags { -#define SCUDO_FLAG(Type, Name, DefaultValue, Description) Type Name; -#include "scudo_flags.inc" -#undef SCUDO_FLAG - - void setDefaults(); -}; - -Flags *getFlags(); - -void initFlags(); - -} // namespace __scudo - -#endif // SCUDO_FLAGS_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.inc b/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.inc deleted file mode 100644 index c124738c1f3..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_flags.inc +++ /dev/null @@ -1,48 +0,0 @@ -//===-- scudo_flags.inc -----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Hardened Allocator runtime flags. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_FLAG -# error "Define SCUDO_FLAG prior to including this file!" -#endif - -SCUDO_FLAG(int, QuarantineSizeMb, -1, - "Deprecated. Please use QuarantineSizeKb.") - -// Default value is set in scudo_flags.cpp based on architecture. -SCUDO_FLAG(int, QuarantineSizeKb, -1, - "Size in KB of quarantine used to delay the actual deallocation of " - "chunks. Lower value may reduce memory usage but decrease the " - "effectiveness of the mitigation. Defaults to 64KB (32-bit) or " - "256KB (64-bit)") - -// Default value is set in scudo_flags.cpp based on architecture. -SCUDO_FLAG(int, ThreadLocalQuarantineSizeKb, -1, - "Size in KB of per-thread cache used to offload the global " - "quarantine. Lower value may reduce memory usage but might increase " - "the contention on the global quarantine. Defaults to 16KB (32-bit) " - "or 64KB (64-bit)") - -// Default value is set in scudo_flags.cpp based on architecture. -SCUDO_FLAG(int, QuarantineChunksUpToSize, -1, - "Size in bytes up to which chunks will be quarantined (if lower than" - "or equal to). Defaults to 256 (32-bit) or 2048 (64-bit)") - -// Disable the deallocation type check by default on Android, it causes too many -// issues with third party libraries. -SCUDO_FLAG(bool, DeallocationTypeMismatch, !SANITIZER_ANDROID, - "Report errors on malloc/delete, new/free, new/delete[], etc.") - -SCUDO_FLAG(bool, DeleteSizeMismatch, true, - "Report errors on mismatch between size of new and delete.") - -SCUDO_FLAG(bool, ZeroContents, false, - "Zero chunk contents on allocation and deallocation.") diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_interface_internal.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_interface_internal.h deleted file mode 100644 index 75c63aa6d48..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_interface_internal.h +++ /dev/null @@ -1,32 +0,0 @@ -//===-- scudo_interface_internal.h ------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Private Scudo interface header. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_INTERFACE_INTERNAL_H_ -#define SCUDO_INTERFACE_INTERNAL_H_ - -#include "sanitizer_common/sanitizer_internal_defs.h" - -using __sanitizer::uptr; -using __sanitizer::s32; - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -const char* __scudo_default_options(); - -SANITIZER_INTERFACE_ATTRIBUTE -void __scudo_set_rss_limit(uptr LimitMb, s32 HardLimit); - -SANITIZER_INTERFACE_ATTRIBUTE -void __scudo_print_stats(); -} // extern "C" - -#endif // SCUDO_INTERFACE_INTERNAL_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_malloc.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_malloc.cpp deleted file mode 100644 index a72b861e28e..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_malloc.cpp +++ /dev/null @@ -1,84 +0,0 @@ -//===-- scudo_malloc.cpp ----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Interceptors for malloc related functions. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_allocator.h" - -#include "interception/interception.h" -#include "sanitizer_common/sanitizer_platform_interceptors.h" - -#include <stddef.h> - -using namespace __scudo; - -extern "C" { -INTERCEPTOR_ATTRIBUTE void free(void *ptr) { - scudoDeallocate(ptr, 0, 0, FromMalloc); -} - -INTERCEPTOR_ATTRIBUTE void *malloc(size_t size) { - return scudoAllocate(size, 0, FromMalloc); -} - -INTERCEPTOR_ATTRIBUTE void *realloc(void *ptr, size_t size) { - return scudoRealloc(ptr, size); -} - -INTERCEPTOR_ATTRIBUTE void *calloc(size_t nmemb, size_t size) { - return scudoCalloc(nmemb, size); -} - -INTERCEPTOR_ATTRIBUTE void *valloc(size_t size) { - return scudoValloc(size); -} - -INTERCEPTOR_ATTRIBUTE -int posix_memalign(void **memptr, size_t alignment, size_t size) { - return scudoPosixMemalign(memptr, alignment, size); -} - -#if SANITIZER_INTERCEPT_CFREE -INTERCEPTOR_ATTRIBUTE void cfree(void *ptr) ALIAS("free"); -#endif - -#if SANITIZER_INTERCEPT_MEMALIGN -INTERCEPTOR_ATTRIBUTE void *memalign(size_t alignment, size_t size) { - return scudoAllocate(size, alignment, FromMemalign); -} - -INTERCEPTOR_ATTRIBUTE -void *__libc_memalign(size_t alignment, size_t size) ALIAS("memalign"); -#endif - -#if SANITIZER_INTERCEPT_PVALLOC -INTERCEPTOR_ATTRIBUTE void *pvalloc(size_t size) { - return scudoPvalloc(size); -} -#endif - -#if SANITIZER_INTERCEPT_ALIGNED_ALLOC -INTERCEPTOR_ATTRIBUTE void *aligned_alloc(size_t alignment, size_t size) { - return scudoAlignedAlloc(alignment, size); -} -#endif - -#if SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE -INTERCEPTOR_ATTRIBUTE size_t malloc_usable_size(void *ptr) { - return scudoMallocUsableSize(ptr); -} -#endif - -#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO -INTERCEPTOR_ATTRIBUTE int mallopt(int cmd, int value) { - return 0; -} -#endif -} // extern "C" diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_new_delete.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_new_delete.cpp deleted file mode 100644 index 03eef7f28bb..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_new_delete.cpp +++ /dev/null @@ -1,107 +0,0 @@ -//===-- scudo_new_delete.cpp ------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Interceptors for operators new and delete. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_allocator.h" -#include "scudo_errors.h" - -#include "interception/interception.h" - -#include <stddef.h> - -using namespace __scudo; - -#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE - -// Fake std::nothrow_t to avoid including <new>. -namespace std { -struct nothrow_t {}; -enum class align_val_t: size_t {}; -} // namespace std - -// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. -#define OPERATOR_NEW_BODY_ALIGN(Type, Align, NoThrow) \ - void *Ptr = scudoAllocate(size, static_cast<uptr>(Align), Type); \ - if (!NoThrow && UNLIKELY(!Ptr)) reportOutOfMemory(size); \ - return Ptr; -#define OPERATOR_NEW_BODY(Type, NoThrow) \ - OPERATOR_NEW_BODY_ALIGN(Type, 0, NoThrow) - -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size) -{ OPERATOR_NEW_BODY(FromNew, /*NoThrow=*/false); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size) -{ OPERATOR_NEW_BODY(FromNewArray, /*NoThrow=*/false); } -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::nothrow_t const&) -{ OPERATOR_NEW_BODY(FromNew, /*NoThrow=*/true); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::nothrow_t const&) -{ OPERATOR_NEW_BODY(FromNewArray, /*NoThrow=*/true); } -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::align_val_t align) -{ OPERATOR_NEW_BODY_ALIGN(FromNew, align, /*NoThrow=*/false); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::align_val_t align) -{ OPERATOR_NEW_BODY_ALIGN(FromNewArray, align, /*NoThrow=*/false); } -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_NEW_BODY_ALIGN(FromNew, align, /*NoThrow=*/true); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_NEW_BODY_ALIGN(FromNewArray, align, /*NoThrow=*/true); } - -#define OPERATOR_DELETE_BODY(Type) \ - scudoDeallocate(ptr, 0, 0, Type); -#define OPERATOR_DELETE_BODY_SIZE(Type) \ - scudoDeallocate(ptr, size, 0, Type); -#define OPERATOR_DELETE_BODY_ALIGN(Type) \ - scudoDeallocate(ptr, 0, static_cast<uptr>(align), Type); -#define OPERATOR_DELETE_BODY_SIZE_ALIGN(Type) \ - scudoDeallocate(ptr, size, static_cast<uptr>(align), Type); - -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr) NOEXCEPT -{ OPERATOR_DELETE_BODY(FromNew); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr) NOEXCEPT -{ OPERATOR_DELETE_BODY(FromNewArray); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY(FromNew); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY(FromNewArray); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE(FromNew); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE(FromNewArray); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_ALIGN(FromNew); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_ALIGN(FromNewArray); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY_ALIGN(FromNew); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY_ALIGN(FromNewArray); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FromNew); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FromNewArray); } diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_platform.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_platform.h deleted file mode 100644 index 07d4b70fc8e..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_platform.h +++ /dev/null @@ -1,93 +0,0 @@ -//===-- scudo_platform.h ----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo platform specific definitions. -/// TODO(kostyak): add tests for the compile time defines. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_PLATFORM_H_ -#define SCUDO_PLATFORM_H_ - -#include "sanitizer_common/sanitizer_allocator.h" - -#if !SANITIZER_LINUX && !SANITIZER_FUCHSIA -# error "The Scudo hardened allocator is not supported on this platform." -#endif - -#define SCUDO_TSD_EXCLUSIVE_SUPPORTED (!SANITIZER_ANDROID && !SANITIZER_FUCHSIA) - -#ifndef SCUDO_TSD_EXCLUSIVE -// SCUDO_TSD_EXCLUSIVE wasn't defined, use a default TSD model for the platform. -# if SANITIZER_ANDROID || SANITIZER_FUCHSIA -// Android and Fuchsia use a pool of TSDs shared between threads. -# define SCUDO_TSD_EXCLUSIVE 0 -# elif SANITIZER_LINUX && !SANITIZER_ANDROID -// Non-Android Linux use an exclusive TSD per thread. -# define SCUDO_TSD_EXCLUSIVE 1 -# else -# error "No default TSD model defined for this platform." -# endif // SANITIZER_ANDROID || SANITIZER_FUCHSIA -#endif // SCUDO_TSD_EXCLUSIVE - -// If the exclusive TSD model is chosen, make sure the platform supports it. -#if SCUDO_TSD_EXCLUSIVE && !SCUDO_TSD_EXCLUSIVE_SUPPORTED -# error "The exclusive TSD model is not supported on this platform." -#endif - -// Maximum number of TSDs that can be created for the Shared model. -#ifndef SCUDO_SHARED_TSD_POOL_SIZE -# if SANITIZER_ANDROID -# define SCUDO_SHARED_TSD_POOL_SIZE 2U -# else -# define SCUDO_SHARED_TSD_POOL_SIZE 32U -# endif // SANITIZER_ANDROID -#endif // SCUDO_SHARED_TSD_POOL_SIZE - -// The following allows the public interface functions to be disabled. -#ifndef SCUDO_CAN_USE_PUBLIC_INTERFACE -# define SCUDO_CAN_USE_PUBLIC_INTERFACE 1 -#endif - -// Hooks in the allocation & deallocation paths can become a security concern if -// implemented improperly, or if overwritten by an attacker. Use with caution. -#ifndef SCUDO_CAN_USE_HOOKS -# if SANITIZER_FUCHSIA -# define SCUDO_CAN_USE_HOOKS 1 -# else -# define SCUDO_CAN_USE_HOOKS 0 -# endif // SANITIZER_FUCHSIA -#endif // SCUDO_CAN_USE_HOOKS - -namespace __scudo { - -#if SANITIZER_CAN_USE_ALLOCATOR64 -# if defined(__aarch64__) && SANITIZER_ANDROID -const uptr AllocatorSize = 0x4000000000ULL; // 256G. -# elif defined(__aarch64__) -const uptr AllocatorSize = 0x10000000000ULL; // 1T. -# else -const uptr AllocatorSize = 0x40000000000ULL; // 4T. -# endif -#else -const uptr RegionSizeLog = SANITIZER_ANDROID ? 19 : 20; -#endif // SANITIZER_CAN_USE_ALLOCATOR64 - -#if !defined(SCUDO_SIZE_CLASS_MAP) -# define SCUDO_SIZE_CLASS_MAP Dense -#endif - -#define SIZE_CLASS_MAP_TYPE SIZE_CLASS_MAP_TYPE_(SCUDO_SIZE_CLASS_MAP) -#define SIZE_CLASS_MAP_TYPE_(T) SIZE_CLASS_MAP_TYPE__(T) -#define SIZE_CLASS_MAP_TYPE__(T) T##SizeClassMap - -typedef SIZE_CLASS_MAP_TYPE SizeClassMap; - -} // namespace __scudo - -#endif // SCUDO_PLATFORM_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_termination.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_termination.cpp deleted file mode 100644 index 6c7c0abc6d3..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_termination.cpp +++ /dev/null @@ -1,41 +0,0 @@ -//===-- scudo_termination.cpp -----------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// This file contains bare-bones termination functions to replace the -/// __sanitizer ones, in order to avoid any potential abuse of the callbacks -/// functionality. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_utils.h" - -#include "sanitizer_common/sanitizer_common.h" - -namespace __sanitizer { - -bool AddDieCallback(DieCallbackType Callback) { return true; } - -bool RemoveDieCallback(DieCallbackType Callback) { return true; } - -void SetUserDieCallback(DieCallbackType Callback) {} - -void NORETURN Die() { - if (common_flags()->abort_on_error) - Abort(); - internal__exit(common_flags()->exitcode); -} - -void SetCheckFailedCallback(CheckFailedCallbackType callback) {} - -void NORETURN CheckFailed(const char *File, int Line, const char *Condition, - u64 Value1, u64 Value2) { - __scudo::dieWithMessage("CHECK failed at %s:%d %s (%lld, %lld)\n", - File, Line, Condition, Value1, Value2); -} - -} // namespace __sanitizer diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd.h deleted file mode 100644 index 1d4e4e6f126..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd.h +++ /dev/null @@ -1,65 +0,0 @@ -//===-- scudo_tsd.h ---------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo thread specific data definition. -/// Implementation will differ based on the thread local storage primitives -/// offered by the underlying platform. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_TSD_H_ -#define SCUDO_TSD_H_ - -#include "scudo_allocator.h" -#include "scudo_utils.h" - -#include <pthread.h> - -namespace __scudo { - -struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) ScudoTSD { - AllocatorCacheT Cache; - uptr QuarantineCachePlaceHolder[4]; - - void init(); - void commitBack(); - - INLINE bool tryLock() { - if (Mutex.TryLock()) { - atomic_store_relaxed(&Precedence, 0); - return true; - } - if (atomic_load_relaxed(&Precedence) == 0) - atomic_store_relaxed(&Precedence, static_cast<uptr>( - MonotonicNanoTime() >> FIRST_32_SECOND_64(16, 0))); - return false; - } - - INLINE void lock() { - atomic_store_relaxed(&Precedence, 0); - Mutex.Lock(); - } - - INLINE void unlock() { Mutex.Unlock(); } - - INLINE uptr getPrecedence() { return atomic_load_relaxed(&Precedence); } - - private: - StaticSpinMutex Mutex; - atomic_uintptr_t Precedence; -}; - -void initThread(bool MinimalInit); - -// TSD model specific fastpath functions definitions. -#include "scudo_tsd_exclusive.inc" -#include "scudo_tsd_shared.inc" - -} // namespace __scudo - -#endif // SCUDO_TSD_H_ diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_exclusive.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_exclusive.cpp deleted file mode 100644 index a203a74bbcf..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_exclusive.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//===-- scudo_tsd_exclusive.cpp ---------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo exclusive TSD implementation. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_tsd.h" - -#if SCUDO_TSD_EXCLUSIVE - -namespace __scudo { - -static pthread_once_t GlobalInitialized = PTHREAD_ONCE_INIT; -static pthread_key_t PThreadKey; - -__attribute__((tls_model("initial-exec"))) -THREADLOCAL ThreadState ScudoThreadState = ThreadNotInitialized; -__attribute__((tls_model("initial-exec"))) -THREADLOCAL ScudoTSD TSD; - -// Fallback TSD for when the thread isn't initialized yet or is torn down. It -// can be shared between multiple threads and as such must be locked. -ScudoTSD FallbackTSD; - -static void teardownThread(void *Ptr) { - uptr I = reinterpret_cast<uptr>(Ptr); - // The glibc POSIX thread-local-storage deallocation routine calls user - // provided destructors in a loop of PTHREAD_DESTRUCTOR_ITERATIONS. - // We want to be called last since other destructors might call free and the - // like, so we wait until PTHREAD_DESTRUCTOR_ITERATIONS before draining the - // quarantine and swallowing the cache. - if (I > 1) { - // If pthread_setspecific fails, we will go ahead with the teardown. - if (LIKELY(pthread_setspecific(PThreadKey, - reinterpret_cast<void *>(I - 1)) == 0)) - return; - } - TSD.commitBack(); - ScudoThreadState = ThreadTornDown; -} - - -static void initOnce() { - CHECK_EQ(pthread_key_create(&PThreadKey, teardownThread), 0); - initScudo(); - FallbackTSD.init(); -} - -void initThread(bool MinimalInit) { - CHECK_EQ(pthread_once(&GlobalInitialized, initOnce), 0); - if (UNLIKELY(MinimalInit)) - return; - CHECK_EQ(pthread_setspecific(PThreadKey, reinterpret_cast<void *>( - GetPthreadDestructorIterations())), 0); - TSD.init(); - ScudoThreadState = ThreadInitialized; -} - -} // namespace __scudo - -#endif // SCUDO_TSD_EXCLUSIVE diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_exclusive.inc b/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_exclusive.inc deleted file mode 100644 index 08e4d3af731..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_exclusive.inc +++ /dev/null @@ -1,47 +0,0 @@ -//===-- scudo_tsd_exclusive.inc ---------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo exclusive TSD fastpath functions implementation. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_TSD_H_ -# error "This file must be included inside scudo_tsd.h." -#endif // SCUDO_TSD_H_ - -#if SCUDO_TSD_EXCLUSIVE - -enum ThreadState : u8 { - ThreadNotInitialized = 0, - ThreadInitialized, - ThreadTornDown, -}; -__attribute__((tls_model("initial-exec"))) -extern THREADLOCAL ThreadState ScudoThreadState; -__attribute__((tls_model("initial-exec"))) -extern THREADLOCAL ScudoTSD TSD; - -extern ScudoTSD FallbackTSD; - -ALWAYS_INLINE void initThreadMaybe(bool MinimalInit = false) { - if (LIKELY(ScudoThreadState != ThreadNotInitialized)) - return; - initThread(MinimalInit); -} - -ALWAYS_INLINE ScudoTSD *getTSDAndLock(bool *UnlockRequired) { - if (UNLIKELY(ScudoThreadState != ThreadInitialized)) { - FallbackTSD.lock(); - *UnlockRequired = true; - return &FallbackTSD; - } - *UnlockRequired = false; - return &TSD; -} - -#endif // SCUDO_TSD_EXCLUSIVE diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_shared.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_shared.cpp deleted file mode 100644 index 59ad2549998..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_shared.cpp +++ /dev/null @@ -1,107 +0,0 @@ -//===-- scudo_tsd_shared.cpp ------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo shared TSD implementation. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_tsd.h" - -#if !SCUDO_TSD_EXCLUSIVE - -namespace __scudo { - -static pthread_once_t GlobalInitialized = PTHREAD_ONCE_INIT; -pthread_key_t PThreadKey; - -static atomic_uint32_t CurrentIndex; -static ScudoTSD *TSDs; -static u32 NumberOfTSDs; -static u32 CoPrimes[SCUDO_SHARED_TSD_POOL_SIZE]; -static u32 NumberOfCoPrimes = 0; - -#if SANITIZER_LINUX && !SANITIZER_ANDROID -__attribute__((tls_model("initial-exec"))) -THREADLOCAL ScudoTSD *CurrentTSD; -#endif - -static void initOnce() { - CHECK_EQ(pthread_key_create(&PThreadKey, NULL), 0); - initScudo(); - NumberOfTSDs = Min(Max(1U, GetNumberOfCPUsCached()), - static_cast<u32>(SCUDO_SHARED_TSD_POOL_SIZE)); - TSDs = reinterpret_cast<ScudoTSD *>( - MmapOrDie(sizeof(ScudoTSD) * NumberOfTSDs, "ScudoTSDs")); - for (u32 I = 0; I < NumberOfTSDs; I++) { - TSDs[I].init(); - u32 A = I + 1; - u32 B = NumberOfTSDs; - while (B != 0) { const u32 T = A; A = B; B = T % B; } - if (A == 1) - CoPrimes[NumberOfCoPrimes++] = I + 1; - } -} - -ALWAYS_INLINE void setCurrentTSD(ScudoTSD *TSD) { -#if SANITIZER_ANDROID - *get_android_tls_ptr() = reinterpret_cast<uptr>(TSD); -#elif SANITIZER_LINUX - CurrentTSD = TSD; -#else - CHECK_EQ(pthread_setspecific(PThreadKey, reinterpret_cast<void *>(TSD)), 0); -#endif // SANITIZER_ANDROID -} - -void initThread(bool MinimalInit) { - pthread_once(&GlobalInitialized, initOnce); - // Initial context assignment is done in a plain round-robin fashion. - u32 Index = atomic_fetch_add(&CurrentIndex, 1, memory_order_relaxed); - setCurrentTSD(&TSDs[Index % NumberOfTSDs]); -} - -ScudoTSD *getTSDAndLockSlow(ScudoTSD *TSD) { - if (NumberOfTSDs > 1) { - // Use the Precedence of the current TSD as our random seed. Since we are in - // the slow path, it means that tryLock failed, and as a result it's very - // likely that said Precedence is non-zero. - u32 RandState = static_cast<u32>(TSD->getPrecedence()); - const u32 R = Rand(&RandState); - const u32 Inc = CoPrimes[R % NumberOfCoPrimes]; - u32 Index = R % NumberOfTSDs; - uptr LowestPrecedence = UINTPTR_MAX; - ScudoTSD *CandidateTSD = nullptr; - // Go randomly through at most 4 contexts and find a candidate. - for (u32 I = 0; I < Min(4U, NumberOfTSDs); I++) { - if (TSDs[Index].tryLock()) { - setCurrentTSD(&TSDs[Index]); - return &TSDs[Index]; - } - const uptr Precedence = TSDs[Index].getPrecedence(); - // A 0 precedence here means another thread just locked this TSD. - if (Precedence && Precedence < LowestPrecedence) { - CandidateTSD = &TSDs[Index]; - LowestPrecedence = Precedence; - } - Index += Inc; - if (Index >= NumberOfTSDs) - Index -= NumberOfTSDs; - } - if (CandidateTSD) { - CandidateTSD->lock(); - setCurrentTSD(CandidateTSD); - return CandidateTSD; - } - } - // Last resort, stick with the current one. - TSD->lock(); - return TSD; -} - -} // namespace __scudo - -#endif // !SCUDO_TSD_EXCLUSIVE diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_shared.inc b/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_shared.inc deleted file mode 100644 index 8f3362dd3d7..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_tsd_shared.inc +++ /dev/null @@ -1,55 +0,0 @@ -//===-- scudo_tsd_shared.inc ------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Scudo shared TSD fastpath functions implementation. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_TSD_H_ -# error "This file must be included inside scudo_tsd.h." -#endif // SCUDO_TSD_H_ - -#if !SCUDO_TSD_EXCLUSIVE - -extern pthread_key_t PThreadKey; - -#if SANITIZER_LINUX && !SANITIZER_ANDROID -__attribute__((tls_model("initial-exec"))) -extern THREADLOCAL ScudoTSD *CurrentTSD; -#endif - -ALWAYS_INLINE ScudoTSD* getCurrentTSD() { -#if SANITIZER_ANDROID - return reinterpret_cast<ScudoTSD *>(*get_android_tls_ptr()); -#elif SANITIZER_LINUX - return CurrentTSD; -#else - return reinterpret_cast<ScudoTSD *>(pthread_getspecific(PThreadKey)); -#endif // SANITIZER_ANDROID -} - -ALWAYS_INLINE void initThreadMaybe(bool MinimalInit = false) { - if (LIKELY(getCurrentTSD())) - return; - initThread(MinimalInit); -} - -ScudoTSD *getTSDAndLockSlow(ScudoTSD *TSD); - -ALWAYS_INLINE ScudoTSD *getTSDAndLock(bool *UnlockRequired) { - ScudoTSD *TSD = getCurrentTSD(); - DCHECK(TSD && "No TSD associated with the current thread!"); - *UnlockRequired = true; - // Try to lock the currently associated context. - if (TSD->tryLock()) - return TSD; - // If it failed, go the slow path. - return getTSDAndLockSlow(TSD); -} - -#endif // !SCUDO_TSD_EXCLUSIVE diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_utils.cpp b/gnu/llvm/compiler-rt/lib/scudo/scudo_utils.cpp deleted file mode 100644 index 5e76a4a30f1..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_utils.cpp +++ /dev/null @@ -1,134 +0,0 @@ -//===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Platform specific utility functions. -/// -//===----------------------------------------------------------------------===// - -#include "scudo_utils.h" - -#if defined(__x86_64__) || defined(__i386__) -# include <cpuid.h> -#elif defined(__arm__) || defined(__aarch64__) -# include "sanitizer_common/sanitizer_getauxval.h" -# if SANITIZER_FUCHSIA -# include <zircon/syscalls.h> -# include <zircon/features.h> -# elif SANITIZER_POSIX -# include "sanitizer_common/sanitizer_posix.h" -# include <fcntl.h> -# endif -#endif - -#include <stdarg.h> - -// TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less -// complicated string formatting code. The following is a -// temporary workaround to be able to use __sanitizer::VSNPrintf. -namespace __sanitizer { - -extern int VSNPrintf(char *buff, int buff_length, const char *format, - va_list args); - -} // namespace __sanitizer - -namespace __scudo { - -FORMAT(1, 2) void NORETURN dieWithMessage(const char *Format, ...) { - static const char ScudoError[] = "Scudo ERROR: "; - static constexpr uptr PrefixSize = sizeof(ScudoError) - 1; - // Our messages are tiny, 256 characters is more than enough. - char Message[256]; - va_list Args; - va_start(Args, Format); - internal_memcpy(Message, ScudoError, PrefixSize); - VSNPrintf(Message + PrefixSize, sizeof(Message) - PrefixSize, Format, Args); - va_end(Args); - LogMessageOnPrintf(Message); - if (common_flags()->abort_on_error) - SetAbortMessage(Message); - RawWrite(Message); - Die(); -} - -#if defined(__x86_64__) || defined(__i386__) -// i386 and x86_64 specific code to detect CRC32 hardware support via CPUID. -// CRC32 requires the SSE 4.2 instruction set. -# ifndef bit_SSE4_2 -# define bit_SSE4_2 bit_SSE42 // clang and gcc have different defines. -# endif -bool hasHardwareCRC32() { - u32 Eax, Ebx, Ecx, Edx; - __get_cpuid(0, &Eax, &Ebx, &Ecx, &Edx); - const bool IsIntel = (Ebx == signature_INTEL_ebx) && - (Edx == signature_INTEL_edx) && - (Ecx == signature_INTEL_ecx); - const bool IsAMD = (Ebx == signature_AMD_ebx) && - (Edx == signature_AMD_edx) && - (Ecx == signature_AMD_ecx); - if (!IsIntel && !IsAMD) - return false; - __get_cpuid(1, &Eax, &Ebx, &Ecx, &Edx); - return !!(Ecx & bit_SSE4_2); -} -#elif defined(__arm__) || defined(__aarch64__) -// For ARM and AArch64, hardware CRC32 support is indicated in the AT_HWCAP -// auxiliary vector. -# ifndef AT_HWCAP -# define AT_HWCAP 16 -# endif -# ifndef HWCAP_CRC32 -# define HWCAP_CRC32 (1 << 7) // HWCAP_CRC32 is missing on older platforms. -# endif -# if SANITIZER_POSIX -bool hasHardwareCRC32ARMPosix() { - uptr F = internal_open("/proc/self/auxv", O_RDONLY); - if (internal_iserror(F)) - return false; - struct { uptr Tag; uptr Value; } Entry = { 0, 0 }; - for (;;) { - uptr N = internal_read(F, &Entry, sizeof(Entry)); - if (internal_iserror(N) || N != sizeof(Entry) || - (Entry.Tag == 0 && Entry.Value == 0) || Entry.Tag == AT_HWCAP) - break; - } - internal_close(F); - return (Entry.Tag == AT_HWCAP && (Entry.Value & HWCAP_CRC32) != 0); -} -# else -bool hasHardwareCRC32ARMPosix() { return false; } -# endif // SANITIZER_POSIX - -// Bionic doesn't initialize its globals early enough. This causes issues when -// trying to access them from a preinit_array (b/25751302) or from another -// constructor called before the libc one (b/68046352). __progname is -// initialized after the other globals, so we can check its value to know if -// calling getauxval is safe. -extern "C" SANITIZER_WEAK_ATTRIBUTE char *__progname; -INLINE bool areBionicGlobalsInitialized() { - return !SANITIZER_ANDROID || (&__progname && __progname); -} - -bool hasHardwareCRC32() { -#if SANITIZER_FUCHSIA - u32 HWCap; - zx_status_t Status = zx_system_get_features(ZX_FEATURE_KIND_CPU, &HWCap); - if (Status != ZX_OK || (HWCap & ZX_ARM64_FEATURE_ISA_CRC32) == 0) - return false; - return true; -#else - if (&getauxval && areBionicGlobalsInitialized()) - return !!(getauxval(AT_HWCAP) & HWCAP_CRC32); - return hasHardwareCRC32ARMPosix(); -#endif // SANITIZER_FUCHSIA -} -#else -bool hasHardwareCRC32() { return false; } -#endif // defined(__x86_64__) || defined(__i386__) - -} // namespace __scudo diff --git a/gnu/llvm/compiler-rt/lib/scudo/scudo_utils.h b/gnu/llvm/compiler-rt/lib/scudo/scudo_utils.h deleted file mode 100644 index a8dfbdeb3b7..00000000000 --- a/gnu/llvm/compiler-rt/lib/scudo/scudo_utils.h +++ /dev/null @@ -1,36 +0,0 @@ -//===-- scudo_utils.h -------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// Header for scudo_utils.cpp. -/// -//===----------------------------------------------------------------------===// - -#ifndef SCUDO_UTILS_H_ -#define SCUDO_UTILS_H_ - -#include "sanitizer_common/sanitizer_common.h" - -#include <string.h> - -namespace __scudo { - -template <class Dest, class Source> -INLINE Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), "Sizes are not equal!"); - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} - -void NORETURN dieWithMessage(const char *Format, ...); - -bool hasHardwareCRC32(); - -} // namespace __scudo - -#endif // SCUDO_UTILS_H_ diff --git a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_clock.cpp b/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_clock.cpp deleted file mode 100644 index 4b7aa0653da..00000000000 --- a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_clock.cpp +++ /dev/null @@ -1,597 +0,0 @@ -//===-- tsan_clock.cpp ----------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_clock.h" -#include "tsan_rtl.h" -#include "sanitizer_common/sanitizer_placement_new.h" - -// SyncClock and ThreadClock implement vector clocks for sync variables -// (mutexes, atomic variables, file descriptors, etc) and threads, respectively. -// ThreadClock contains fixed-size vector clock for maximum number of threads. -// SyncClock contains growable vector clock for currently necessary number of -// threads. -// Together they implement very simple model of operations, namely: -// -// void ThreadClock::acquire(const SyncClock *src) { -// for (int i = 0; i < kMaxThreads; i++) -// clock[i] = max(clock[i], src->clock[i]); -// } -// -// void ThreadClock::release(SyncClock *dst) const { -// for (int i = 0; i < kMaxThreads; i++) -// dst->clock[i] = max(dst->clock[i], clock[i]); -// } -// -// void ThreadClock::ReleaseStore(SyncClock *dst) const { -// for (int i = 0; i < kMaxThreads; i++) -// dst->clock[i] = clock[i]; -// } -// -// void ThreadClock::acq_rel(SyncClock *dst) { -// acquire(dst); -// release(dst); -// } -// -// Conformance to this model is extensively verified in tsan_clock_test.cpp. -// However, the implementation is significantly more complex. The complexity -// allows to implement important classes of use cases in O(1) instead of O(N). -// -// The use cases are: -// 1. Singleton/once atomic that has a single release-store operation followed -// by zillions of acquire-loads (the acquire-load is O(1)). -// 2. Thread-local mutex (both lock and unlock can be O(1)). -// 3. Leaf mutex (unlock is O(1)). -// 4. A mutex shared by 2 threads (both lock and unlock can be O(1)). -// 5. An atomic with a single writer (writes can be O(1)). -// The implementation dynamically adopts to workload. So if an atomic is in -// read-only phase, these reads will be O(1); if it later switches to read/write -// phase, the implementation will correctly handle that by switching to O(N). -// -// Thread-safety note: all const operations on SyncClock's are conducted under -// a shared lock; all non-const operations on SyncClock's are conducted under -// an exclusive lock; ThreadClock's are private to respective threads and so -// do not need any protection. -// -// Description of SyncClock state: -// clk_ - variable size vector clock, low kClkBits hold timestamp, -// the remaining bits hold "acquired" flag (the actual value is thread's -// reused counter); -// if acquried == thr->reused_, then the respective thread has already -// acquired this clock (except possibly for dirty elements). -// dirty_ - holds up to two indeces in the vector clock that other threads -// need to acquire regardless of "acquired" flag value; -// release_store_tid_ - denotes that the clock state is a result of -// release-store operation by the thread with release_store_tid_ index. -// release_store_reused_ - reuse count of release_store_tid_. - -// We don't have ThreadState in these methods, so this is an ugly hack that -// works only in C++. -#if !SANITIZER_GO -# define CPP_STAT_INC(typ) StatInc(cur_thread(), typ) -#else -# define CPP_STAT_INC(typ) (void)0 -#endif - -namespace __tsan { - -static atomic_uint32_t *ref_ptr(ClockBlock *cb) { - return reinterpret_cast<atomic_uint32_t *>(&cb->table[ClockBlock::kRefIdx]); -} - -// Drop reference to the first level block idx. -static void UnrefClockBlock(ClockCache *c, u32 idx, uptr blocks) { - ClockBlock *cb = ctx->clock_alloc.Map(idx); - atomic_uint32_t *ref = ref_ptr(cb); - u32 v = atomic_load(ref, memory_order_acquire); - for (;;) { - CHECK_GT(v, 0); - if (v == 1) - break; - if (atomic_compare_exchange_strong(ref, &v, v - 1, memory_order_acq_rel)) - return; - } - // First level block owns second level blocks, so them as well. - for (uptr i = 0; i < blocks; i++) - ctx->clock_alloc.Free(c, cb->table[ClockBlock::kBlockIdx - i]); - ctx->clock_alloc.Free(c, idx); -} - -ThreadClock::ThreadClock(unsigned tid, unsigned reused) - : tid_(tid) - , reused_(reused + 1) // 0 has special meaning - , cached_idx_() - , cached_size_() - , cached_blocks_() { - CHECK_LT(tid, kMaxTidInClock); - CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits); - nclk_ = tid_ + 1; - last_acquire_ = 0; - internal_memset(clk_, 0, sizeof(clk_)); -} - -void ThreadClock::ResetCached(ClockCache *c) { - if (cached_idx_) { - UnrefClockBlock(c, cached_idx_, cached_blocks_); - cached_idx_ = 0; - cached_size_ = 0; - cached_blocks_ = 0; - } -} - -void ThreadClock::acquire(ClockCache *c, SyncClock *src) { - DCHECK_LE(nclk_, kMaxTid); - DCHECK_LE(src->size_, kMaxTid); - CPP_STAT_INC(StatClockAcquire); - - // Check if it's empty -> no need to do anything. - const uptr nclk = src->size_; - if (nclk == 0) { - CPP_STAT_INC(StatClockAcquireEmpty); - return; - } - - bool acquired = false; - for (unsigned i = 0; i < kDirtyTids; i++) { - SyncClock::Dirty dirty = src->dirty_[i]; - unsigned tid = dirty.tid; - if (tid != kInvalidTid) { - if (clk_[tid] < dirty.epoch) { - clk_[tid] = dirty.epoch; - acquired = true; - } - } - } - - // Check if we've already acquired src after the last release operation on src - if (tid_ >= nclk || src->elem(tid_).reused != reused_) { - // O(N) acquire. - CPP_STAT_INC(StatClockAcquireFull); - nclk_ = max(nclk_, nclk); - u64 *dst_pos = &clk_[0]; - for (ClockElem &src_elem : *src) { - u64 epoch = src_elem.epoch; - if (*dst_pos < epoch) { - *dst_pos = epoch; - acquired = true; - } - dst_pos++; - } - - // Remember that this thread has acquired this clock. - if (nclk > tid_) - src->elem(tid_).reused = reused_; - } - - if (acquired) { - CPP_STAT_INC(StatClockAcquiredSomething); - last_acquire_ = clk_[tid_]; - ResetCached(c); - } -} - -void ThreadClock::release(ClockCache *c, SyncClock *dst) { - DCHECK_LE(nclk_, kMaxTid); - DCHECK_LE(dst->size_, kMaxTid); - - if (dst->size_ == 0) { - // ReleaseStore will correctly set release_store_tid_, - // which can be important for future operations. - ReleaseStore(c, dst); - return; - } - - CPP_STAT_INC(StatClockRelease); - // Check if we need to resize dst. - if (dst->size_ < nclk_) - dst->Resize(c, nclk_); - - // Check if we had not acquired anything from other threads - // since the last release on dst. If so, we need to update - // only dst->elem(tid_). - if (dst->elem(tid_).epoch > last_acquire_) { - UpdateCurrentThread(c, dst); - if (dst->release_store_tid_ != tid_ || - dst->release_store_reused_ != reused_) - dst->release_store_tid_ = kInvalidTid; - return; - } - - // O(N) release. - CPP_STAT_INC(StatClockReleaseFull); - dst->Unshare(c); - // First, remember whether we've acquired dst. - bool acquired = IsAlreadyAcquired(dst); - if (acquired) - CPP_STAT_INC(StatClockReleaseAcquired); - // Update dst->clk_. - dst->FlushDirty(); - uptr i = 0; - for (ClockElem &ce : *dst) { - ce.epoch = max(ce.epoch, clk_[i]); - ce.reused = 0; - i++; - } - // Clear 'acquired' flag in the remaining elements. - if (nclk_ < dst->size_) - CPP_STAT_INC(StatClockReleaseClearTail); - for (uptr i = nclk_; i < dst->size_; i++) - dst->elem(i).reused = 0; - dst->release_store_tid_ = kInvalidTid; - dst->release_store_reused_ = 0; - // If we've acquired dst, remember this fact, - // so that we don't need to acquire it on next acquire. - if (acquired) - dst->elem(tid_).reused = reused_; -} - -void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) { - DCHECK_LE(nclk_, kMaxTid); - DCHECK_LE(dst->size_, kMaxTid); - CPP_STAT_INC(StatClockStore); - - if (dst->size_ == 0 && cached_idx_ != 0) { - // Reuse the cached clock. - // Note: we could reuse/cache the cached clock in more cases: - // we could update the existing clock and cache it, or replace it with the - // currently cached clock and release the old one. And for a shared - // existing clock, we could replace it with the currently cached; - // or unshare, update and cache. But, for simplicity, we currnetly reuse - // cached clock only when the target clock is empty. - dst->tab_ = ctx->clock_alloc.Map(cached_idx_); - dst->tab_idx_ = cached_idx_; - dst->size_ = cached_size_; - dst->blocks_ = cached_blocks_; - CHECK_EQ(dst->dirty_[0].tid, kInvalidTid); - // The cached clock is shared (immutable), - // so this is where we store the current clock. - dst->dirty_[0].tid = tid_; - dst->dirty_[0].epoch = clk_[tid_]; - dst->release_store_tid_ = tid_; - dst->release_store_reused_ = reused_; - // Rememeber that we don't need to acquire it in future. - dst->elem(tid_).reused = reused_; - // Grab a reference. - atomic_fetch_add(ref_ptr(dst->tab_), 1, memory_order_relaxed); - return; - } - - // Check if we need to resize dst. - if (dst->size_ < nclk_) - dst->Resize(c, nclk_); - - if (dst->release_store_tid_ == tid_ && - dst->release_store_reused_ == reused_ && - dst->elem(tid_).epoch > last_acquire_) { - CPP_STAT_INC(StatClockStoreFast); - UpdateCurrentThread(c, dst); - return; - } - - // O(N) release-store. - CPP_STAT_INC(StatClockStoreFull); - dst->Unshare(c); - // Note: dst can be larger than this ThreadClock. - // This is fine since clk_ beyond size is all zeros. - uptr i = 0; - for (ClockElem &ce : *dst) { - ce.epoch = clk_[i]; - ce.reused = 0; - i++; - } - for (uptr i = 0; i < kDirtyTids; i++) - dst->dirty_[i].tid = kInvalidTid; - dst->release_store_tid_ = tid_; - dst->release_store_reused_ = reused_; - // Rememeber that we don't need to acquire it in future. - dst->elem(tid_).reused = reused_; - - // If the resulting clock is cachable, cache it for future release operations. - // The clock is always cachable if we released to an empty sync object. - if (cached_idx_ == 0 && dst->Cachable()) { - // Grab a reference to the ClockBlock. - atomic_uint32_t *ref = ref_ptr(dst->tab_); - if (atomic_load(ref, memory_order_acquire) == 1) - atomic_store_relaxed(ref, 2); - else - atomic_fetch_add(ref_ptr(dst->tab_), 1, memory_order_relaxed); - cached_idx_ = dst->tab_idx_; - cached_size_ = dst->size_; - cached_blocks_ = dst->blocks_; - } -} - -void ThreadClock::acq_rel(ClockCache *c, SyncClock *dst) { - CPP_STAT_INC(StatClockAcquireRelease); - acquire(c, dst); - ReleaseStore(c, dst); -} - -// Updates only single element related to the current thread in dst->clk_. -void ThreadClock::UpdateCurrentThread(ClockCache *c, SyncClock *dst) const { - // Update the threads time, but preserve 'acquired' flag. - for (unsigned i = 0; i < kDirtyTids; i++) { - SyncClock::Dirty *dirty = &dst->dirty_[i]; - const unsigned tid = dirty->tid; - if (tid == tid_ || tid == kInvalidTid) { - CPP_STAT_INC(StatClockReleaseFast); - dirty->tid = tid_; - dirty->epoch = clk_[tid_]; - return; - } - } - // Reset all 'acquired' flags, O(N). - // We are going to touch dst elements, so we need to unshare it. - dst->Unshare(c); - CPP_STAT_INC(StatClockReleaseSlow); - dst->elem(tid_).epoch = clk_[tid_]; - for (uptr i = 0; i < dst->size_; i++) - dst->elem(i).reused = 0; - dst->FlushDirty(); -} - -// Checks whether the current thread has already acquired src. -bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const { - if (src->elem(tid_).reused != reused_) - return false; - for (unsigned i = 0; i < kDirtyTids; i++) { - SyncClock::Dirty dirty = src->dirty_[i]; - if (dirty.tid != kInvalidTid) { - if (clk_[dirty.tid] < dirty.epoch) - return false; - } - } - return true; -} - -// Sets a single element in the vector clock. -// This function is called only from weird places like AcquireGlobal. -void ThreadClock::set(ClockCache *c, unsigned tid, u64 v) { - DCHECK_LT(tid, kMaxTid); - DCHECK_GE(v, clk_[tid]); - clk_[tid] = v; - if (nclk_ <= tid) - nclk_ = tid + 1; - last_acquire_ = clk_[tid_]; - ResetCached(c); -} - -void ThreadClock::DebugDump(int(*printf)(const char *s, ...)) { - printf("clock=["); - for (uptr i = 0; i < nclk_; i++) - printf("%s%llu", i == 0 ? "" : ",", clk_[i]); - printf("] tid=%u/%u last_acq=%llu", tid_, reused_, last_acquire_); -} - -SyncClock::SyncClock() { - ResetImpl(); -} - -SyncClock::~SyncClock() { - // Reset must be called before dtor. - CHECK_EQ(size_, 0); - CHECK_EQ(blocks_, 0); - CHECK_EQ(tab_, 0); - CHECK_EQ(tab_idx_, 0); -} - -void SyncClock::Reset(ClockCache *c) { - if (size_) - UnrefClockBlock(c, tab_idx_, blocks_); - ResetImpl(); -} - -void SyncClock::ResetImpl() { - tab_ = 0; - tab_idx_ = 0; - size_ = 0; - blocks_ = 0; - release_store_tid_ = kInvalidTid; - release_store_reused_ = 0; - for (uptr i = 0; i < kDirtyTids; i++) - dirty_[i].tid = kInvalidTid; -} - -void SyncClock::Resize(ClockCache *c, uptr nclk) { - CPP_STAT_INC(StatClockReleaseResize); - Unshare(c); - if (nclk <= capacity()) { - // Memory is already allocated, just increase the size. - size_ = nclk; - return; - } - if (size_ == 0) { - // Grow from 0 to one-level table. - CHECK_EQ(size_, 0); - CHECK_EQ(blocks_, 0); - CHECK_EQ(tab_, 0); - CHECK_EQ(tab_idx_, 0); - tab_idx_ = ctx->clock_alloc.Alloc(c); - tab_ = ctx->clock_alloc.Map(tab_idx_); - internal_memset(tab_, 0, sizeof(*tab_)); - atomic_store_relaxed(ref_ptr(tab_), 1); - size_ = 1; - } else if (size_ > blocks_ * ClockBlock::kClockCount) { - u32 idx = ctx->clock_alloc.Alloc(c); - ClockBlock *new_cb = ctx->clock_alloc.Map(idx); - uptr top = size_ - blocks_ * ClockBlock::kClockCount; - CHECK_LT(top, ClockBlock::kClockCount); - const uptr move = top * sizeof(tab_->clock[0]); - internal_memcpy(&new_cb->clock[0], tab_->clock, move); - internal_memset(&new_cb->clock[top], 0, sizeof(*new_cb) - move); - internal_memset(tab_->clock, 0, move); - append_block(idx); - } - // At this point we have first level table allocated and all clock elements - // are evacuated from it to a second level block. - // Add second level tables as necessary. - while (nclk > capacity()) { - u32 idx = ctx->clock_alloc.Alloc(c); - ClockBlock *cb = ctx->clock_alloc.Map(idx); - internal_memset(cb, 0, sizeof(*cb)); - append_block(idx); - } - size_ = nclk; -} - -// Flushes all dirty elements into the main clock array. -void SyncClock::FlushDirty() { - for (unsigned i = 0; i < kDirtyTids; i++) { - Dirty *dirty = &dirty_[i]; - if (dirty->tid != kInvalidTid) { - CHECK_LT(dirty->tid, size_); - elem(dirty->tid).epoch = dirty->epoch; - dirty->tid = kInvalidTid; - } - } -} - -bool SyncClock::IsShared() const { - if (size_ == 0) - return false; - atomic_uint32_t *ref = ref_ptr(tab_); - u32 v = atomic_load(ref, memory_order_acquire); - CHECK_GT(v, 0); - return v > 1; -} - -// Unshares the current clock if it's shared. -// Shared clocks are immutable, so they need to be unshared before any updates. -// Note: this does not apply to dirty entries as they are not shared. -void SyncClock::Unshare(ClockCache *c) { - if (!IsShared()) - return; - // First, copy current state into old. - SyncClock old; - old.tab_ = tab_; - old.tab_idx_ = tab_idx_; - old.size_ = size_; - old.blocks_ = blocks_; - old.release_store_tid_ = release_store_tid_; - old.release_store_reused_ = release_store_reused_; - for (unsigned i = 0; i < kDirtyTids; i++) - old.dirty_[i] = dirty_[i]; - // Then, clear current object. - ResetImpl(); - // Allocate brand new clock in the current object. - Resize(c, old.size_); - // Now copy state back into this object. - Iter old_iter(&old); - for (ClockElem &ce : *this) { - ce = *old_iter; - ++old_iter; - } - release_store_tid_ = old.release_store_tid_; - release_store_reused_ = old.release_store_reused_; - for (unsigned i = 0; i < kDirtyTids; i++) - dirty_[i] = old.dirty_[i]; - // Drop reference to old and delete if necessary. - old.Reset(c); -} - -// Can we cache this clock for future release operations? -ALWAYS_INLINE bool SyncClock::Cachable() const { - if (size_ == 0) - return false; - for (unsigned i = 0; i < kDirtyTids; i++) { - if (dirty_[i].tid != kInvalidTid) - return false; - } - return atomic_load_relaxed(ref_ptr(tab_)) == 1; -} - -// elem linearizes the two-level structure into linear array. -// Note: this is used only for one time accesses, vector operations use -// the iterator as it is much faster. -ALWAYS_INLINE ClockElem &SyncClock::elem(unsigned tid) const { - DCHECK_LT(tid, size_); - const uptr block = tid / ClockBlock::kClockCount; - DCHECK_LE(block, blocks_); - tid %= ClockBlock::kClockCount; - if (block == blocks_) - return tab_->clock[tid]; - u32 idx = get_block(block); - ClockBlock *cb = ctx->clock_alloc.Map(idx); - return cb->clock[tid]; -} - -ALWAYS_INLINE uptr SyncClock::capacity() const { - if (size_ == 0) - return 0; - uptr ratio = sizeof(ClockBlock::clock[0]) / sizeof(ClockBlock::table[0]); - // How many clock elements we can fit into the first level block. - // +1 for ref counter. - uptr top = ClockBlock::kClockCount - RoundUpTo(blocks_ + 1, ratio) / ratio; - return blocks_ * ClockBlock::kClockCount + top; -} - -ALWAYS_INLINE u32 SyncClock::get_block(uptr bi) const { - DCHECK(size_); - DCHECK_LT(bi, blocks_); - return tab_->table[ClockBlock::kBlockIdx - bi]; -} - -ALWAYS_INLINE void SyncClock::append_block(u32 idx) { - uptr bi = blocks_++; - CHECK_EQ(get_block(bi), 0); - tab_->table[ClockBlock::kBlockIdx - bi] = idx; -} - -// Used only by tests. -u64 SyncClock::get(unsigned tid) const { - for (unsigned i = 0; i < kDirtyTids; i++) { - Dirty dirty = dirty_[i]; - if (dirty.tid == tid) - return dirty.epoch; - } - return elem(tid).epoch; -} - -// Used only by Iter test. -u64 SyncClock::get_clean(unsigned tid) const { - return elem(tid).epoch; -} - -void SyncClock::DebugDump(int(*printf)(const char *s, ...)) { - printf("clock=["); - for (uptr i = 0; i < size_; i++) - printf("%s%llu", i == 0 ? "" : ",", elem(i).epoch); - printf("] reused=["); - for (uptr i = 0; i < size_; i++) - printf("%s%llu", i == 0 ? "" : ",", elem(i).reused); - printf("] release_store_tid=%d/%d dirty_tids=%d[%llu]/%d[%llu]", - release_store_tid_, release_store_reused_, - dirty_[0].tid, dirty_[0].epoch, - dirty_[1].tid, dirty_[1].epoch); -} - -void SyncClock::Iter::Next() { - // Finished with the current block, move on to the next one. - block_++; - if (block_ < parent_->blocks_) { - // Iterate over the next second level block. - u32 idx = parent_->get_block(block_); - ClockBlock *cb = ctx->clock_alloc.Map(idx); - pos_ = &cb->clock[0]; - end_ = pos_ + min(parent_->size_ - block_ * ClockBlock::kClockCount, - ClockBlock::kClockCount); - return; - } - if (block_ == parent_->blocks_ && - parent_->size_ > parent_->blocks_ * ClockBlock::kClockCount) { - // Iterate over elements in the first level block. - pos_ = &parent_->tab_->clock[0]; - end_ = pos_ + min(parent_->size_ - block_ * ClockBlock::kClockCount, - ClockBlock::kClockCount); - return; - } - parent_ = nullptr; // denotes end -} -} // namespace __tsan diff --git a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_clock.h b/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_clock.h deleted file mode 100644 index 6a1d15a2a16..00000000000 --- a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_clock.h +++ /dev/null @@ -1,225 +0,0 @@ -//===-- tsan_clock.h --------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#ifndef TSAN_CLOCK_H -#define TSAN_CLOCK_H - -#include "tsan_defs.h" -#include "tsan_dense_alloc.h" - -namespace __tsan { - -typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc; -typedef DenseSlabAllocCache ClockCache; - -// The clock that lives in sync variables (mutexes, atomics, etc). -class SyncClock { - public: - SyncClock(); - ~SyncClock(); - - uptr size() const; - - // These are used only in tests. - u64 get(unsigned tid) const; - u64 get_clean(unsigned tid) const; - - void Resize(ClockCache *c, uptr nclk); - void Reset(ClockCache *c); - - void DebugDump(int(*printf)(const char *s, ...)); - - // Clock element iterator. - // Note: it iterates only over the table without regard to dirty entries. - class Iter { - public: - explicit Iter(SyncClock* parent); - Iter& operator++(); - bool operator!=(const Iter& other); - ClockElem &operator*(); - - private: - SyncClock *parent_; - // [pos_, end_) is the current continuous range of clock elements. - ClockElem *pos_; - ClockElem *end_; - int block_; // Current number of second level block. - - NOINLINE void Next(); - }; - - Iter begin(); - Iter end(); - - private: - friend class ThreadClock; - friend class Iter; - static const uptr kDirtyTids = 2; - - struct Dirty { - u64 epoch : kClkBits; - u64 tid : 64 - kClkBits; // kInvalidId if not active - }; - - unsigned release_store_tid_; - unsigned release_store_reused_; - Dirty dirty_[kDirtyTids]; - // If size_ is 0, tab_ is nullptr. - // If size <= 64 (kClockCount), tab_ contains pointer to an array with - // 64 ClockElem's (ClockBlock::clock). - // Otherwise, tab_ points to an array with up to 127 u32 elements, - // each pointing to the second-level 512b block with 64 ClockElem's. - // Unused space in the first level ClockBlock is used to store additional - // clock elements. - // The last u32 element in the first level ClockBlock is always used as - // reference counter. - // - // See the following scheme for details. - // All memory blocks are 512 bytes (allocated from ClockAlloc). - // Clock (clk) elements are 64 bits. - // Idx and ref are 32 bits. - // - // tab_ - // | - // \/ - // +----------------------------------------------------+ - // | clk128 | clk129 | ...unused... | idx1 | idx0 | ref | - // +----------------------------------------------------+ - // | | - // | \/ - // | +----------------+ - // | | clk0 ... clk63 | - // | +----------------+ - // \/ - // +------------------+ - // | clk64 ... clk127 | - // +------------------+ - // - // Note: dirty entries, if active, always override what's stored in the clock. - ClockBlock *tab_; - u32 tab_idx_; - u16 size_; - u16 blocks_; // Number of second level blocks. - - void Unshare(ClockCache *c); - bool IsShared() const; - bool Cachable() const; - void ResetImpl(); - void FlushDirty(); - uptr capacity() const; - u32 get_block(uptr bi) const; - void append_block(u32 idx); - ClockElem &elem(unsigned tid) const; -}; - -// The clock that lives in threads. -class ThreadClock { - public: - typedef DenseSlabAllocCache Cache; - - explicit ThreadClock(unsigned tid, unsigned reused = 0); - - u64 get(unsigned tid) const; - void set(ClockCache *c, unsigned tid, u64 v); - void set(u64 v); - void tick(); - uptr size() const; - - void acquire(ClockCache *c, SyncClock *src); - void release(ClockCache *c, SyncClock *dst); - void acq_rel(ClockCache *c, SyncClock *dst); - void ReleaseStore(ClockCache *c, SyncClock *dst); - void ResetCached(ClockCache *c); - - void DebugReset(); - void DebugDump(int(*printf)(const char *s, ...)); - - private: - static const uptr kDirtyTids = SyncClock::kDirtyTids; - // Index of the thread associated with he clock ("current thread"). - const unsigned tid_; - const unsigned reused_; // tid_ reuse count. - // Current thread time when it acquired something from other threads. - u64 last_acquire_; - - // Cached SyncClock (without dirty entries and release_store_tid_). - // We reuse it for subsequent store-release operations without intervening - // acquire operations. Since it is shared (and thus constant), clock value - // for the current thread is then stored in dirty entries in the SyncClock. - // We host a refernece to the table while it is cached here. - u32 cached_idx_; - u16 cached_size_; - u16 cached_blocks_; - - // Number of active elements in the clk_ table (the rest is zeros). - uptr nclk_; - u64 clk_[kMaxTidInClock]; // Fixed size vector clock. - - bool IsAlreadyAcquired(const SyncClock *src) const; - void UpdateCurrentThread(ClockCache *c, SyncClock *dst) const; -}; - -ALWAYS_INLINE u64 ThreadClock::get(unsigned tid) const { - DCHECK_LT(tid, kMaxTidInClock); - return clk_[tid]; -} - -ALWAYS_INLINE void ThreadClock::set(u64 v) { - DCHECK_GE(v, clk_[tid_]); - clk_[tid_] = v; -} - -ALWAYS_INLINE void ThreadClock::tick() { - clk_[tid_]++; -} - -ALWAYS_INLINE uptr ThreadClock::size() const { - return nclk_; -} - -ALWAYS_INLINE SyncClock::Iter SyncClock::begin() { - return Iter(this); -} - -ALWAYS_INLINE SyncClock::Iter SyncClock::end() { - return Iter(nullptr); -} - -ALWAYS_INLINE uptr SyncClock::size() const { - return size_; -} - -ALWAYS_INLINE SyncClock::Iter::Iter(SyncClock* parent) - : parent_(parent) - , pos_(nullptr) - , end_(nullptr) - , block_(-1) { - if (parent) - Next(); -} - -ALWAYS_INLINE SyncClock::Iter& SyncClock::Iter::operator++() { - pos_++; - if (UNLIKELY(pos_ >= end_)) - Next(); - return *this; -} - -ALWAYS_INLINE bool SyncClock::Iter::operator!=(const SyncClock::Iter& other) { - return parent_ != other.parent_; -} - -ALWAYS_INLINE ClockElem &SyncClock::Iter::operator*() { - return *pos_; -} -} // namespace __tsan - -#endif // TSAN_CLOCK_H diff --git a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h b/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h deleted file mode 100644 index f955ddf9924..00000000000 --- a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h +++ /dev/null @@ -1,132 +0,0 @@ -//===-- tsan_interface_inl.h ------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// - -#include "tsan_interface.h" -#include "tsan_rtl.h" - -#define CALLERPC ((uptr)__builtin_return_address(0)) - -using namespace __tsan; - -void __tsan_read1(void *addr) { - MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); -} - -void __tsan_read2(void *addr) { - MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); -} - -void __tsan_read4(void *addr) { - MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); -} - -void __tsan_read8(void *addr) { - MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); -} - -void __tsan_write1(void *addr) { - MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); -} - -void __tsan_write2(void *addr) { - MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); -} - -void __tsan_write4(void *addr) { - MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); -} - -void __tsan_write8(void *addr) { - MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); -} - -void __tsan_read1_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1); -} - -void __tsan_read2_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2); -} - -void __tsan_read4_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4); -} - -void __tsan_read8_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); -} - -void __tsan_write1_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1); -} - -void __tsan_write2_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2); -} - -void __tsan_write4_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4); -} - -void __tsan_write8_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); -} - -void __tsan_vptr_update(void **vptr_p, void *new_val) { - CHECK_EQ(sizeof(vptr_p), 8); - if (*vptr_p != new_val) { - ThreadState *thr = cur_thread(); - thr->is_vptr_access = true; - MemoryWrite(thr, CALLERPC, (uptr)vptr_p, kSizeLog8); - thr->is_vptr_access = false; - } -} - -void __tsan_vptr_read(void **vptr_p) { - CHECK_EQ(sizeof(vptr_p), 8); - ThreadState *thr = cur_thread(); - thr->is_vptr_access = true; - MemoryRead(thr, CALLERPC, (uptr)vptr_p, kSizeLog8); - thr->is_vptr_access = false; -} - -void __tsan_func_entry(void *pc) { - FuncEntry(cur_thread(), (uptr)pc); -} - -void __tsan_func_exit() { - FuncExit(cur_thread()); -} - -void __tsan_ignore_thread_begin() { - ThreadIgnoreBegin(cur_thread(), CALLERPC); -} - -void __tsan_ignore_thread_end() { - ThreadIgnoreEnd(cur_thread(), CALLERPC); -} - -void __tsan_read_range(void *addr, uptr size) { - MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, false); -} - -void __tsan_write_range(void *addr, uptr size) { - MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, true); -} - -void __tsan_read_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), (uptr)pc, (uptr)addr, size, false); -} - -void __tsan_write_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), (uptr)pc, (uptr)addr, size, true); -} diff --git a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_update_shadow_word_inl.h deleted file mode 100644 index 056c3aa2032..00000000000 --- a/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_update_shadow_word_inl.h +++ /dev/null @@ -1,69 +0,0 @@ -//===-- tsan_update_shadow_word_inl.h ---------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -// Body of the hottest inner loop. -// If we wrap this body into a function, compilers (both gcc and clang) -// produce sligtly less efficient code. -//===----------------------------------------------------------------------===// -do { - StatInc(thr, StatShadowProcessed); - const unsigned kAccessSize = 1 << kAccessSizeLog; - u64 *sp = &shadow_mem[idx]; - old = LoadShadow(sp); - if (LIKELY(old.IsZero())) { - StatInc(thr, StatShadowZero); - if (!stored) { - StoreIfNotYetStored(sp, &store_word); - stored = true; - } - break; - } - // is the memory access equal to the previous? - if (LIKELY(Shadow::Addr0AndSizeAreEqual(cur, old))) { - StatInc(thr, StatShadowSameSize); - // same thread? - if (LIKELY(Shadow::TidsAreEqual(old, cur))) { - StatInc(thr, StatShadowSameThread); - if (LIKELY(old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))) { - StoreIfNotYetStored(sp, &store_word); - stored = true; - } - break; - } - StatInc(thr, StatShadowAnotherThread); - if (HappensBefore(old, thr)) { - if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) { - StoreIfNotYetStored(sp, &store_word); - stored = true; - } - break; - } - if (LIKELY(old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))) - break; - goto RACE; - } - // Do the memory access intersect? - if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { - StatInc(thr, StatShadowIntersect); - if (Shadow::TidsAreEqual(old, cur)) { - StatInc(thr, StatShadowSameThread); - break; - } - StatInc(thr, StatShadowAnotherThread); - if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) - break; - if (LIKELY(HappensBefore(old, thr))) - break; - goto RACE; - } - // The accesses do not intersect. - StatInc(thr, StatShadowNotIntersect); - break; -} while (0); diff --git a/gnu/llvm/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp b/gnu/llvm/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp deleted file mode 100644 index 6d835ba85c3..00000000000 --- a/gnu/llvm/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cpp +++ /dev/null @@ -1,493 +0,0 @@ -//===-- tsan_clock_test.cpp -----------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_clock.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include <sys/time.h> -#include <time.h> - -namespace __tsan { - -ClockCache cache; - -TEST(Clock, VectorBasic) { - ThreadClock clk(0); - ASSERT_EQ(clk.size(), 1U); - clk.tick(); - ASSERT_EQ(clk.size(), 1U); - ASSERT_EQ(clk.get(0), 1U); - clk.set(&cache, 3, clk.get(3) + 1); - ASSERT_EQ(clk.size(), 4U); - ASSERT_EQ(clk.get(0), 1U); - ASSERT_EQ(clk.get(1), 0U); - ASSERT_EQ(clk.get(2), 0U); - ASSERT_EQ(clk.get(3), 1U); - clk.set(&cache, 3, clk.get(3) + 1); - ASSERT_EQ(clk.get(3), 2U); -} - -TEST(Clock, ChunkedBasic) { - ThreadClock vector(0); - SyncClock chunked; - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 0U); - vector.acquire(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 0U); - vector.release(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 1U); - vector.acq_rel(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 1U); - chunked.Reset(&cache); -} - -static const uptr interesting_sizes[] = {0, 1, 2, 30, 61, 62, 63, 64, 65, 66, - 100, 124, 125, 126, 127, 128, 129, 130, 188, 189, 190, 191, 192, 193, 254, - 255}; - -TEST(Clock, Iter) { - const uptr n = ARRAY_SIZE(interesting_sizes); - for (uptr fi = 0; fi < n; fi++) { - const uptr size = interesting_sizes[fi]; - SyncClock sync; - ThreadClock vector(0); - for (uptr i = 0; i < size; i++) - vector.set(&cache, i, i + 1); - if (size != 0) - vector.release(&cache, &sync); - uptr i = 0; - for (ClockElem &ce : sync) { - ASSERT_LT(i, size); - ASSERT_EQ(sync.get_clean(i), ce.epoch); - i++; - } - ASSERT_EQ(i, size); - sync.Reset(&cache); - } -} - -TEST(Clock, AcquireRelease) { - ThreadClock vector1(100); - vector1.tick(); - SyncClock chunked; - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 101U); - ThreadClock vector2(0); - vector2.acquire(&cache, &chunked); - ASSERT_EQ(vector2.size(), 101U); - ASSERT_EQ(vector2.get(0), 0U); - ASSERT_EQ(vector2.get(1), 0U); - ASSERT_EQ(vector2.get(99), 0U); - ASSERT_EQ(vector2.get(100), 1U); - chunked.Reset(&cache); -} - -TEST(Clock, RepeatedAcquire) { - ThreadClock thr1(1); - thr1.tick(); - ThreadClock thr2(2); - thr2.tick(); - - SyncClock sync; - thr1.ReleaseStore(&cache, &sync); - - thr2.acquire(&cache, &sync); - thr2.acquire(&cache, &sync); - - sync.Reset(&cache); -} - -TEST(Clock, ManyThreads) { - SyncClock chunked; - for (unsigned i = 0; i < 200; i++) { - ThreadClock vector(0); - vector.tick(); - vector.set(&cache, i, i + 1); - vector.release(&cache, &chunked); - ASSERT_EQ(i + 1, chunked.size()); - vector.acquire(&cache, &chunked); - ASSERT_EQ(i + 1, vector.size()); - } - - for (unsigned i = 0; i < 200; i++) { - printf("i=%d\n", i); - ASSERT_EQ(i + 1, chunked.get(i)); - } - - ThreadClock vector(1); - vector.acquire(&cache, &chunked); - ASSERT_EQ(200U, vector.size()); - for (unsigned i = 0; i < 200; i++) - ASSERT_EQ(i + 1, vector.get(i)); - - chunked.Reset(&cache); -} - -TEST(Clock, DifferentSizes) { - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(20); - vector2.tick(); - { - SyncClock chunked; - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 11U); - vector2.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector2.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector1.release(&cache, &chunked); - vector2.acquire(&cache, &chunked); - ASSERT_EQ(vector2.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector2.release(&cache, &chunked); - vector1.acquire(&cache, &chunked); - ASSERT_EQ(vector1.size(), 21U); - chunked.Reset(&cache); - } - } -} - -TEST(Clock, Growth) { - { - ThreadClock vector(10); - vector.tick(); - vector.set(&cache, 5, 42); - SyncClock sync; - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), 11U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(1), 0ULL); - ASSERT_EQ(sync.get(5), 42ULL); - ASSERT_EQ(sync.get(9), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(20); - vector2.tick(); - SyncClock sync; - vector1.release(&cache, &sync); - vector2.release(&cache, &sync); - ASSERT_EQ(sync.size(), 21U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - ASSERT_EQ(sync.get(19), 0ULL); - ASSERT_EQ(sync.get(20), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector(100); - vector.tick(); - vector.set(&cache, 5, 42); - vector.set(&cache, 90, 84); - SyncClock sync; - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), 101U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(1), 0ULL); - ASSERT_EQ(sync.get(5), 42ULL); - ASSERT_EQ(sync.get(60), 0ULL); - ASSERT_EQ(sync.get(70), 0ULL); - ASSERT_EQ(sync.get(90), 84ULL); - ASSERT_EQ(sync.get(99), 0ULL); - ASSERT_EQ(sync.get(100), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(100); - vector2.tick(); - SyncClock sync; - vector1.release(&cache, &sync); - vector2.release(&cache, &sync); - ASSERT_EQ(sync.size(), 101U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - ASSERT_EQ(sync.get(99), 0ULL); - ASSERT_EQ(sync.get(100), 1ULL); - sync.Reset(&cache); - } -} - -TEST(Clock, Growth2) { - // Test clock growth for every pair of sizes: - const uptr n = ARRAY_SIZE(interesting_sizes); - for (uptr fi = 0; fi < n; fi++) { - for (uptr ti = fi + 1; ti < n; ti++) { - const uptr from = interesting_sizes[fi]; - const uptr to = interesting_sizes[ti]; - SyncClock sync; - ThreadClock vector(0); - for (uptr i = 0; i < from; i++) - vector.set(&cache, i, i + 1); - if (from != 0) - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), from); - for (uptr i = 0; i < from; i++) - ASSERT_EQ(sync.get(i), i + 1); - for (uptr i = 0; i < to; i++) - vector.set(&cache, i, i + 1); - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), to); - for (uptr i = 0; i < to; i++) - ASSERT_EQ(sync.get(i), i + 1); - vector.set(&cache, to + 1, to + 1); - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), to + 2); - for (uptr i = 0; i < to; i++) - ASSERT_EQ(sync.get(i), i + 1); - ASSERT_EQ(sync.get(to), 0U); - ASSERT_EQ(sync.get(to + 1), to + 1); - sync.Reset(&cache); - } - } -} - -const uptr kThreads = 4; -const uptr kClocks = 4; - -// SimpleSyncClock and SimpleThreadClock implement the same thing as -// SyncClock and ThreadClock, but in a very simple way. -struct SimpleSyncClock { - u64 clock[kThreads]; - uptr size; - - SimpleSyncClock() { - Reset(); - } - - void Reset() { - size = 0; - for (uptr i = 0; i < kThreads; i++) - clock[i] = 0; - } - - bool verify(const SyncClock *other) const { - for (uptr i = 0; i < min(size, other->size()); i++) { - if (clock[i] != other->get(i)) - return false; - } - for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { - if (i < size && clock[i] != 0) - return false; - if (i < other->size() && other->get(i) != 0) - return false; - } - return true; - } -}; - -struct SimpleThreadClock { - u64 clock[kThreads]; - uptr size; - unsigned tid; - - explicit SimpleThreadClock(unsigned tid) { - this->tid = tid; - size = tid + 1; - for (uptr i = 0; i < kThreads; i++) - clock[i] = 0; - } - - void tick() { - clock[tid]++; - } - - void acquire(const SimpleSyncClock *src) { - if (size < src->size) - size = src->size; - for (uptr i = 0; i < kThreads; i++) - clock[i] = max(clock[i], src->clock[i]); - } - - void release(SimpleSyncClock *dst) const { - if (dst->size < size) - dst->size = size; - for (uptr i = 0; i < kThreads; i++) - dst->clock[i] = max(dst->clock[i], clock[i]); - } - - void acq_rel(SimpleSyncClock *dst) { - acquire(dst); - release(dst); - } - - void ReleaseStore(SimpleSyncClock *dst) const { - if (dst->size < size) - dst->size = size; - for (uptr i = 0; i < kThreads; i++) - dst->clock[i] = clock[i]; - } - - bool verify(const ThreadClock *other) const { - for (uptr i = 0; i < min(size, other->size()); i++) { - if (clock[i] != other->get(i)) - return false; - } - for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { - if (i < size && clock[i] != 0) - return false; - if (i < other->size() && other->get(i) != 0) - return false; - } - return true; - } -}; - -static bool ClockFuzzer(bool printing) { - // Create kThreads thread clocks. - SimpleThreadClock *thr0[kThreads]; - ThreadClock *thr1[kThreads]; - unsigned reused[kThreads]; - for (unsigned i = 0; i < kThreads; i++) { - reused[i] = 0; - thr0[i] = new SimpleThreadClock(i); - thr1[i] = new ThreadClock(i, reused[i]); - } - - // Create kClocks sync clocks. - SimpleSyncClock *sync0[kClocks]; - SyncClock *sync1[kClocks]; - for (unsigned i = 0; i < kClocks; i++) { - sync0[i] = new SimpleSyncClock(); - sync1[i] = new SyncClock(); - } - - // Do N random operations (acquire, release, etc) and compare results - // for SimpleThread/SyncClock and real Thread/SyncClock. - for (int i = 0; i < 10000; i++) { - unsigned tid = rand() % kThreads; - unsigned cid = rand() % kClocks; - thr0[tid]->tick(); - thr1[tid]->tick(); - - switch (rand() % 6) { - case 0: - if (printing) - printf("acquire thr%d <- clk%d\n", tid, cid); - thr0[tid]->acquire(sync0[cid]); - thr1[tid]->acquire(&cache, sync1[cid]); - break; - case 1: - if (printing) - printf("release thr%d -> clk%d\n", tid, cid); - thr0[tid]->release(sync0[cid]); - thr1[tid]->release(&cache, sync1[cid]); - break; - case 2: - if (printing) - printf("acq_rel thr%d <> clk%d\n", tid, cid); - thr0[tid]->acq_rel(sync0[cid]); - thr1[tid]->acq_rel(&cache, sync1[cid]); - break; - case 3: - if (printing) - printf("rel_str thr%d >> clk%d\n", tid, cid); - thr0[tid]->ReleaseStore(sync0[cid]); - thr1[tid]->ReleaseStore(&cache, sync1[cid]); - break; - case 4: - if (printing) - printf("reset clk%d\n", cid); - sync0[cid]->Reset(); - sync1[cid]->Reset(&cache); - break; - case 5: - if (printing) - printf("reset thr%d\n", tid); - u64 epoch = thr0[tid]->clock[tid] + 1; - reused[tid]++; - delete thr0[tid]; - thr0[tid] = new SimpleThreadClock(tid); - thr0[tid]->clock[tid] = epoch; - delete thr1[tid]; - thr1[tid] = new ThreadClock(tid, reused[tid]); - thr1[tid]->set(epoch); - break; - } - - if (printing) { - for (unsigned i = 0; i < kThreads; i++) { - printf("thr%d: ", i); - thr1[i]->DebugDump(printf); - printf("\n"); - } - for (unsigned i = 0; i < kClocks; i++) { - printf("clk%d: ", i); - sync1[i]->DebugDump(printf); - printf("\n"); - } - - printf("\n"); - } - - if (!thr0[tid]->verify(thr1[tid]) || !sync0[cid]->verify(sync1[cid])) { - if (!printing) - return false; - printf("differs with model:\n"); - for (unsigned i = 0; i < kThreads; i++) { - printf("thr%d: clock=[", i); - for (uptr j = 0; j < thr0[i]->size; j++) - printf("%s%llu", j == 0 ? "" : ",", thr0[i]->clock[j]); - printf("]\n"); - } - for (unsigned i = 0; i < kClocks; i++) { - printf("clk%d: clock=[", i); - for (uptr j = 0; j < sync0[i]->size; j++) - printf("%s%llu", j == 0 ? "" : ",", sync0[i]->clock[j]); - printf("]\n"); - } - return false; - } - } - - for (unsigned i = 0; i < kClocks; i++) { - sync1[i]->Reset(&cache); - } - return true; -} - -TEST(Clock, Fuzzer) { - struct timeval tv; - gettimeofday(&tv, NULL); - int seed = tv.tv_sec + tv.tv_usec; - printf("seed=%d\n", seed); - srand(seed); - if (!ClockFuzzer(false)) { - // Redo the test with the same seed, but logging operations. - srand(seed); - ClockFuzzer(true); - ASSERT_TRUE(false); - } -} - -} // namespace __tsan diff --git a/gnu/llvm/compiler-rt/unittests/lit_unittest_cfg_utils.py b/gnu/llvm/compiler-rt/unittests/lit_unittest_cfg_utils.py deleted file mode 100644 index 16281f0b48e..00000000000 --- a/gnu/llvm/compiler-rt/unittests/lit_unittest_cfg_utils.py +++ /dev/null @@ -1,4 +0,0 @@ -# Put all 64-bit tests in the shadow-memory parallelism group. We throttle those -# in our common lit config (lit.common.unit.cfg.py). -def darwin_sanitizer_parallelism_group_func(test): - return "shadow-memory" if "x86_64" in test.file_path else None |