+++ /dev/null
-# TODO(phosek): We should use the runtimes build instead configured with
-# LLVM_ENABLE_RUNTIMES=libcxxabi;libcxx to avoid duplication of logic.
-
-cmake_minimum_required(VERSION 3.13.4)
-project(custom-libcxx C CXX)
-
-find_package(Python3 REQUIRED COMPONENTS Interpreter)
-
-# Build static libcxxabi.
-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_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)
+++ /dev/null
-# Check if compile-rt library file path exists.
-# If found, cache the path in:
-# COMPILER_RT_LIBRARY-<name>-<target>
-# If err_flag is true OR path not found, emit a message and set:
-# COMPILER_RT_LIBRARY-<name>-<target> to NOTFOUND
-function(cache_compiler_rt_library err_flag name target library_file)
- if(err_flag OR NOT EXISTS "${library_file}")
- message(STATUS "Failed to find compiler-rt ${name} library for ${target}")
- set(COMPILER_RT_LIBRARY-${name}-${target} "NOTFOUND" CACHE INTERNAL
- "compiler-rt ${name} library for ${target}")
- else()
- message(STATUS "Found compiler-rt ${name} library: ${library_file}")
- set(COMPILER_RT_LIBRARY-${name}-${target} "${library_file}" CACHE INTERNAL
- "compiler-rt ${name} library for ${target}")
- endif()
-endfunction()
-
-# Find the path to compiler-rt library `name` (e.g. "builtins") for
-# the specified `target` (e.g. "x86_64-linux") and return it in `variable`.
-# This calls cache_compiler_rt_library that caches the path to speed up
-# repeated invocations with the same `name` and `target`.
-function(find_compiler_rt_library name target variable)
- if(NOT CMAKE_CXX_COMPILER_ID MATCHES Clang)
- set(${variable} "NOTFOUND" PARENT_SCOPE)
- return()
- endif()
- if (NOT target AND CMAKE_CXX_COMPILER_TARGET)
- set(target "${CMAKE_CXX_COMPILER_TARGET}")
- endif()
- if(NOT DEFINED COMPILER_RT_LIBRARY-builtins-${target})
- # If the cache variable is not defined, invoke clang and then
- # set it with cache_compiler_rt_library.
- set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${SANITIZER_COMMON_FLAGS}
- "--rtlib=compiler-rt" "-print-libgcc-file-name")
- if(target)
- list(APPEND CLANG_COMMAND "--target=${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)
- cache_compiler_rt_library(${HAD_ERROR}
- builtins "${target}" "${LIBRARY_FILE}")
- endif()
- if(NOT COMPILER_RT_LIBRARY-builtins-${target})
- set(${variable} "NOTFOUND" PARENT_SCOPE)
- return()
- endif()
- if(NOT DEFINED COMPILER_RT_LIBRARY-${name}-${target})
- # clang gives only the builtins library path. Other library paths are
- # obtained by substituting "builtins" with ${name} in the builtins
- # path and then checking if the resultant path exists. The result of
- # this check is also cached by cache_compiler_rt_library.
- set(LIBRARY_FILE "${COMPILER_RT_LIBRARY-builtins-${target}}")
- string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}")
- cache_compiler_rt_library(FALSE "${name}" "${target}" "${LIBRARY_FILE}")
- endif()
- set(${variable} "${COMPILER_RT_LIBRARY-${name}-${target}}" PARENT_SCOPE)
-endfunction()
#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;
__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;
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
// 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 {
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);
}
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
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);
}
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();
+++ /dev/null
-//===-- 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); }
+++ /dev/null
-//===-- 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"
-#include "builtins/assembly.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
- BTI_C
- 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
- BTI_C
- 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
- BTI_C
- 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
-
-GNU_PROPERTY_BTI_PAC
#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."
// 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.
# 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
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, ...) \
// 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 { \
__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__) \
#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; \
# 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) \
+++ /dev/null
-/*===- 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 */
+++ /dev/null
-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()
+++ /dev/null
-//===-- 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"));
-}
+++ /dev/null
-//===-- 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);
-}
+++ /dev/null
-//===- 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);
-}
+++ /dev/null
-//===-- 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";
-}
+++ /dev/null
-//===-- 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));
-}
+++ /dev/null
-//===-- 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();
-}
+++ /dev/null
-//===-- 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);
-}
+++ /dev/null
-//===-- 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));
-}
+++ /dev/null
-//===-- 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);
-}
+++ /dev/null
-//===-- 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_;
-};
-
#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"
// 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
#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" {
// 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
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
}
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);
}
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
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;
}
#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
#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) {
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);
}
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);
}
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
}
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);
}
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() {
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;
// '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++) {
# 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
// 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
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) {
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
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);
#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.
// 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;
}
#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
#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);
: "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) {
: "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;
: "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;
: "memory");
return res;
}
-#endif // defined(__x86_64__) && SANITIZER_LINUX
+#endif
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX
int internal_uname(struct utsname *buf) {
#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; }
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;
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;
}
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;
#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;
case 0x2a: // swl
case 0x2e: // swr
#endif
- return SignalContext::WRITE;
+ return SignalContext::Write;
case 0x20: // lb
case 0x24: // lbu
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).
#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)
#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
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
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
#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) {
#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
#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) {
#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
}
*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];
*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
*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); }
// Do nothing.
}
-void MaybeReexec() {
- // No need to re-exec on Linux.
-}
-
void CheckASLR() {
#if SANITIZER_NETBSD
int mib[3];
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() {
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);
+++ /dev/null
-//===-- 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
+++ /dev/null
-//===-- 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
#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
// 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
#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.
// (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
// 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
/// * 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
// 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.
// 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
+++ /dev/null
-#!/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,-runtime/references,-readability/todo,-whitespace/parens
-
-COMMON_LIT_TEST_LINT_FILTER=-whitespace/indent,-whitespace/line_length,-runtime/arrays,-readability/braces
-
-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
+++ /dev/null
-#!/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 an allowed list approach to check these
- # rather than an exclusion list. 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 list of allowed safe macros instead of a list 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 allowed list wrong means some extra
- # semicolons, while the downside for getting the exclusion list 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.
- allowed_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
- r'operator\s*[<>][<>]|'
- r'static_assert|COMPILE_ASSERT'
- r')\s*\(')
- if Search(allowed_functions, line):
- return
- elif not Search(r'\S+\([^)]*$', line):
- # Don't see an allowed function entry 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(allowed_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()
+++ /dev/null
-#!/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
-from io import open
-
-# 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', encoding='utf-8') 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)
+++ /dev/null
-#!/usr/bin/env 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()
+++ /dev/null
-#!/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
+++ /dev/null
-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)
- list(APPEND SCUDO_MINIMAL_OBJECT_LIBS
- RTGwpAsan RTGwpAsanOptionsParser RTGwpAsanBacktraceLibc
- RTGwpAsanSegvHandler)
- 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()
+++ /dev/null
-//===-- 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"
-#include "gwp_asan/optional/segv_handler.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) NO_THREAD_SAFETY_ANALYSIS {
- initThreadMaybe();
-
- if (UNLIKELY(Alignment > MaxAlignment)) {
- if (AllocatorMayReturnNull())
- return nullptr;
- reportAllocationAlignmentTooBig(Alignment, MaxAlignment);
- }
- if (UNLIKELY(Alignment < MinAlignment))
- Alignment = MinAlignment;
-
-#ifdef GWP_ASAN_HOOKS
- if (UNLIKELY(GuardedAlloc.shouldSample())) {
- if (void *Ptr = GuardedAlloc.allocate(Size, Alignment)) {
- if (SCUDO_CAN_USE_HOOKS && &__sanitizer_malloc_hook)
- __sanitizer_malloc_hook(Ptr, Size);
- return Ptr;
- }
- }
-#endif // GWP_ASAN_HOOKS
-
- 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) NO_THREAD_SAFETY_ANALYSIS {
- 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(__sanitizer::GetEnv("GWP_ASAN_OPTIONS"),
- Printf);
- gwp_asan::options::Options &Opts = gwp_asan::options::getOptions();
- Opts.Backtrace = gwp_asan::backtrace::getBacktraceFunction();
- GuardedAlloc.init(Opts);
-
- if (Opts.InstallSignalHandlers)
- gwp_asan::segv_handler::installSignalHandlers(
- &GuardedAlloc, __sanitizer::Printf,
- gwp_asan::backtrace::getPrintBacktraceFunction(),
- gwp_asan::backtrace::getSegvBacktraceFunction());
-#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();
-}
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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.")
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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"
+++ /dev/null
-//===-- 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); }
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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 SetCheckUnwindCallback(void (*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
+++ /dev/null
-//===-- 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() TRY_ACQUIRE(true, Mutex) {
- 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() ACQUIRE(Mutex) {
- atomic_store_relaxed(&Precedence, 0);
- Mutex.Lock();
- }
-
- inline void unlock() RELEASE(Mutex) { 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_
+++ /dev/null
-//===-- 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
+++ /dev/null
-//===-- 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) NO_THREAD_SAFETY_ANALYSIS {
- if (UNLIKELY(ScudoThreadState != ThreadInitialized)) {
- FallbackTSD.lock();
- *UnlockRequired = true;
- return &FallbackTSD;
- }
- *UnlockRequired = false;
- return &TSD;
-}
-
-#endif // SCUDO_TSD_EXCLUSIVE
+++ /dev/null
-//===-- 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) NO_THREAD_SAFETY_ANALYSIS {
- 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
+++ /dev/null
-//===-- 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) NO_THREAD_SAFETY_ANALYSIS {
- 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
+++ /dev/null
-//===-- 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
-
-#ifndef signature_HYGON_ebx // They are not defined in gcc.
-// HYGON: "HygonGenuine".
-#define signature_HYGON_ebx 0x6f677948
-#define signature_HYGON_edx 0x6e65476e
-#define signature_HYGON_ecx 0x656e6975
-#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);
- const bool IsHygon = (Ebx == signature_HYGON_ebx) &&
- (Edx == signature_HYGON_edx) &&
- (Ecx == signature_HYGON_ecx);
- if (!IsIntel && !IsAMD && !IsHygon)
- 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
+++ /dev/null
-//===-- 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_
+++ /dev/null
-//===-- 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::releaseStoreAcquire(SyncClock *sc) const {
-// for (int i = 0; i < kMaxThreads; i++) {
-// tmp = clock[i];
-// clock[i] = max(clock[i], sc->clock[i]);
-// sc->clock[i] = tmp;
-// }
-// }
-//
-// 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_.
-
-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
- , last_acquire_()
- , global_acquire_()
- , cached_idx_()
- , cached_size_()
- , cached_blocks_() {
- CHECK_LT(tid, kMaxTidInClock);
- CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits);
- nclk_ = tid_ + 1;
- 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);
-
- // Check if it's empty -> no need to do anything.
- const uptr nclk = src->size_;
- if (nclk == 0)
- 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.
- 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) {
- last_acquire_ = clk_[tid_];
- ResetCached(c);
- }
-}
-
-void ThreadClock::releaseStoreAcquire(ClockCache *c, SyncClock *sc) {
- DCHECK_LE(nclk_, kMaxTid);
- DCHECK_LE(sc->size_, kMaxTid);
-
- if (sc->size_ == 0) {
- // ReleaseStore will correctly set release_store_tid_,
- // which can be important for future operations.
- ReleaseStore(c, sc);
- return;
- }
-
- nclk_ = max(nclk_, (uptr) sc->size_);
-
- // Check if we need to resize sc.
- if (sc->size_ < nclk_)
- sc->Resize(c, nclk_);
-
- bool acquired = false;
-
- sc->Unshare(c);
- // Update sc->clk_.
- sc->FlushDirty();
- uptr i = 0;
- for (ClockElem &ce : *sc) {
- u64 tmp = clk_[i];
- if (clk_[i] < ce.epoch) {
- clk_[i] = ce.epoch;
- acquired = true;
- }
- ce.epoch = tmp;
- ce.reused = 0;
- i++;
- }
- sc->release_store_tid_ = kInvalidTid;
- sc->release_store_reused_ = 0;
-
- if (acquired) {
- 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;
- }
-
- // 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 (!HasAcquiredAfterRelease(dst)) {
- UpdateCurrentThread(c, dst);
- if (dst->release_store_tid_ != tid_ ||
- dst->release_store_reused_ != reused_)
- dst->release_store_tid_ = kInvalidTid;
- return;
- }
-
- // O(N) release.
- dst->Unshare(c);
- // First, remember whether we've acquired dst.
- bool acquired = IsAlreadyAcquired(dst);
- // 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.
- 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);
-
- 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].set_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_ &&
- !HasAcquiredAfterRelease(dst)) {
- UpdateCurrentThread(c, dst);
- return;
- }
-
- // O(N) release-store.
- 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].set_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) {
- 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) {
- dirty->set_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);
- 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;
-}
-
-// Checks whether the current thread has acquired anything
-// from other clocks after releasing to dst (directly or indirectly).
-bool ThreadClock::HasAcquiredAfterRelease(const SyncClock *dst) const {
- const u64 my_epoch = dst->elem(tid_).epoch;
- return my_epoch <= last_acquire_ ||
- my_epoch <= atomic_load_relaxed(&global_acquire_);
-}
-
-// 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].set_tid(kInvalidTid);
-}
-
-void SyncClock::Resize(ClockCache *c, uptr nclk) {
- 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->set_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
+++ /dev/null
-//===-- 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 << 22, 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 {
- u32 tid() const { return tid_ == kShortInvalidTid ? kInvalidTid : tid_; }
- void set_tid(u32 tid) {
- tid_ = tid == kInvalidTid ? kShortInvalidTid : tid;
- }
- u64 epoch : kClkBits;
-
- private:
- // Full kInvalidTid won't fit into Dirty::tid.
- static const u64 kShortInvalidTid = (1ull << (64 - kClkBits)) - 1;
- u64 tid_ : 64 - kClkBits; // kInvalidId if not active
- };
-
- static_assert(sizeof(Dirty) == 8, "Dirty is not 64bit");
-
- 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 releaseStoreAcquire(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 NoteGlobalAcquire(u64 v);
-
- 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_;
-
- // Last time another thread has done a global acquire of this thread's clock.
- // It helps to avoid problem described in:
- // https://github.com/golang/go/issues/39186
- // See test/tsan/java_finalizer2.cpp for a regression test.
- // Note the failuire is _extremely_ hard to hit, so if you are trying
- // to reproduce it, you may want to run something like:
- // $ go get golang.org/x/tools/cmd/stress
- // $ stress -p=64 ./a.out
- //
- // The crux of the problem is roughly as follows.
- // A number of O(1) optimizations in the clocks algorithm assume proper
- // transitive cumulative propagation of clock values. The AcquireGlobal
- // operation may produce an inconsistent non-linearazable view of
- // thread clocks. Namely, it may acquire a later value from a thread
- // with a higher ID, but fail to acquire an earlier value from a thread
- // with a lower ID. If a thread that executed AcquireGlobal then releases
- // to a sync clock, it will spoil the sync clock with the inconsistent
- // values. If another thread later releases to the sync clock, the optimized
- // algorithm may break.
- //
- // The exact sequence of events that leads to the failure.
- // - thread 1 executes AcquireGlobal
- // - thread 1 acquires value 1 for thread 2
- // - thread 2 increments clock to 2
- // - thread 2 releases to sync object 1
- // - thread 3 at time 1
- // - thread 3 acquires from sync object 1
- // - thread 3 increments clock to 2
- // - thread 1 acquires value 2 for thread 3
- // - thread 1 releases to sync object 2
- // - sync object 2 clock has 1 for thread 2 and 2 for thread 3
- // - thread 3 releases to sync object 2
- // - thread 3 sees value 2 in the clock for itself
- // and decides that it has already released to the clock
- // and did not acquire anything from other threads after that
- // (the last_acquire_ check in release operation)
- // - thread 3 does not update the value for thread 2 in the clock from 1 to 2
- // - thread 4 acquires from sync object 2
- // - thread 4 detects a false race with thread 2
- // as it should have been synchronized with thread 2 up to time 2,
- // but because of the broken clock it is now synchronized only up to time 1
- //
- // The global_acquire_ value helps to prevent this scenario.
- // Namely, thread 3 will not trust any own clock values up to global_acquire_
- // for the purposes of the last_acquire_ optimization.
- atomic_uint64_t global_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;
- bool HasAcquiredAfterRelease(const SyncClock *dst) 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 void ThreadClock::NoteGlobalAcquire(u64 v) {
- // Here we rely on the fact that AcquireGlobal is protected by
- // ThreadRegistryLock, thus only one thread at a time executes it
- // and values passed to this function should not go backwards.
- CHECK_LE(atomic_load_relaxed(&global_acquire_), v);
- atomic_store_relaxed(&global_acquire_, v);
-}
-
-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
+++ /dev/null
-//===-- 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"
-#include "sanitizer_common/sanitizer_ptrauth.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(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog1);
-}
-
-void __tsan_read2_pc(void *addr, void *pc) {
- MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog2);
-}
-
-void __tsan_read4_pc(void *addr, void *pc) {
- MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog4);
-}
-
-void __tsan_read8_pc(void *addr, void *pc) {
- MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8);
-}
-
-void __tsan_write1_pc(void *addr, void *pc) {
- MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog1);
-}
-
-void __tsan_write2_pc(void *addr, void *pc) {
- MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog2);
-}
-
-void __tsan_write4_pc(void *addr, void *pc) {
- MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog4);
-}
-
-void __tsan_write8_pc(void *addr, void *pc) {
- MemoryWrite(cur_thread(), STRIP_PAC_PC(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(), STRIP_PAC_PC(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(), STRIP_PAC_PC(pc), (uptr)addr, size, false);
-}
-
-void __tsan_write_range_pc(void *addr, uptr size, void *pc) {
- MemoryAccessRange(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, size, true);
-}
+++ /dev/null
-//===-- 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 {
- const unsigned kAccessSize = 1 << kAccessSizeLog;
- u64 *sp = &shadow_mem[idx];
- old = LoadShadow(sp);
- if (LIKELY(old.IsZero())) {
- if (!stored) {
- StoreIfNotYetStored(sp, &store_word);
- stored = true;
- }
- break;
- }
- // is the memory access equal to the previous?
- if (LIKELY(Shadow::Addr0AndSizeAreEqual(cur, old))) {
- // same thread?
- if (LIKELY(Shadow::TidsAreEqual(old, cur))) {
- if (LIKELY(old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))) {
- StoreIfNotYetStored(sp, &store_word);
- stored = true;
- }
- break;
- }
- 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)) {
- if (Shadow::TidsAreEqual(old, cur))
- break;
- if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))
- break;
- if (LIKELY(HappensBefore(old, thr)))
- break;
- goto RACE;
- }
- // The accesses do not intersect.
- break;
-} while (0);
+++ /dev/null
-//===-- 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, releaseStoreAcquire) {
- ThreadClock thr0(0);
- thr0.tick();
- ThreadClock thr1(1);
- thr1.tick();
- SyncClock syncA;
- SyncClock syncB;
- ASSERT_EQ(syncA.size(), 0U);
- ASSERT_EQ(syncB.size(), 0U);
- thr1.releaseStoreAcquire(&cache, &syncB);
- ASSERT_EQ(syncB.size(), 2U); // T0 and T1
- // releaseStoreAcquire to an empty SyncClock
- thr0.releaseStoreAcquire(&cache, &syncA);
- ASSERT_EQ(syncA.size(), 1U);
- // releaseStoreAcquire from a non-empty SyncClock
- // T0 learns about T1
- thr0.releaseStoreAcquire(&cache, &syncB);
- // releaseStoreAcquire to the originally empty SyncClock
- // T0 deposits info about T1 into syncA
- thr0.releaseStoreAcquire(&cache, &syncA);
- ASSERT_EQ(syncA.size(), 2U);
- syncA.Reset(&cache);
- syncB.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 releaseStoreAcquire(SimpleSyncClock *sc) {
- if (sc->size < size)
- sc->size = size;
- else
- size = sc->size;
- for (uptr i = 0; i < kThreads; i++) {
- uptr tmp = clock[i];
- clock[i] = max(sc->clock[i], clock[i]);
- sc->clock[i] = tmp;
- }
- }
-
- 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() % 7) {
- 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("releaseStoreAcquire thr%d -> clk%d\n", tid, cid);
- thr0[tid]->releaseStoreAcquire(sync0[cid]);
- thr1[tid]->releaseStoreAcquire(&cache, sync1[cid]);
- break;
- case 6:
- 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
+++ /dev/null
-# 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