diff options
author | Jose Fonseca <jfonseca@vmware.com> | 2016-05-05 16:48:29 +0100 |
---|---|---|
committer | Jose Fonseca <jfonseca@vmware.com> | 2016-05-05 16:48:29 +0100 |
commit | 1ee7c4cfceb11c667da9327bdb61a1921fff384e (patch) | |
tree | 610c9606d2eafb45ec8e6b05b5cb9deab86d29b9 /wrappers | |
parent | 620060f1d86ada4c957d661e78cbd1149adc0a84 (diff) |
wrappers: Add a module to track memory changes to
This will be the cornerstone to detect partial changes to buffer mappings
in D3D10+ and OpenGL.
Diffstat (limited to 'wrappers')
-rw-r--r-- | wrappers/CMakeLists.txt | 4 | ||||
-rw-r--r-- | wrappers/memtrace.cpp | 236 | ||||
-rw-r--r-- | wrappers/memtrace.hpp | 63 |
3 files changed, 303 insertions, 0 deletions
diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt index 1e703406..84ec9a41 100644 --- a/wrappers/CMakeLists.txt +++ b/wrappers/CMakeLists.txt @@ -54,6 +54,7 @@ include_directories ( ${CMAKE_BINARY_DIR}/dispatch ${CMAKE_SOURCE_DIR}/dispatch ${CMAKE_SOURCE_DIR}/lib/guids + ${CMAKE_SOURCE_DIR}/thirdparty/crc32c ) if (NOT WIN32 AND NOT APPLE) @@ -75,10 +76,13 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_convenience_library (trace assert.cpp + memtrace.hpp + memtrace.cpp ) target_link_libraries (trace common guids + crc32c ${SNAPPY_LIBRARIES} ) diff --git a/wrappers/memtrace.cpp b/wrappers/memtrace.cpp new file mode 100644 index 00000000..4707f8b7 --- /dev/null +++ b/wrappers/memtrace.cpp @@ -0,0 +1,236 @@ +/************************************************************************** + * + * Copyright 2014-2016 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#include "memtrace.hpp" + +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include <algorithm> + +#include "crc32c.hpp" + + +#if \ + defined(__i386__) /* gcc */ || defined(_M_IX86) /* msvc */ || \ + defined(__x86_64__) /* gcc */ || defined(_M_X64) /* msvc */ || defined(_M_AMD64) /* msvc */ + +#define HAVE_SSE2 + +// TODO: Detect and leverage SSE 4.1 and 4.2 at runtime +#undef HAVE_SSE41 +#undef HAVE_SSE42 + +#endif + + +#if defined(HAVE_SSE42) +# include <nmmintrin.h> +#elif defined(HAVE_SSE41) +# include <smmintrin.h> +#elif defined(HAVE_SSE2) +# include <emmintrin.h> +#endif + + +#define BLOCK_SIZE 512 + + +template< class T > +static inline T * +lAlignPtr(T *p, uintptr_t alignment) +{ + return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) & ~(alignment - 1)); +} + + +template< class T > +static inline T * +rAlignPtr(T *p, uintptr_t alignment) +{ + return reinterpret_cast<T *>((reinterpret_cast<uintptr_t>(p) + alignment - 1) & ~(alignment - 1)); +} + + +#ifdef HAVE_SSE2 + +#ifdef HAVE_SSE41 + #define mm_stream_load_si128 _mm_stream_load_si128 + #define mm_extract_epi32_0(x) _mm_extract_epi32(x, 0) + #define mm_extract_epi32_1(x) _mm_extract_epi32(x, 1) + #define mm_extract_epi32_2(x) _mm_extract_epi32(x, 2) + #define mm_extract_epi32_3(x) _mm_extract_epi32(x, 3) +#else /* !HAVE_SSE41 */ + #define mm_stream_load_si128 _mm_load_si128 + #define mm_extract_epi32_0(x) _mm_cvtsi128_si32(x) + #define mm_extract_epi32_1(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(1,1,1,1))) + #define mm_extract_epi32_2(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(2,2,2,2))) + #define mm_extract_epi32_3(x) _mm_cvtsi128_si32(_mm_shuffle_epi32(x,_MM_SHUFFLE(3,3,3,3))) +#endif /* !HAVE_SSE41 */ + +#ifdef HAVE_SSE42 + +#define mm_crc32_u32 _mm_crc32_u32 + +#else /* !HAVE_SSE42 */ + +static inline uint32_t +mm_crc32_u32(uint32_t crc, uint32_t current) +{ + uint32_t one = current ^ crc; + crc = crc32c_8x256_table[0][ one >> 24 ] ^ + crc32c_8x256_table[1][(one >> 16) & 0xff] ^ + crc32c_8x256_table[2][(one >> 8) & 0xff] ^ + crc32c_8x256_table[3][ one & 0xff]; + return crc; +} + +#endif /* !HAVE_SSE42 */ + +#endif /* HAVE_SSE2 */ + + +uint32_t +hashBlock(const void *p) +{ + assert((intptr_t)p % BLOCK_SIZE == 0); + + uint32_t crc; + +#ifdef HAVE_SSE2 + crc = 0; + + __m128i *q = (__m128i *)(void *)p; + + crc = ~crc; + + for (unsigned c = BLOCK_SIZE / (4 * sizeof *q); c; --c) { + __m128i m0 = mm_stream_load_si128(q++); + __m128i m1 = mm_stream_load_si128(q++); + __m128i m2 = mm_stream_load_si128(q++); + __m128i m3 = mm_stream_load_si128(q++); + + crc = mm_crc32_u32(crc, mm_extract_epi32_0(m0)); + crc = mm_crc32_u32(crc, mm_extract_epi32_1(m0)); + crc = mm_crc32_u32(crc, mm_extract_epi32_2(m0)); + crc = mm_crc32_u32(crc, mm_extract_epi32_3(m0)); + + crc = mm_crc32_u32(crc, mm_extract_epi32_0(m1)); + crc = mm_crc32_u32(crc, mm_extract_epi32_1(m1)); + crc = mm_crc32_u32(crc, mm_extract_epi32_2(m1)); + crc = mm_crc32_u32(crc, mm_extract_epi32_3(m1)); + + crc = mm_crc32_u32(crc, mm_extract_epi32_0(m2)); + crc = mm_crc32_u32(crc, mm_extract_epi32_1(m2)); + crc = mm_crc32_u32(crc, mm_extract_epi32_2(m2)); + crc = mm_crc32_u32(crc, mm_extract_epi32_3(m2)); + + crc = mm_crc32_u32(crc, mm_extract_epi32_0(m3)); + crc = mm_crc32_u32(crc, mm_extract_epi32_1(m3)); + crc = mm_crc32_u32(crc, mm_extract_epi32_2(m3)); + crc = mm_crc32_u32(crc, mm_extract_epi32_3(m3)); + } + + crc = ~crc; + +#else /* !HAVE_SSE2 */ + + crc = crc32c_8bytes(p, BLOCK_SIZE); + +#endif + + return crc; +} + + +// We must reset the data on discard, otherwise the old data could match just +// by chance. +// +// XXX: if the appplication writes 0xCDCDCDCD at the start or the end of the +// buffer range, we'll fail to detect. The only way to be 100% sure things +// won't fall through would be to setup memory traps. +void MemoryShadow::zero(void *_ptr, size_t _size) +{ + memset(_ptr, 0xCD, _size); +} + + +void MemoryShadow::cover(void *_ptr, size_t _size, bool _discard) +{ + assert(_ptr); + + if (_size != size) { + nBlocks = ((intptr_t)_ptr + _size + BLOCK_SIZE - 1)/BLOCK_SIZE - (intptr_t)_ptr/BLOCK_SIZE; + + hashPtr = (uint32_t *)realloc(hashPtr, nBlocks * sizeof *hashPtr); + size = _size; + } + + realPtr = (const uint8_t *)_ptr; + + if (_discard) { + zero(_ptr, size); + } + + const uint8_t *p = lAlignPtr((const uint8_t *)_ptr, BLOCK_SIZE); + if (_discard) { + hashPtr[0] = hashBlock(p); + for (size_t i = 1; i < nBlocks; ++i) { + hashPtr[i] = hashPtr[0]; + } + } else { + for (size_t i = 0; i < nBlocks; ++i) { + hashPtr[i] = hashBlock(p); + p += BLOCK_SIZE; + } + } +} + + +void MemoryShadow::update(Callback callback) const +{ + const uint8_t *realStart = realPtr + size; + const uint8_t *realStop = realPtr; + + const uint8_t *p = lAlignPtr(realPtr, BLOCK_SIZE); + for (size_t i = 0; i < nBlocks; ++i) { + uint32_t crc = hashBlock(p); + if (crc != hashPtr[i]) { + realStart = std::min(realStart, p); + realStop = std::max(realStop, p + BLOCK_SIZE); + } + p += BLOCK_SIZE; + } + + realStart = std::max(realStart, realPtr); + realStop = std::min(realStop, realPtr + size); + + // Update the rest + if (realStart < realStop) { + callback(realStart, realStop - realStart); + } +} diff --git a/wrappers/memtrace.hpp b/wrappers/memtrace.hpp new file mode 100644 index 00000000..e366cc67 --- /dev/null +++ b/wrappers/memtrace.hpp @@ -0,0 +1,63 @@ +/************************************************************************** + * + * Copyright 2014-2016 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#pragma once + + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + + +uint32_t +hashBlock(const void *p); + + +class MemoryShadow +{ + size_t size = 0; + size_t nBlocks = 0; + const uint8_t *realPtr = nullptr; + uint32_t *hashPtr = nullptr; + +public: + MemoryShadow() + { + } + + ~MemoryShadow() + { + free(hashPtr); + } + + typedef void (*Callback)(const void *ptr, size_t size); + + static void zero(void *_ptr, size_t _size); + + void cover(void *ptr, size_t size, bool discard); + + void update(Callback callback) const; +}; |