From 4e3a0d5a8f750527f2f433019967f8f8214c558a Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 6 Jan 2016 20:03:21 +0000 Subject: Vendor import of compiler-rt trunk r256945: https://llvm.org/svn/llvm-project/compiler-rt/trunk@256945 --- cmake/config-ix.cmake | 2 +- include/sanitizer/coverage_interface.h | 7 ++ lib/asan/asan_win_dll_thunk.cc | 1 + lib/profile/CMakeLists.txt | 3 +- lib/profile/GCDAProfiling.c | 6 + lib/profile/InstrProfData.inc | 21 +++- lib/profile/InstrProfilingFile.c | 4 + lib/profile/InstrProfilingPort.h | 22 +--- lib/profile/WindowsMMap.c | 128 +++++++++++++++++++++ lib/profile/WindowsMMap.h | 65 +++++++++++ lib/sanitizer_common/sanitizer_coverage_libcdep.cc | 19 +++ lib/sanitizer_common/sanitizer_flags.inc | 3 + test/asan/TestCases/coverage-pc-buffer.cc | 48 ++++++++ 13 files changed, 308 insertions(+), 21 deletions(-) create mode 100644 lib/profile/WindowsMMap.c create mode 100644 lib/profile/WindowsMMap.h create mode 100644 test/asan/TestCases/coverage-pc-buffer.cc diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index f91530bb4403..ba21238623a5 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -559,7 +559,7 @@ else() endif() if (PROFILE_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows") set(COMPILER_RT_HAS_PROFILE TRUE) else() set(COMPILER_RT_HAS_PROFILE FALSE) diff --git a/include/sanitizer/coverage_interface.h b/include/sanitizer/coverage_interface.h index b93111b859bc..2dcc09fc8499 100644 --- a/include/sanitizer/coverage_interface.h +++ b/include/sanitizer/coverage_interface.h @@ -41,6 +41,13 @@ extern "C" { // Some of the entries in *data will be zero. uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data); + // Set *data to the growing buffer with covered PCs and return the size + // of the buffer. The entries are never zero. + // When only unique pcs are collected, the size is equal to + // __sanitizer_get_total_unique_coverage. + // WARNING: EXPERIMENTAL API. + uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data); + // The coverage instrumentation may optionally provide imprecise counters. // Rather than exposing the counter values to the user we instead map // the counters to a bitset. diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc index 308196d307cc..672cabf43ec1 100644 --- a/lib/asan/asan_win_dll_thunk.cc +++ b/lib/asan/asan_win_dll_thunk.cc @@ -315,6 +315,7 @@ INTERFACE_FUNCTION(__sanitizer_cov_trace_switch) INTERFACE_FUNCTION(__sanitizer_cov_with_check) INTERFACE_FUNCTION(__sanitizer_get_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) +INTERFACE_FUNCTION(__sanitizer_get_coverage_pc_buffer) INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes) INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_free_bytes) diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt index 1b10ade0eee6..3f84c0e6db35 100644 --- a/lib/profile/CMakeLists.txt +++ b/lib/profile/CMakeLists.txt @@ -35,7 +35,8 @@ set(PROFILE_SOURCES InstrProfilingPlatformLinux.c InstrProfilingPlatformOther.c InstrProfilingRuntime.cc - InstrProfilingUtil.c) + InstrProfilingUtil.c + WindowsMMap.c) if(UNIX) set(EXTRA_FLAGS diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c index aec232856e74..2338761ae1ab 100644 --- a/lib/profile/GCDAProfiling.c +++ b/lib/profile/GCDAProfiling.c @@ -27,8 +27,13 @@ #include #include #include + +#if defined(_WIN32) +#include "WindowsMMap.h" +#else #include #include +#endif #define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__)) @@ -37,6 +42,7 @@ #endif #if defined(_MSC_VER) +typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #elif I386_FREEBSD diff --git a/lib/profile/InstrProfData.inc b/lib/profile/InstrProfData.inc index 48dae506cabb..3a7c0c5f2773 100644 --- a/lib/profile/InstrProfData.inc +++ b/lib/profile/InstrProfData.inc @@ -1,4 +1,4 @@ -/*===-- InstrProfData.inc - instr profiling runtime structures -----------=== *\ +/*===-- InstrProfData.inc - instr profiling runtime structures -*- C++ -*-=== *\ |* |* The LLVM Compiler Infrastructure |* @@ -167,6 +167,25 @@ COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \ #undef COVMAP_FUNC_RECORD /* COVMAP_FUNC_RECORD end. */ +/* COVMAP_HEADER start */ +/* Definition of member fields of coverage map header. + */ +#ifndef COVMAP_HEADER +#define COVMAP_HEADER(Type, LLVMType, Name, Initializer) +#else +#define INSTR_PROF_DATA_DEFINED +#endif +COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \ + llvm::ConstantInt::get(Int32Ty, FunctionRecords.size())) +COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \ + llvm::ConstantInt::get(Int32Ty, FilenamesSize)) +COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \ + llvm::ConstantInt::get(Int32Ty, CoverageMappingSize)) +COVMAP_HEADER(uint32_t, Int32Ty, Version, \ + llvm::ConstantInt::get(Int32Ty, CoverageMappingVersion1)) +#undef COVMAP_HEADER +/* COVMAP_HEADER end. */ + #ifdef INSTR_PROF_VALUE_PROF_DATA #define INSTR_PROF_DATA_DEFINED diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c index 4ea7fbf9738a..bf50c02761d3 100644 --- a/lib/profile/InstrProfilingFile.c +++ b/lib/profile/InstrProfilingFile.c @@ -17,6 +17,10 @@ #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + /* Return 1 if there is an error, otherwise return 0. */ static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, void **WriterCtx) { diff --git a/lib/profile/InstrProfilingPort.h b/lib/profile/InstrProfilingPort.h index da4f18fcbb46..5da814a4a3d7 100644 --- a/lib/profile/InstrProfilingPort.h +++ b/lib/profile/InstrProfilingPort.h @@ -47,26 +47,12 @@ if (GetEnvHook && GetEnvHook("LLVM_PROFILE_VERBOSE_ERRORS")) \ fprintf(stderr, Format, __VA_ARGS__); -#if defined(__FreeBSD__) && defined(__i386__) +#if defined(__FreeBSD__) -/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to - * FreeBSD 10, r232261) when compiled in 32-bit mode. - */ -#define PRIu64 "llu" -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; -typedef uint32_t uintptr_t; -#elif defined(__FreeBSD__) && defined(__x86_64__) -#define PRIu64 "lu" -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; -typedef unsigned long int uintptr_t; +#include +#include -#else /* defined(__FreeBSD__) && defined(__i386__) */ +#else /* defined(__FreeBSD__) */ #include #include diff --git a/lib/profile/WindowsMMap.c b/lib/profile/WindowsMMap.c new file mode 100644 index 000000000000..1f7342050032 --- /dev/null +++ b/lib/profile/WindowsMMap.c @@ -0,0 +1,128 @@ +/* + * This code is derived from uClibc (original license follows). + * https://git.uclibc.org/uClibc/tree/utils/mmap-windows.c + */ + /* mmap() replacement for Windows + * + * Author: Mike Frysinger + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#if defined(_WIN32) + +#include "WindowsMMap.h" +#include "InstrProfiling.h" + +#ifdef __USE_FILE_OFFSET64 +# define DWORD_HI(x) (x >> 32) +# define DWORD_LO(x) ((x) & 0xffffffff) +#else +# define DWORD_HI(x) (0) +# define DWORD_LO(x) (x) +#endif + +COMPILER_RT_VISIBILITY +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +COMPILER_RT_VISIBILITY +void munmap(void *addr, size_t length) +{ + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} + +COMPILER_RT_VISIBILITY +int msync(void *addr, size_t length, int flags) +{ + if (flags & MS_INVALIDATE) + return -1; /* Not supported. */ + + /* Exactly one of MS_ASYNC or MS_SYNC must be specified. */ + switch (flags & (MS_ASYNC | MS_SYNC)) { + case MS_SYNC: + case MS_ASYNC: + break; + default: + return -1; + } + + if (!FlushViewOfFile(addr, length)) + return -1; + + if (flags & MS_SYNC) { + /* FIXME: No longer have access to handle from CreateFileMapping(). */ + /* + * if (!FlushFileBuffers(h)) + * return -1; + */ + } + + return 0; +} + +COMPILER_RT_VISIBILITY +int flock(int fd, int operation) +{ + return -1; /* Not supported. */ +} + +#undef DWORD_HI +#undef DWORD_LO + +#endif /* _WIN32 */ diff --git a/lib/profile/WindowsMMap.h b/lib/profile/WindowsMMap.h new file mode 100644 index 000000000000..7b94eb28230c --- /dev/null +++ b/lib/profile/WindowsMMap.h @@ -0,0 +1,65 @@ +/*===- WindowsMMap.h - Support library for PGO instrumentation ------------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILING_WINDOWS_MMAP_H +#define PROFILE_INSTRPROFILING_WINDOWS_MMAP_H + +#if defined(_WIN32) + +#include +#include +#include + +/* + * mmap() flags + */ +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE +#define PROT_EXEC 0x4 +#else +#define PROT_EXEC 0x0 +#define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_FILE 0x00 +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +/* + * msync() flags + */ +#define MS_ASYNC 0x0001 /* return immediately */ +#define MS_INVALIDATE 0x0002 /* invalidate all cached data */ +#define MS_SYNC 0x0010 /* msync synchronously */ + +/* + * flock() operations + */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* don't block when locking */ +#define LOCK_UN 8 /* unlock */ + +void *mmap(void *start, size_t length, int prot, int flags, int fd, + off_t offset); + +void munmap(void *addr, size_t length); + +int msync(void *addr, size_t length, int flags); + +int flock(int fd, int operation); + +#endif /* _WIN32 */ + +#endif /* PROFILE_INSTRPROFILING_WINDOWS_MMAP_H */ diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index b9833c5a1d97..eaa1446afd44 100644 --- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -108,6 +108,7 @@ class CoverageData { uptr *data(); uptr size(); + uptr *buffer() const { return pc_buffer; } private: void DirectOpen(); @@ -133,6 +134,8 @@ class CoverageData { // Descriptor of the file mapped pc array. fd_t pc_fd; + uptr *pc_buffer; + // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor guard_array_vec; @@ -209,6 +212,11 @@ void CoverageData::Enable() { atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); } + pc_buffer = nullptr; + if (common_flags()->coverage_pc_buffer) + pc_buffer = reinterpret_cast(MmapNoReserveOrDie( + sizeof(uptr) * kPcArrayMaxSize, "CovInit::pc_buffer")); + cc_array = reinterpret_cast(MmapNoReserveOrDie( sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array")); atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed); @@ -246,6 +254,10 @@ void CoverageData::Disable() { UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize); cc_array = nullptr; } + if (pc_buffer) { + UnmapOrDie(pc_buffer, sizeof(uptr) * kPcArrayMaxSize); + pc_buffer = nullptr; + } if (tr_event_array) { UnmapOrDie(tr_event_array, sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + @@ -414,6 +426,7 @@ void CoverageData::Add(uptr pc, u32 *guard) { atomic_load(&pc_array_size, memory_order_acquire)); uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); pc_array[idx] = BundlePcAndCounter(pc, counter); + if (pc_buffer) pc_buffer[counter] = pc; } // Registers a pair caller=>callee. @@ -943,6 +956,12 @@ uptr __sanitizer_get_coverage_guards(uptr **data) { return coverage_data.size(); } +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_coverage_pc_buffer(uptr **data) { + *data = coverage_data.buffer(); + return __sanitizer_get_total_unique_coverage(); +} + SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_number_of_counters() { return coverage_data.GetNumberOf8bitCounters(); diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc index c89273117a5e..c28f7f4a6034 100644 --- a/lib/sanitizer_common/sanitizer_flags.inc +++ b/lib/sanitizer_common/sanitizer_flags.inc @@ -144,6 +144,9 @@ COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, COMMON_FLAG(const char *, coverage_dir, ".", "Target directory for coverage dumps. Defaults to the current " "directory.") +COMMON_FLAG(bool, coverage_pc_buffer, true, + "If set (and if 'coverage' is set too), the pcs would be collected " + "in a buffer.") COMMON_FLAG(bool, full_address_space, false, "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized") diff --git a/test/asan/TestCases/coverage-pc-buffer.cc b/test/asan/TestCases/coverage-pc-buffer.cc new file mode 100644 index 000000000000..67b6935ec602 --- /dev/null +++ b/test/asan/TestCases/coverage-pc-buffer.cc @@ -0,0 +1,48 @@ +// Test __sanitizer_coverage_pc_buffer(). + +// RUN: %clangxx_asan -fsanitize-coverage=edge %s -o %t && %run %t + +// UNSUPPORTED: android + +#include +#include +#include + +static volatile int sink; +__attribute__((noinline)) void bar() { sink = 2; } +__attribute__((noinline)) void foo() { sink = 1; } + +void assertNotZeroPcs(uintptr_t *buf, uintptr_t size) { + assert(buf); + for (uintptr_t i = 0; i < size; ++i) + assert(buf[i]); +} + +int main() { + uintptr_t *buf = NULL; + uintptr_t sz = __sanitizer_get_coverage_pc_buffer(&buf); + assertNotZeroPcs(buf, sz); + assert(sz); + + foo(); + bar(); + uintptr_t *buf1 = NULL; + uintptr_t sz1 = __sanitizer_get_coverage_pc_buffer(&buf1); + assertNotZeroPcs(buf1, sz1); + assert(buf1 == buf); + assert(sz1 > sz); + + bar(); + uintptr_t *buf2 = NULL; + uintptr_t sz2 = __sanitizer_get_coverage_pc_buffer(&buf2); + assertNotZeroPcs(buf2, sz2); + assert(buf2 == buf); + assert(sz2 > sz1); + + __sanitizer_reset_coverage(); + uintptr_t *buf3 = NULL; + uintptr_t sz3 = __sanitizer_get_coverage_pc_buffer(&buf3); + assertNotZeroPcs(buf3, sz3); + assert(buf3 == buf); + assert(sz3 < sz2); +} -- cgit v1.2.3