├── AUTHORS ├── .gitignore ├── LICENSE.txt ├── config.h ├── config.h.cmake ├── cmake_modules └── FindLibunwind.cmake ├── configure ├── mem.h ├── mem.c ├── util.h ├── backtrace.h ├── posix_platform.c ├── thread_unit.c ├── util.c ├── libc_backtrace.c ├── platform.h ├── ignore_unit.c ├── linux_platform.c ├── handler.h ├── error.h ├── README.md ├── CMakeLists.txt ├── test.c ├── lock_unit.c ├── simple_unit.c ├── test.h ├── libunwind_backtrace.c ├── error.c ├── lksmith.h ├── handler.c ├── error_unit.c ├── tree.h └── lksmith.c /AUTHORS: -------------------------------------------------------------------------------- 1 | "Colin Patrick McCabe" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cscope.files 2 | cscope.out 3 | *.swp 4 | *.swo 5 | *.pyc 6 | *~ 7 | build 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2012, the Locksmith authors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_CONFIG_H 31 | #define LKSMITH_CONFIG_H 32 | 33 | #define HAVE_IMPROVED_TLS 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /config.h.cmake: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_CONFIG_H 31 | #define LKSMITH_CONFIG_H 32 | 33 | #cmakedefine HAVE_IMPROVED_TLS 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /cmake_modules/FindLibunwind.cmake: -------------------------------------------------------------------------------- 1 | # Find the libunwind library 2 | # 3 | # LIBUNWIND_FOUND - True if libunwind was found. 4 | # LIBUNWIND_LIBRARIES - The libraries needed to use libunwind 5 | # LIBUNWIND_INCLUDE_DIR - Location of unwind.h and libunwind.h 6 | 7 | FIND_PATH(LIBUNWIND_INCLUDE_DIR libunwind.h) 8 | if(NOT LIBUNWIND_INCLUDE_DIR) 9 | message(FATAL_ERROR "failed to find libunwind.h") 10 | elif(NOT EXISTS "${LIBUNWIND_INCLUDE_DIR}/unwind.h") 11 | message(FATAL_ERROR "libunwind.h was found, but unwind.h was not found in that directory.") 12 | SET(LIBUNWIND_INCLUDE_DIR "") 13 | endif() 14 | 15 | FIND_LIBRARY(LIBUNWIND_GENERIC_LIBRARY "unwind") 16 | if (NOT LIBUNWIND_GENERIC_LIBRARY) 17 | MESSAGE(FATAL_ERROR "failed to find unwind generic library") 18 | endif () 19 | SET(LIBUNWIND_LIBRARIES ${LIBUNWIND_GENERIC_LIBRARY}) 20 | 21 | # For some reason, we have to link to two libunwind shared object files: 22 | # one arch-specific and one not. 23 | if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") 24 | SET(LIBUNWIND_ARCH "arm") 25 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") 26 | SET(LIBUNWIND_ARCH "x86_64") 27 | elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") 28 | SET(LIBUNWIND_ARCH "x86") 29 | endif() 30 | 31 | if (LIBUNWIND_ARCH) 32 | FIND_LIBRARY(LIBUNWIND_SPECIFIC_LIBRARY "unwind-${LIBUNWIND_ARCH}") 33 | if (NOT LIBUNWIND_SPECIFIC_LIBRARY) 34 | MESSAGE(FATAL_ERROR "failed to find unwind-${LIBUNWIND_ARCH}") 35 | endif () 36 | SET(LIBUNWIND_LIBRARIES ${LIBUNWIND_LIBRARIES} ${LIBUNWIND_SPECIFIC_LIBRARY}) 37 | endif(LIBUNWIND_ARCH) 38 | 39 | MARK_AS_ADVANCED(LIBUNWIND_LIBRARIES LIBUNWIND_INCLUDE_DIR) 40 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | die() { 4 | echo $@ 5 | exit 1 6 | } 7 | 8 | install_prefix_default="/usr" 9 | 10 | usage() { 11 | cat < if you have libraries in a 27 | nonstandard directory 28 | EOF 29 | } 30 | 31 | curdir="$(dirname $0)" 32 | srcdir="$curdir" 33 | build_type="Release" 34 | install_prefix="$install_prefix_default" 35 | while [ $# -gt 0 ]; do 36 | case "$1" in 37 | --debug-build) 38 | build_type="Debug";; 39 | -h|--help) usage; exit 0;; 40 | --prefix) shift 41 | [ $# -eq 0 ] && die "--prefix requires an argument. -h for help." 42 | install_prefix=$1 43 | shift;; 44 | --srcdir) shift 45 | [ $# -eq 0 ] && die "--srcdir requires an argument. -h for help." 46 | srcdir=$1 47 | shift;; 48 | -V|--version) 49 | grep CPACK_PACKAGE_VERSION_ "$curdir/CMakeLists.txt"; 50 | exit 0;; 51 | *) die "can't understand parameter: $1. -h for help." 52 | esac 53 | shift 54 | done 55 | 56 | cmake -DCMAKE_BUILD_TYPE="$build_type" \ 57 | -DCMAKE_INSTALL_PREFIX="$install_prefix" \ 58 | $srcdir 59 | -------------------------------------------------------------------------------- /mem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_MEM_H 31 | #define LKSMITH_MEM_H 32 | 33 | #include /* for size_t */ 34 | 35 | /** 36 | * Allocate some zeroed memory, or die 37 | * 38 | * @param s The number of bytes to allocate. 39 | * 40 | * @return The calloc'ed memory. The program will be aborted with 41 | * an error message to stderr on out-of-memory. 42 | */ 43 | void *xcalloc(size_t s); 44 | 45 | /** 46 | * Duplicate a string, or die. 47 | * 48 | * @param str The string to be duplicated. 49 | * 50 | * @return A malloced copy of the string. 51 | */ 52 | char *xstrdup(const char *str); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /mem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "mem.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | void *xcalloc(size_t s) 40 | { 41 | void *p; 42 | 43 | p = calloc(1, s); 44 | if (!p) { 45 | fprintf(stderr, "out of memory allocating %zd bytes.\n", s); 46 | abort(); 47 | } 48 | return p; 49 | } 50 | 51 | char *xstrdup(const char *str) 52 | { 53 | char *s = strdup(str); 54 | if (!s) { 55 | fprintf(stderr, "failed to duplicate string of size " 56 | "%zd.\n", strlen(str)); 57 | exit(1); 58 | } 59 | return s; 60 | } 61 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_UTIL_H 31 | #define LKSMITH_UTIL_H 32 | 33 | #include /* for size_t */ 34 | 35 | /** Write a formatted string to the next available position in a 36 | * fixed-length buffer 37 | * 38 | * @param buf the buffer 39 | * @param off (inout) current offset in the buffer 40 | * @param max length of the buffer 41 | * @param fmt a printf-style format string 42 | * @param ... format arguments 43 | */ 44 | void fwdprintf(char *buf, size_t *off, size_t buf_len, 45 | const char *fmt, ...) __attribute__((format(printf, 4, 5))); 46 | 47 | void simple_spin_lock(int *lock); 48 | 49 | void simple_spin_unlock(int *lock); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /backtrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2013, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_BACKTRACE_H 31 | #define LKSMITH_BACKTRACE_H 32 | 33 | /** 34 | * Create a backtrace 35 | * 36 | * @param scratch (inout) Thread-local scratch area. 37 | * @param scratch_len (inout) Thread-local scratch area length. 38 | * @param out (out) The backtrace frames. 39 | * 40 | * @return the number of frames on success; a negative error 41 | * code otherwise 42 | */ 43 | int bt_frames_create(void ***scratch, int *scratch_len, char ***out); 44 | 45 | /** 46 | * Free backtrace frames. 47 | * 48 | * @param backtrace The frames to free 49 | */ 50 | void bt_frames_free(char **backtrace); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /posix_platform.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "platform.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | static uint64_t g_tid; 38 | 39 | void platform_create_thread_name(char * __restrict out, size_t out_len) 40 | { 41 | uint64_t new_tid; 42 | 43 | /* pthreads contains no way to get a string representation of a 44 | * thread's name. pthread_self gives you an opaque identifier, but 45 | * you can't do much with it. So let's make up our own identifier 46 | * with an atomic variable. 47 | */ 48 | new_tid = __sync_add_and_fetch(&g_tid, 1); 49 | snprintf(out, out_len, "thread_%"PRId64, new_tid); 50 | } 51 | 52 | void* get_dlsym_next(const char *fname) 53 | { 54 | void *v; 55 | 56 | /* We need RTLD_NEXT to get this to work. */ 57 | v = dlsym(RTLD_NEXT, fname); 58 | if (!v) { 59 | fprintf(stderr, "locksmith handler error: dlsym error: %s\n", 60 | dlerror()); 61 | return NULL; 62 | } 63 | return v; 64 | } 65 | -------------------------------------------------------------------------------- /thread_unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "lksmith.h" 31 | #include "test.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | static int test_thread_name_set_and_get_impl(void) 41 | { 42 | const char * const MY_THREAD = "my_thread"; 43 | const char *name; 44 | 45 | EXPECT_ZERO(lksmith_set_thread_name(MY_THREAD)); 46 | name = lksmith_get_thread_name(); 47 | EXPECT_NOT_EQ(name, NULL); 48 | EXPECT_ZERO(strcmp(MY_THREAD, name)); 49 | return 0; 50 | } 51 | 52 | static void* test_thread_name_set_and_get(void *v __attribute__((unused))) 53 | { 54 | return (void*)(uintptr_t)test_thread_name_set_and_get_impl(); 55 | } 56 | 57 | int main(void) 58 | { 59 | pthread_t pthread; 60 | void *rval; 61 | 62 | set_error_cb(die_on_error); 63 | EXPECT_ZERO(pthread_create(&pthread, NULL, 64 | test_thread_name_set_and_get, NULL)); 65 | EXPECT_ZERO(pthread_join(pthread, &rval)); 66 | EXPECT_EQ(rval, 0); 67 | 68 | return EXIT_SUCCESS; 69 | } 70 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /* get the POSIX strerror_r */ 31 | #undef _GNU_SOURCE 32 | #define _XOPEN_SOURCE 600 33 | #include 34 | #define _GNU_SOURCE 35 | #undef _XOPEN_SOURCE 36 | 37 | #include "config.h" 38 | #include "util.h" 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | void fwdprintf(char *buf, size_t *off, size_t buf_len, const char *fmt, ...) 49 | { 50 | int res; 51 | size_t o; 52 | va_list ap; 53 | 54 | o = *off; 55 | va_start(ap, fmt); 56 | res = vsnprintf(buf + o, buf_len - o, fmt, ap); 57 | va_end(ap); 58 | if (res < 0) 59 | return; 60 | else if (o + res > buf_len) 61 | *off = buf_len; 62 | else 63 | *off = o + res; 64 | } 65 | 66 | void simple_spin_lock(int *lock) 67 | { 68 | struct timespec ts; 69 | 70 | while (1) { 71 | if (__sync_bool_compare_and_swap(lock, 0, 1)) { 72 | return; 73 | } 74 | ts.tv_sec = 0; 75 | ts.tv_nsec = 10000; 76 | nanosleep(&ts, NULL); 77 | } 78 | } 79 | 80 | void simple_spin_unlock(int *lock) 81 | { 82 | __sync_bool_compare_and_swap(lock, 1, 0); 83 | } 84 | -------------------------------------------------------------------------------- /libc_backtrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2013, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "error.h" 31 | #include "backtrace.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define INITIAL_SCRATCH_SIZE 16 39 | 40 | #define MAX_SCRATCH_SIZE 8192 41 | 42 | void bt_frames_free(char **backtrace) 43 | { 44 | free(backtrace); 45 | } 46 | 47 | static int try_backtrace(void **scratch, int scratch_len) 48 | { 49 | int num_symbols; 50 | if (!scratch) { 51 | return -ENOMEM; 52 | } 53 | num_symbols = backtrace(scratch, scratch_len); 54 | if (num_symbols == scratch_len) { 55 | return -ENOMEM; 56 | } 57 | return num_symbols; 58 | } 59 | 60 | int bt_frames_create(void ***scratch, int *scratch_len, char ***out) 61 | { 62 | int num_symbols; 63 | void **next; 64 | char **symbols; 65 | 66 | while (((num_symbols = try_backtrace(*scratch, *scratch_len))) < 0) { 67 | int next_size = (*scratch_len == 0) ? 68 | INITIAL_SCRATCH_SIZE : (*scratch_len * 2); 69 | if (next_size > MAX_SCRATCH_SIZE) { 70 | return -ENOMEM; 71 | } 72 | next = realloc(*scratch, next_size * sizeof(void*)); 73 | if (!next) { 74 | return -ENOMEM; 75 | } 76 | *scratch = next; 77 | *scratch_len = next_size; 78 | } 79 | symbols = backtrace_symbols(*scratch, num_symbols); 80 | if (!symbols) { 81 | return -ENOMEM; 82 | } 83 | *out = symbols; 84 | return num_symbols; 85 | } 86 | -------------------------------------------------------------------------------- /platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_UTIL_PLATFORM_H 31 | #define LKSMITH_UTIL_PLATFORM_H 32 | 33 | /** 34 | * Interface for making platform-specific calls. 35 | */ 36 | 37 | #include /* for size_t */ 38 | 39 | /** 40 | * Create a name for the current thread. 41 | * 42 | * If the platform supports getting the kernel thread ID, we will retrieve 43 | * that. Otherwise, we'll create an arbitrary identifier. 44 | * 45 | * @param out (out param) the buffer to write to 46 | * @param out_len length of the out buffer 47 | */ 48 | void platform_create_thread_name(char * __restrict out, size_t out_len); 49 | 50 | /** 51 | * Find a function named 'fname' in a library other than the current one. We 52 | * need this to forward methods that we have intercepted onwards to the 53 | * "real" pthreads functions. 54 | * 55 | * @param fname The function name to look for 56 | * 57 | * @return NULL if the function could not be found; the function 58 | * pointer otherwise. Technically, a void pointer should 59 | * never be cast to a function pointer, since the C 60 | * standard allows them to have different sizes. However, 61 | * the POSIX dlsym functions we're calling ignore this 62 | * nicety, and alternative interfaces are not available. 63 | * So there seems to be little point in being pedantic about 64 | * it in the code. 65 | */ 66 | void* get_dlsym_next(const char *fname); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /ignore_unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "lksmith.h" 31 | #include "test.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | static pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER; 43 | static pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER; 44 | 45 | int lksmith_get_ignored_frame_patterns(char *** ignored, int *num_ignored); 46 | 47 | static int check_ignored_frame_patterns(void) 48 | { 49 | char **ignored = 0; 50 | int num_ignored = 0; 51 | 52 | EXPECT_EQ(0, lksmith_get_ignored_frame_patterns(&ignored, &num_ignored)); 53 | EXPECT_EQ(3, num_ignored); 54 | EXPECT_ZERO(strcmp("*ignore1*", ignored[0])); 55 | EXPECT_ZERO(strcmp("*ignore2*", ignored[1])); 56 | EXPECT_ZERO(strcmp("*ignore3*", ignored[2])); 57 | return 0; 58 | } 59 | 60 | void ignore1(void) 61 | { 62 | pthread_mutex_lock(&lock2); 63 | pthread_mutex_lock(&lock1); 64 | pthread_mutex_unlock(&lock1); 65 | pthread_mutex_unlock(&lock2); 66 | } 67 | 68 | static int verify_ignored_frame_patterns_work(void) 69 | { 70 | clear_recorded_errors(); 71 | pthread_mutex_lock(&lock1); 72 | pthread_mutex_lock(&lock2); 73 | pthread_mutex_unlock(&lock2); 74 | pthread_mutex_unlock(&lock1); 75 | ignore1(); 76 | EXPECT_EQ(num_recorded_errors(), 0); 77 | return 0; 78 | } 79 | 80 | int main(void) 81 | { 82 | putenv("LKSMITH_IGNORED_FRAME_PATTERNS=*ignore3*:*ignore2*:*ignore1*"); 83 | 84 | set_error_cb(record_error); 85 | EXPECT_ZERO(check_ignored_frame_patterns()); 86 | EXPECT_ZERO(verify_ignored_frame_patterns_work()); 87 | 88 | return EXIT_SUCCESS; 89 | } 90 | -------------------------------------------------------------------------------- /linux_platform.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "platform.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | extern pid_t gettid(void); 39 | 40 | void platform_create_thread_name(char * __restrict out, size_t out_len) 41 | { 42 | /* On Linux, we can call gettid() to get the kernel's ID number for 43 | * this thread. This is preferrable to making up our own number, since 44 | * other debugging tools can also examine the kernel thread ID. */ 45 | pid_t tid = (pid_t)syscall(SYS_gettid); 46 | snprintf(out, out_len, "thread_%"PRId64, (uint64_t)tid); 47 | } 48 | 49 | void* get_dlsym_next(const char *fname) 50 | { 51 | void *v; 52 | 53 | if ((!strcmp(fname, "pthread_cond_init")) || 54 | (!strcmp(fname, "pthread_cond_wait")) || 55 | (!strcmp(fname, "pthread_cond_timedwait")) || 56 | (!strcmp(fname, "pthread_cond_destroy"))) { 57 | v = dlvsym(RTLD_NEXT, fname, "GLIBC_2.3.2"); 58 | } else { 59 | v = dlsym(RTLD_NEXT, fname); 60 | } 61 | if (!v) { 62 | /* dlerror is not thread-safe. However, since there is no 63 | * thread-safe interface, we really don't have much choice, 64 | * do we? 65 | * 66 | * Also, technically a NULL return from dlsym doesn't 67 | * necessarily indicate an error. However, NULL is not 68 | * a valid address for any of the things we're looking up, so 69 | * we're going to assume that an error occurred. 70 | */ 71 | fprintf(stderr, "locksmith handler error: dlsym error: %s\n", 72 | dlerror()); 73 | return NULL; 74 | } 75 | /* Another problem with the dlsym interface is that technically, a 76 | * void* should never be cast to a function pointer, since the C 77 | * standard allows them to have different sizes. 78 | * Apparently the POSIX committee didn't read that part of the C 79 | * standard. We'll pretend we didn't either. 80 | */ 81 | return v; 82 | } 83 | -------------------------------------------------------------------------------- /handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_HANDLER_H 31 | #define LKSMITH_HANDLER_H 32 | 33 | #include 34 | 35 | /****************************************************************** 36 | * The raw pthreads functions. 37 | *****************************************************************/ 38 | #ifndef LKSMITH_HANDLER_DOT_C 39 | #define EXTERN extern 40 | #else 41 | #define EXTERN 42 | #endif 43 | 44 | EXTERN int (*r_pthread_mutex_init)(pthread_mutex_t *mutex, 45 | const pthread_mutexattr_t *attr); 46 | 47 | EXTERN int (*r_pthread_mutex_destroy)(pthread_mutex_t *mutex); 48 | 49 | EXTERN int (*r_pthread_mutex_trylock)(pthread_mutex_t *mutex); 50 | 51 | EXTERN int (*r_pthread_mutex_lock)(pthread_mutex_t *mutex); 52 | 53 | EXTERN int (*r_pthread_mutex_timedlock)(pthread_mutex_t *__restrict mutex, 54 | __const struct timespec *__restrict ts); 55 | 56 | EXTERN int (*r_pthread_mutex_unlock)(pthread_mutex_t *__restrict mutex); 57 | 58 | EXTERN int (*r_pthread_spin_init)(pthread_spinlock_t *lock, int pshared); 59 | 60 | EXTERN int (*r_pthread_spin_destroy)(pthread_spinlock_t *lock); 61 | 62 | EXTERN int (*r_pthread_spin_lock)(pthread_spinlock_t *lock); 63 | 64 | EXTERN int (*r_pthread_spin_trylock)(pthread_spinlock_t *lock); 65 | 66 | EXTERN int (*r_pthread_spin_unlock)(pthread_spinlock_t *lock); 67 | 68 | EXTERN int (*r_pthread_cond_init)(pthread_cond_t *__restrict cond, 69 | const pthread_condattr_t *__restrict attr); 70 | 71 | EXTERN int (*r_pthread_cond_wait)(pthread_cond_t *__restrict cond, 72 | pthread_mutex_t *__restrict mutex); 73 | 74 | EXTERN int (*r_pthread_cond_timedwait)(pthread_cond_t *__restrict cond, 75 | pthread_mutex_t *__restrict mutex, 76 | const struct timespec *__restrict abstime); 77 | 78 | EXTERN int (*r_pthread_cond_destroy)(pthread_cond_t *cond); 79 | 80 | /****************************************************************** 81 | * Functions 82 | *****************************************************************/ 83 | int lksmith_handler_init(void); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_ERROR_H 31 | #define LKSMITH_ERROR_H 32 | 33 | #include 34 | #include 35 | 36 | /** 37 | * The type signature for a Locksmith error reporting callback. 38 | * 39 | * For obvious reasons, functions used as error reporting callbacks should not 40 | * take Locksmith-managed mutexes. 41 | * 42 | * @param code The numeric Locksmith error code (see LKSMITH_ERROR_). 43 | * @param msg The human-readable error string. 44 | */ 45 | typedef void (*lksmith_error_cb_t)(int code, const char * __restrict msg); 46 | 47 | /** 48 | * Log a Locksmith error message. 49 | * 50 | * @param err The error code. 51 | * @param fmt printf-style format string. 52 | * @param ... printf-style arguments. 53 | */ 54 | void lksmith_error(int err, const char *fmt, ...) 55 | __attribute__((format(printf, 2, 3))); 56 | 57 | /** 58 | * Log a Locksmith error message. 59 | * 60 | * @param err The error code. 61 | * @param fmt printf-style format string. 62 | * @param ... printf-style arguments. 63 | */ 64 | void lksmith_errora(int err, const char *fmt, va_list ap); 65 | 66 | /** 67 | * Log a Locksmith error message together with a backtrace. 68 | * 69 | * @param err The error code. 70 | * @param frames string array 71 | * @param frames_len length of string array. If this is <= 0, the array 72 | * will be ignored. 73 | * @param fmt printf-style format string. 74 | * @param ap printf-style arguments. 75 | */ 76 | void lksmith_errora_with_bt(int err, char **frames, int frames_len, 77 | const char *fmt, va_list ap); 78 | 79 | /** 80 | * Look up the error message associated with a POSIX error code. 81 | * 82 | * This function is thread-safe. 83 | * 84 | * @param err The POSIX error code (should be non-negative) 85 | * 86 | * @return The error message. This is a statically allocated 87 | * string. 88 | */ 89 | const char *terror(int err); 90 | 91 | /* 92 | * If we don't have ELIBACC, use EIO instead. 93 | */ 94 | #ifndef ELIBACC 95 | #define ELIBACC EIO 96 | #endif 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Locksmith 2 | ====================== 3 | Locksmith is a library for debugging locking in C, C++, or Objective C programs. 4 | 5 | It's designed to catch common locking errors at runtime that might otherwise 6 | lead to deadlocks or crashes. Locksmith is built on top of pthreads. 7 | 8 | How do I build the source code? 9 | ---------------------------------- 10 | ./configure 11 | make 12 | sudo make install 13 | 14 | How to use Locksmith 15 | -------------------------- 16 | Using locksmith is simple. You do not need to recompile your program. Just 17 | run your program with the LD\_PRELOAD environment variable set to the locksmith 18 | library. For example, 19 | 20 | LKSMITH_LOG=syslog LD_PRELOAD=/path/to/liblksmith.so /bin/ls 21 | 22 | What kinds of errors does Locksmith catch? 23 | -------------------------------------------- 24 | 1. Locking inversions. 25 | For example, if one thread locks mutex A and then tries to lock mutex B, and 26 | another thread locks mutex B and then tries to locks mutex A. 27 | 28 | 2. Freeing a mutex, spinlock, or condition variable that you currently hold. 29 | In the pthreads library, freeing a mutex, spinlock, or condition variable that 30 | you currently hold can cause undefined behavior. You must release it first. 31 | Locksmith issues an error message in this case. 32 | 33 | 3. Unlocking a mutex from a different thread than the one which locked it. 34 | This is another scenario which triggers undefined behavior in pthreads, but 35 | which Locksmith turns into a hard error. 36 | 37 | 4. Calling pthread_cond_wait on something that isn't a mutex locked by the 38 | current thread. 39 | 40 | 5. Simultaneously calling pthread_cond_wait on the same condition variable 41 | using different mutexes. 42 | 43 | What choices are available for LKSMITH\_LOG? 44 | ------------------------------------------------- 45 | LKSMITH_LOG=syslog 46 | This will redirect all output to syslog. 47 | 48 | LKSMITH_LOG=stderr 49 | This will redirect all output to standard error. 50 | 51 | LKSMITH_LOG=stdout 52 | This will redirect all output to standard output. 53 | 54 | LKSMITH_LOG=file:///tmp/foo 55 | This will redirect all output to /tmp/foo. Substitute your own file name as appropriate. 56 | 57 | What languages and libraries is Locksmith compatible with? 58 | ------------------------------------------------------------- 59 | Locksmith should be compatible with every library built on top of pthreads in C 60 | or C++. This includes boost::mutex, KDE's QMutex, glib's Glib::Mutex, and so 61 | forth. Locksmith has no problems with global constructors or destructors in 62 | C++. Locksmith also works with mutexes that have been initialized statically 63 | with PTHREAD\_MUTEX\_INITIALIZER. Finally, Locksmith handles pthreads mutexes 64 | created and used in a shared library independent of the main executable. 65 | 66 | What license is Locksmith under? 67 | ------------------------------------------------------------- 68 | Locksmith is released under the 2-clause BSD license. See LICENSE.txt for 69 | details. 70 | 71 | TODO 72 | ------------------------------------------------------------- 73 | * Expose the lock APIs to client code. This will make Locksmith usable in code that implements its own locking primitives. 74 | * Support pthread rwlocks 75 | * Support pthread barriers 76 | * Add the ability to dump out debugging information about the state of all locks on command. 77 | * Support thread cancellation (?) 78 | * Support POSIX semaphores 79 | * Add a way to suppress deadlock warnings through the use of compile-time annotations. 80 | * Add the ability to name mutexes and threads through the use of compile-time annotations. 81 | * Better support for debugging cross-process mutexes and spin-locks (perhaps by putting Locksmith globals into a shared memory segment?) This is tricky because cross-process locks won't have the same memory address in different processes. 82 | 83 | Contact information 84 | ------------------------------------------------------------- 85 | Colin Patrick McCabe 86 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR) 2 | 3 | # The name of our project is "Locksmith". CMakeLists files in this project can 4 | # refer to the root source directory of the project as ${LOCKSMITH_SOURCE_DIR} 5 | # and to the root binary directory of the project as ${LOCKSMITH_BINARY_DIR}. 6 | project(locksmith) 7 | 8 | enable_testing() 9 | 10 | # Define "make check" as an alias for "make test." 11 | add_custom_target(check COMMAND ctest) 12 | 13 | SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Locksmith lock-checking library") 14 | SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt") 15 | SET(CPACK_PACKAGE_VERSION_MAJOR "1") 16 | SET(CPACK_PACKAGE_VERSION_MINOR "0") 17 | SET(CPACK_PACKAGE_CONTACT "cmccabe@alumni.cmu.edu") 18 | SET(CPACK_GENERATOR "TGZ") 19 | INCLUDE(CPack) 20 | 21 | set(CMAKE_BUILD_TYPE, Release) # can also be Debug 22 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fno-strict-aliasing -rdynamic") 23 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_REENTRANT -D_FILE_OFFSET_BITS=64") 24 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") 25 | # We use pthread_key_create with a non-NULL destructor parameter, so we 26 | # cannot allow our shared library to be unloaded. If we did allow this, 27 | # we might get users into a situation where the destructor functions were 28 | # invoked, but they had been unloaded. 29 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -z nodelete") 30 | 31 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules") 32 | 33 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3 -Wuninitialized") 34 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g -O0 -fstack-protector") 35 | set(CMAKE_CURRENT_BINARY_DIR, builds) 36 | if (NOT CMAKE_INSTALL_PREFIX) 37 | set(CMAKE_INSTALL_PREFIX /usr/local) 38 | endif() 39 | 40 | # If our compiler and linker both support the __thread attribute, we can use 41 | # it as a more efficient alternative to POSIX thread-local storage in some 42 | # scenarios. This should always work on Linux. 43 | INCLUDE(CheckCSourceCompiles) 44 | CHECK_C_SOURCE_COMPILES("int main(void) { static __thread int i = 0; return 0; }" HAVE_IMPROVED_TLS) 45 | 46 | SET(USE_LIBUNWIND false) 47 | if(USE_LIBUNWIND) 48 | find_package(Libunwind REQUIRED) 49 | endif(USE_LIBUNWIND) 50 | 51 | CHECK_C_SOURCE_COMPILES("#include 52 | int main(void) { return backtrace(0, 0); }" HAVE_LIBC_BACKTRACE) 53 | 54 | CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h) 55 | 56 | # Set up include paths 57 | include_directories( 58 | ${CMAKE_CURRENT_SOURCE_DIR} 59 | ${CMAKE_BINARY_DIR} 60 | ${LIBUNWIND_INCLUDE_DIR} 61 | ) 62 | 63 | macro(add_utest utest) 64 | add_test(${utest} ${CMAKE_CURRENT_BINARY_DIR}/${utest} ${utest}) 65 | endmacro(add_utest) 66 | 67 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 68 | set(PLATFORM_FILES linux_platform.c) 69 | else() 70 | set(PLATFORM_FILES posix_platform.c) 71 | endif() 72 | 73 | if (USE_LIBUNWIND) 74 | set(PLATFORM_FILES ${PLATFORM_FILES} libunwind_backtrace.c) 75 | elseif (HAVE_LIBC_BACKTRACE) 76 | set(PLATFORM_FILES ${PLATFORM_FILES} libc_backtrace.c) 77 | else() 78 | MESSAGE(FATAL_ERROR "can't find any backtrace scheme to use. Perhaps install libunwind.") 79 | endif() 80 | 81 | add_library(lksmith SHARED 82 | ${PLATFORM_FILES} 83 | error.c 84 | lksmith.c 85 | handler.c 86 | util.c 87 | ) 88 | 89 | target_link_libraries(lksmith pthread) 90 | INSTALL(TARGETS lksmith LIBRARY DESTINATION lib) 91 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 92 | target_link_libraries(lksmith dl) 93 | endif() 94 | if(USE_LIBUNWIND) 95 | target_link_libraries(lksmith ${LIBUNWIND_LIBRARIES}) 96 | endif(USE_LIBUNWIND) 97 | 98 | add_executable(thread_unit test.c thread_unit.c test.c mem.c) 99 | target_link_libraries(thread_unit lksmith) 100 | add_utest(thread_unit) 101 | 102 | add_executable(simple_unit test.c simple_unit.c test.c mem.c) 103 | target_link_libraries(simple_unit lksmith) 104 | add_utest(simple_unit) 105 | 106 | add_executable(error_unit test.c error_unit.c test.c mem.c) 107 | target_link_libraries(error_unit lksmith) 108 | add_utest(error_unit) 109 | 110 | add_executable(ignore_unit test.c ignore_unit.c test.c mem.c) 111 | target_link_libraries(ignore_unit lksmith) 112 | add_utest(ignore_unit) 113 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "error.h" 31 | #include "handler.h" 32 | #include "test.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | void die_on_error(int code, const char *msg) 44 | { 45 | if (code == 0) 46 | return; 47 | fprintf(stderr, "die_on_error: got error %d: %s\n", code, msg); 48 | abort(); 49 | } 50 | 51 | static char g_lksmith_log_env[4096]; 52 | 53 | void set_error_cb(lksmith_error_cb_t cb) 54 | { 55 | snprintf(g_lksmith_log_env, sizeof(g_lksmith_log_env), 56 | "LKSMITH_LOG=callback://0x%"PRIxPTR, (uintptr_t)cb); 57 | putenv(g_lksmith_log_env); 58 | } 59 | 60 | struct recorded_error { 61 | int code; 62 | struct recorded_error *next; 63 | }; 64 | 65 | static struct recorded_error *g_recorded_errors; 66 | 67 | static pthread_mutex_t g_recorded_errors_lock = PTHREAD_MUTEX_INITIALIZER; 68 | 69 | void record_error(int code, const char *msg __attribute__((unused))) 70 | { 71 | struct recorded_error *rec; 72 | 73 | if (code == 0) 74 | return; 75 | rec = calloc(1, sizeof(*rec)); 76 | if (!rec) 77 | abort(); 78 | rec->code = code; 79 | r_pthread_mutex_lock(&g_recorded_errors_lock); 80 | rec->next = g_recorded_errors; 81 | g_recorded_errors = rec; 82 | r_pthread_mutex_unlock(&g_recorded_errors_lock); 83 | } 84 | 85 | void clear_recorded_errors(void) 86 | { 87 | struct recorded_error *rec, *next; 88 | 89 | r_pthread_mutex_lock(&g_recorded_errors_lock); 90 | rec = g_recorded_errors; 91 | while (1) { 92 | if (!rec) 93 | break; 94 | next = rec->next; 95 | free(rec); 96 | rec = next; 97 | } 98 | g_recorded_errors = NULL; 99 | r_pthread_mutex_unlock(&g_recorded_errors_lock); 100 | } 101 | 102 | int find_recorded_error(int expect) 103 | { 104 | int found = 0; 105 | struct recorded_error **rec, *cur; 106 | 107 | r_pthread_mutex_lock(&g_recorded_errors_lock); 108 | rec = &g_recorded_errors; 109 | while (1) { 110 | cur = *rec; 111 | if (!cur) 112 | break; 113 | if (expect == cur->code) { 114 | found = 1; 115 | *rec = cur->next; 116 | free(cur); 117 | break; 118 | } 119 | rec = &(*rec)->next; 120 | } 121 | r_pthread_mutex_unlock(&g_recorded_errors_lock); 122 | return found; 123 | } 124 | 125 | int num_recorded_errors(void) 126 | { 127 | struct recorded_error *rec; 128 | int num = 0; 129 | 130 | r_pthread_mutex_lock(&g_recorded_errors_lock); 131 | for (rec = g_recorded_errors; rec; rec = rec->next) { 132 | num++; 133 | } 134 | r_pthread_mutex_unlock(&g_recorded_errors_lock); 135 | return num; 136 | } 137 | 138 | int get_current_timespec(struct timespec *ts) 139 | { 140 | struct timeval tv; 141 | 142 | if (gettimeofday(&tv, NULL)) 143 | return errno; 144 | ts->tv_nsec = tv.tv_usec * 1000; 145 | ts->tv_sec = tv.tv_sec; 146 | return 0; 147 | } 148 | 149 | void timespec_add_milli(struct timespec *ts, unsigned int ms) 150 | { 151 | uint64_t nsec = ts->tv_nsec; 152 | nsec += (ms * 1000); 153 | if (nsec > 1000000000) { 154 | ts->tv_sec++; 155 | ts->tv_nsec = nsec - 1000000000; 156 | } else { 157 | ts->tv_nsec = nsec; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /lock_unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "lksmith.h" 31 | #include "test.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | static int test_multi_mutex_lock(signed int max_locks) 40 | { 41 | signed int i; 42 | struct lksmith_mutex *mutex; 43 | static char name[LKSMITH_LOCK_NAME_MAX]; 44 | 45 | mutex = xcalloc(sizeof(struct lksmith_mutex) * max_locks); 46 | for (i = 0; i < max_locks; i++) { 47 | snprintf(name, sizeof(name), "test_multi_%04d", i); 48 | EXPECT_ZERO(lksmith_mutex_init(name, &mutex[i], NULL)); 49 | } 50 | for (i = 0; i < max_locks; i++) { 51 | EXPECT_ZERO(lksmith_mutex_lock(&mutex[i])); 52 | } 53 | for (i = max_locks - 1; i >= 0; i--) { 54 | EXPECT_ZERO(lksmith_mutex_unlock(&mutex[i])); 55 | } 56 | for (i = 0; i < max_locks; i++) { 57 | EXPECT_ZERO(lksmith_mutex_destroy(&mutex[i])); 58 | } 59 | return 0; 60 | } 61 | 62 | struct contention_data { 63 | struct lksmith_mutex *locks; 64 | int num_locks; 65 | int *thread_data; 66 | pthread_t *threads; 67 | int num_threads; 68 | }; 69 | 70 | static struct contention_data cdata; 71 | 72 | static int do_test_thread_contention_impl(int idx) 73 | { 74 | int i; 75 | 76 | while (cdata.thread_data[idx]-- > 0) { 77 | for (i = 0; i < cdata.num_locks; i++) { 78 | EXPECT_ZERO(lksmith_mutex_lock(&cdata.locks[i])); 79 | pthread_yield(); 80 | } 81 | for (i = 0; i < cdata.num_locks; i++) { 82 | EXPECT_ZERO(lksmith_mutex_unlock( 83 | &cdata.locks[cdata.num_locks - i - 1])); 84 | pthread_yield(); 85 | } 86 | } 87 | return 0; 88 | } 89 | 90 | static void *do_test_thread_contention(void *v) 91 | { 92 | return (void*)(intptr_t) 93 | do_test_thread_contention_impl((int)(intptr_t)v); 94 | } 95 | 96 | static int test_thread_contention(int num_locks, int num_threads) 97 | { 98 | int i; 99 | char name[LKSMITH_LOCK_NAME_MAX]; 100 | void *rval; 101 | 102 | memset(&cdata, 0, sizeof(cdata)); 103 | cdata.locks = xcalloc(sizeof(struct lksmith_mutex) * num_locks); 104 | cdata.num_locks = num_locks; 105 | cdata.thread_data = xcalloc(sizeof(int) * num_threads); 106 | cdata.threads = xcalloc(sizeof(pthread_t) * num_threads); 107 | cdata.num_threads = num_threads; 108 | 109 | for (i = 0; i < num_locks; i++) { 110 | snprintf(name, sizeof(name), "test_cnt_%04d", i); 111 | EXPECT_ZERO(lksmith_mutex_init(name, &cdata.locks[i], NULL)); 112 | cdata.thread_data[i] = 15; 113 | } 114 | for (i = 0; i < num_threads; i++) { 115 | EXPECT_ZERO(pthread_create(&cdata.threads[i], NULL, 116 | do_test_thread_contention, (void*)(intptr_t)i)); 117 | } 118 | for (i = 0; i < num_threads; i++) { 119 | pthread_join(cdata.threads[i], &rval); 120 | EXPECT_EQ(rval, NULL); 121 | } 122 | for (i = 0; i < num_locks; i++) { 123 | lksmith_mutex_destroy(&cdata.locks[i]); 124 | } 125 | free(cdata.locks); 126 | free(cdata.thread_data); 127 | free(cdata.threads); 128 | 129 | return 0; 130 | } 131 | 132 | int main(void) 133 | { 134 | lksmith_set_error_cb(die_on_error); 135 | 136 | EXPECT_ZERO(test_multi_mutex_lock(5)); 137 | EXPECT_ZERO(test_multi_mutex_lock(100)); 138 | EXPECT_ZERO(test_thread_contention(3, 2)); 139 | EXPECT_ZERO(test_thread_contention(2, 3)); 140 | EXPECT_ZERO(test_thread_contention(15, 60)); 141 | return EXIT_SUCCESS; 142 | } 143 | -------------------------------------------------------------------------------- /simple_unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "test.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | static int test_mutex_init_teardown(void) 40 | { 41 | pthread_mutex_t mutex; 42 | EXPECT_ZERO(pthread_mutex_init(&mutex, NULL)); 43 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 44 | return 0; 45 | } 46 | 47 | static int test_mutex_static_init_teardown(void) 48 | { 49 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 50 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 51 | return 0; 52 | } 53 | 54 | static int test_spin_init_teardown(void) 55 | { 56 | pthread_spinlock_t spin; 57 | EXPECT_ZERO(pthread_spin_init(&spin, 0)); 58 | EXPECT_ZERO(pthread_spin_destroy(&spin)); 59 | return 0; 60 | } 61 | 62 | static int test_mutex_lock_simple(void) 63 | { 64 | pthread_mutex_t mutex; 65 | struct timespec ts; 66 | 67 | EXPECT_ZERO(pthread_mutex_init(&mutex, NULL)); 68 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 69 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 70 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 71 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 72 | EXPECT_ZERO(pthread_mutex_trylock(&mutex)); 73 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 74 | EXPECT_ZERO(get_current_timespec(&ts)); 75 | timespec_add_milli(&ts, 50); 76 | EXPECT_ZERO(pthread_mutex_timedlock(&mutex, &ts)); 77 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 78 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 79 | return 0; 80 | } 81 | 82 | static int test_mutex_lock_simple_static(void) 83 | { 84 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 85 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 86 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 87 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 88 | return 0; 89 | } 90 | 91 | static int test_spin_lock_simple(void) 92 | { 93 | pthread_mutex_t mutex; 94 | EXPECT_ZERO(pthread_mutex_init(&mutex, NULL)); 95 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 96 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 97 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 98 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 99 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 100 | return 0; 101 | } 102 | 103 | static int test_recursive_mutex(void) 104 | { 105 | pthread_mutexattr_t attr; 106 | pthread_mutex_t mutex; 107 | int ty = PTHREAD_MUTEX_RECURSIVE; 108 | 109 | EXPECT_ZERO(pthread_mutexattr_init(&attr)); 110 | EXPECT_ZERO(pthread_mutexattr_settype(&attr, ty)); 111 | EXPECT_ZERO(pthread_mutex_init(&mutex, &attr)); 112 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 113 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 114 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 115 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 116 | EXPECT_ZERO(pthread_mutexattr_destroy(&attr)); 117 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 118 | return 0; 119 | } 120 | 121 | int main(void) 122 | { 123 | set_error_cb(die_on_error); 124 | EXPECT_ZERO(test_mutex_init_teardown()); 125 | EXPECT_ZERO(test_mutex_static_init_teardown()); 126 | EXPECT_ZERO(test_mutex_init_teardown()); 127 | EXPECT_ZERO(test_spin_init_teardown()); 128 | EXPECT_ZERO(test_mutex_lock_simple()); 129 | EXPECT_ZERO(test_mutex_lock_simple_static()); 130 | EXPECT_ZERO(test_spin_lock_simple()); 131 | EXPECT_ZERO(test_recursive_mutex()); 132 | 133 | return EXIT_SUCCESS; 134 | } 135 | -------------------------------------------------------------------------------- /test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_UTIL_TEST_H 31 | #define LKSMITH_UTIL_TEST_H 32 | 33 | #include "error.h" /* for lksmith_error_cb_t */ 34 | 35 | #include /* for fprintf */ 36 | 37 | struct timespec; 38 | 39 | /** 40 | * Set the Locksmith error handling callback. 41 | * 42 | * @param cb The callback 43 | */ 44 | void set_error_cb(lksmith_error_cb_t cb); 45 | 46 | /** 47 | * Error handling function that just aborts. 48 | * 49 | * @param code The error code 50 | * @param msg The error message 51 | */ 52 | void die_on_error(int code, const char *msg); 53 | 54 | /** 55 | * Error handling function that records errors. 56 | * 57 | * @param code The error code 58 | * @param msg The error message 59 | */ 60 | void record_error(int code, const char *msg); 61 | 62 | /** 63 | * Clear all errors recorded by record_error. 64 | */ 65 | void clear_recorded_errors(void); 66 | 67 | /** 68 | * Find a recorded error code and clear it. 69 | * 70 | * @param expect The error code to search for. 71 | * 72 | * @return 0 if the error code was not found; 1 if it was. 73 | */ 74 | int find_recorded_error(int expect); 75 | 76 | /** 77 | * @return the number of recorded errors. 78 | */ 79 | int num_recorded_errors(void); 80 | 81 | /** 82 | * Get the current time 83 | * 84 | * @param ts (out param) the current time 85 | * 86 | * @return 0 on success; gettimeofday errno otherwise 87 | */ 88 | int get_current_timespec(struct timespec *ts); 89 | 90 | /** 91 | * Increment a timespec by a few milliseconds 92 | * 93 | * @param ts (out param) the timespec 94 | * @param ms The number of milliseconds to add 95 | */ 96 | void timespec_add_milli(struct timespec *ts, unsigned int ms); 97 | 98 | #define EXPECT_ZERO(x) \ 99 | do { \ 100 | int __my_ret__ = x; \ 101 | if (__my_ret__) { \ 102 | fprintf(stderr, "failed on line %d: %s\n",\ 103 | __LINE__, #x); \ 104 | return __my_ret__; \ 105 | } \ 106 | } while (0); 107 | 108 | #define EXPECT_NONZERO(x) \ 109 | do { \ 110 | int __my_ret__ = x; \ 111 | if (__my_ret__ == 0) { \ 112 | fprintf(stderr, "failed on line %d: %s\n",\ 113 | __LINE__, #x); \ 114 | return -1; \ 115 | } \ 116 | } while (0); 117 | 118 | #define EXPECT_POSITIVE(x) \ 119 | do { \ 120 | int __my_ret__ = x; \ 121 | if (__my_ret__ < 0) { \ 122 | fprintf(stderr, "failed on line %d: %s\n",\ 123 | __LINE__, #x); \ 124 | return __my_ret__; \ 125 | } \ 126 | } while (0); 127 | 128 | #define EXPECT_EQ(x, y) \ 129 | do { \ 130 | if ((x) != (y)) { \ 131 | fprintf(stderr, "failed on line %d: %s\n",\ 132 | __LINE__, #x); \ 133 | return 1; \ 134 | } \ 135 | } while (0); 136 | 137 | #define EXPECT_NOT_EQ(x, y) \ 138 | do { \ 139 | if ((x) == (y)) { \ 140 | fprintf(stderr, "failed on line %d: %s\n",\ 141 | __LINE__, #x); \ 142 | return 1; \ 143 | } \ 144 | } while (0); 145 | 146 | #define EXPECT_LT(x, y) \ 147 | do { \ 148 | if ((x) >= (y)) { \ 149 | fprintf(stderr, "failed on line %d: %s\n",\ 150 | __LINE__, #x); \ 151 | return 1; \ 152 | } \ 153 | } while (0); 154 | 155 | #define EXPECT_GE(x, y) \ 156 | do { \ 157 | if ((x) < (y)) { \ 158 | fprintf(stderr, "failed on line %d: %s\n",\ 159 | __LINE__, #x); \ 160 | return 1; \ 161 | } \ 162 | } while (0); 163 | 164 | #define EXPECT_GT(x, y) \ 165 | do { \ 166 | if ((x) <= (y)) { \ 167 | fprintf(stderr, "failed on line %d: %s\n",\ 168 | __LINE__, #x); \ 169 | return 1; \ 170 | } \ 171 | } while (0); 172 | 173 | #endif 174 | -------------------------------------------------------------------------------- /libunwind_backtrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2013, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "error.h" 31 | #include "backtrace.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define STACK_BUF_SZ 512 39 | 40 | static int do_get_proc_fname(unw_cursor_t *cursor, char **out_fname, 41 | char **heap_buf, size_t *heap_buf_len) 42 | { 43 | int ret; 44 | unw_word_t offset; 45 | char *next_heap_buf = NULL, *stack_buf = NULL, *fname; 46 | size_t len = *heap_buf_len; 47 | 48 | while (1) { 49 | if (!len) { 50 | stack_buf = alloca(STACK_BUF_SZ); 51 | ret = unw_get_proc_name(cursor, stack_buf, 52 | STACK_BUF_SZ, &offset); 53 | } else { 54 | ret = unw_get_proc_name(cursor, *heap_buf, 55 | len, &offset); 56 | } 57 | if (ret == 0) { 58 | break; 59 | } else if (ret != -UNW_ENOMEM) { 60 | lksmith_error(ENOMEM, "create_backtrace " 61 | "failed: unw_get_proc_name failed " 62 | "with error %d\n", ret); 63 | return EIO; 64 | } 65 | len = len ? (len * 2) : (STACK_BUF_SZ * 2); 66 | next_heap_buf = realloc(*heap_buf, len); 67 | if (!next_heap_buf) { 68 | lksmith_error(ENOMEM, "create_backtrace " 69 | "failed: failed to allocate buffer of " 70 | "size %zd to hold proc name\n", len); 71 | return ENOMEM; 72 | } 73 | *heap_buf = next_heap_buf; 74 | *heap_buf_len = len; 75 | } 76 | fname = strdup(len ? *heap_buf : stack_buf); 77 | if (!fname) { 78 | lksmith_error(ENOMEM, "create_backtrace failed: failed " 79 | "to allocate buffer to hold proc name\n"); 80 | return ENOMEM; 81 | } 82 | *out_fname = fname; 83 | return 0; 84 | } 85 | 86 | void bt_frames_free(char **backtrace) 87 | { 88 | char **b; 89 | 90 | if (!backtrace) 91 | return; 92 | for (b = backtrace; *b; b++) { 93 | free(*b); 94 | } 95 | free(backtrace); 96 | } 97 | 98 | int bt_frames_create(void ***scratch __attribute__((__unused__)), 99 | int *scratch_len __attribute__((__unused__)), char ***out) 100 | { 101 | int ret; 102 | unw_cursor_t cursor; 103 | unw_context_t context; 104 | char *heap_buf = NULL; 105 | size_t heap_buf_len = 0; 106 | char **backtrace = NULL, **backtrace_new; 107 | size_t backtrace_len = 0, cap = 0; 108 | 109 | if (unw_getcontext(&context)) { 110 | lksmith_error(ENOMEM, "bt_frames_create failed: " 111 | "unw_getcontext failed.\n"); 112 | return -EIO; 113 | } 114 | ret = unw_init_local(&cursor, &context); 115 | if (ret) { 116 | lksmith_error(ENOMEM, "bt_frames_create failed: " 117 | "unw_init_local failed with error %d\n", ret); 118 | return -EIO; 119 | } 120 | while (unw_step(&cursor) > 0) { 121 | if (++backtrace_len > cap) { 122 | size_t new_cap = cap ? cap * 2 : 32; 123 | backtrace_new = realloc(backtrace, 124 | sizeof(char*) * new_cap); 125 | if (!backtrace_new) { 126 | lksmith_error(ENOMEM, "bt_frames_create " 127 | "failed: failed to allocate char* " 128 | "array of length %zd\n", new_cap); 129 | ret = -EIO; 130 | goto done; 131 | } 132 | backtrace = backtrace_new; 133 | cap = new_cap; 134 | } 135 | ret = do_get_proc_fname(&cursor, &backtrace[backtrace_len - 1], 136 | &heap_buf, &heap_buf_len); 137 | if (ret != 0) { 138 | lksmith_error(ENOMEM, "bt_frames_create failed: " 139 | "do_get_proc_fname failed with error %d\n", 140 | ret); 141 | ret = -EIO; 142 | goto done; 143 | } 144 | } 145 | backtrace[backtrace_len] = NULL; 146 | ret = backtrace_len; 147 | done: 148 | free(heap_buf); 149 | if (ret < 0) { 150 | bt_frames_free(backtrace); 151 | return ret; 152 | } 153 | *out = backtrace; 154 | return ret; 155 | } 156 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "error.h" 31 | #include "handler.h" 32 | #include "util.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | enum lksmith_log_type { 43 | LKSMITH_LOG_UNINIT = 0, 44 | LKSMITH_LOG_SYSLOG, 45 | LKSMITH_LOG_FILE, 46 | LKSMITH_LOG_CALLBACK, 47 | }; 48 | 49 | #define DEFAULT_LKSMITH_LOG_TYPE "stderr" 50 | 51 | #define FILE_PREFIX "file://" 52 | #define CALLBACK_PREFIX "callback://" 53 | 54 | static enum lksmith_log_type g_log_type; 55 | 56 | /** 57 | * Locksmith error callback to use. 58 | */ 59 | static lksmith_error_cb_t g_error_cb; 60 | 61 | /** 62 | * Log file 63 | */ 64 | static FILE *g_log_file; 65 | 66 | /** 67 | * Protects g_error_cb. This is not held while the callback is in progress, 68 | * though. 69 | */ 70 | static pthread_mutex_t g_error_lock = PTHREAD_MUTEX_INITIALIZER; 71 | 72 | static void lksmith_log_init_file(const char *name) 73 | { 74 | int err; 75 | 76 | g_log_type = LKSMITH_LOG_FILE; 77 | g_log_file = fopen(name, "w"); 78 | if (!g_log_file) { 79 | err = errno; 80 | fprintf(stderr, "Unable to open '%s': error %d: %s\n" 81 | "redirecting output to stderr.\n", 82 | name, err, terror(err)); 83 | g_log_file = stderr; 84 | return; 85 | } 86 | } 87 | 88 | static int lksmith_log_init_cb(const char *addr_str) 89 | { 90 | int err; 91 | unsigned long long int addr; 92 | 93 | if ((addr_str[0] != '0') || (addr_str[1] != 'x')) { 94 | fprintf(stderr, "Invalid callback address '%s'.\n" 95 | "Callback address must begin with 0x.\n" 96 | "Redirecting output to stderr.\n", addr_str); 97 | return EINVAL; 98 | } 99 | errno = 0; 100 | addr = strtoull(addr_str, NULL, 16); 101 | err = errno; 102 | if (err) { 103 | fprintf(stderr, "Unable to parse callback address '%s'.\n" 104 | "error %d: %s.\n" 105 | "Redirecting output to stderr.\n", 106 | addr_str, err, terror(err)); 107 | return err; 108 | } 109 | g_error_cb = (lksmith_error_cb_t)addr; 110 | g_log_type = LKSMITH_LOG_CALLBACK; 111 | return 0; 112 | } 113 | 114 | static void lksmith_log_init(void) 115 | { 116 | const char *ty; 117 | ty = getenv("LKSMITH_LOG"); 118 | if (!ty) 119 | ty = DEFAULT_LKSMITH_LOG_TYPE; 120 | if (!strcmp(ty, "syslog")) { 121 | g_log_type = LKSMITH_LOG_SYSLOG; 122 | } else if (!strcmp(ty, "stderr")) { 123 | g_log_type = LKSMITH_LOG_FILE; 124 | g_log_file = stderr; 125 | } else if (!strcmp(ty, "stdout")) { 126 | g_log_type = LKSMITH_LOG_FILE; 127 | g_log_file = stdout; 128 | } else if (strstr(ty, FILE_PREFIX) == ty) { 129 | lksmith_log_init_file(ty + strlen(FILE_PREFIX)); 130 | } else if (strstr(ty, CALLBACK_PREFIX) == ty) { 131 | if (lksmith_log_init_cb(ty + strlen(CALLBACK_PREFIX))) { 132 | g_log_type = LKSMITH_LOG_FILE; 133 | g_log_file = stderr; 134 | } 135 | } else { 136 | fprintf(stderr, "Sorry, unable to understand log target '%s'. " 137 | "redirecting output to stderr.\n", ty); 138 | g_log_type = LKSMITH_LOG_FILE; 139 | g_log_file = stderr; 140 | } 141 | } 142 | 143 | static void lksmith_errora_unlocked(int err, const char *fmt, va_list ap) 144 | { 145 | if (g_log_type == LKSMITH_LOG_UNINIT) { 146 | lksmith_log_init(); 147 | } 148 | if (g_log_type == LKSMITH_LOG_SYSLOG) { 149 | vsyslog(LOG_USER | LOG_INFO, fmt, ap); 150 | } else if (g_log_type == LKSMITH_LOG_FILE) { 151 | vfprintf(g_log_file, fmt, ap); 152 | } else if (g_log_type == LKSMITH_LOG_CALLBACK) { 153 | char buf[4096]; 154 | vsnprintf(buf, sizeof(buf), fmt, ap); 155 | g_error_cb(err, buf); 156 | } 157 | } 158 | 159 | static void lksmith_error_unlocked(int err, const char *fmt, ...) 160 | { 161 | va_list ap; 162 | 163 | va_start(ap, fmt); 164 | lksmith_errora_unlocked(err, fmt, ap); 165 | va_end(ap); 166 | } 167 | 168 | void lksmith_error(int err, const char *fmt, ...) 169 | { 170 | va_list ap; 171 | 172 | va_start(ap, fmt); 173 | lksmith_errora(err, fmt, ap); 174 | va_end(ap); 175 | } 176 | 177 | void lksmith_errora(int err, const char *fmt, va_list ap) 178 | { 179 | r_pthread_mutex_lock(&g_error_lock); 180 | lksmith_errora_unlocked(err, fmt, ap); 181 | r_pthread_mutex_unlock(&g_error_lock); 182 | } 183 | 184 | void lksmith_errora_with_bt(int err, char **frames, int frames_len, 185 | const char *fmt, va_list ap) 186 | { 187 | int i; 188 | 189 | r_pthread_mutex_lock(&g_error_lock); 190 | lksmith_errora_unlocked(err, fmt, ap); 191 | for (i = 0; i < frames_len; i++) { 192 | lksmith_error_unlocked(0, "%s\n", frames[i]); 193 | } 194 | r_pthread_mutex_unlock(&g_error_lock); 195 | } 196 | 197 | const char *terror(int err) 198 | { 199 | #ifdef HAVE_IMPROVED_TLS 200 | static __thread char buf[4096]; 201 | int ret; 202 | 203 | ret = strerror_r(err, buf, sizeof(buf)); 204 | if (ret) 205 | return "unknown error"; 206 | return buf; 207 | #else 208 | if ((err < 0) || (err >= sys_nerr)) { 209 | return "unknown error"; 210 | } 211 | return sys_errlist[err]; 212 | #endif 213 | } 214 | 215 | -------------------------------------------------------------------------------- /lksmith.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef LKSMITH_H 31 | #define LKSMITH_H 32 | 33 | #include /* for uint32_t, etc. */ 34 | #include /* for size_t */ 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | struct lksmith_cond; 41 | 42 | /****************************************************************** 43 | * Locksmith macros 44 | *****************************************************************/ 45 | /** The current Locksmith version. 46 | * 47 | * Format: the first 16 bits are the major version; the second 16 bits are the 48 | * minor version. Changes in the major version break the ABI; minor version 49 | * changes may add to the ABI, but they never break it. 50 | * 51 | * Use lksmith_verion_to_str to get a human-readable version. 52 | */ 53 | #define LKSMITH_API_VERSION 0x0001000 54 | 55 | /** 56 | * Maximum length of a thread name, including the terminating NULL byte. 57 | */ 58 | #define LKSMITH_THREAD_NAME_MAX 16 59 | 60 | /****************************************************************** 61 | * Locksmith API 62 | *****************************************************************/ 63 | /** 64 | * Get the current Locksmith API version 65 | * 66 | * @return The current locksmith API version 67 | */ 68 | uint32_t lksmith_get_version(void); 69 | 70 | /** 71 | * Convert the current Locksmith API version to a human-readable string. 72 | * This function is thread-safe. 73 | * 74 | * @param ver A locksmith API version 75 | * @param str (out parameter) buffer to place the version string 76 | * @param str_len Length of the str buffer 77 | * 78 | * @return 0 on success; -ENAMETOOLONG if the provided buffer 79 | * length was too short; -EIO if another failure happened. 80 | */ 81 | int lksmith_verion_to_str(uint32_t ver, char *str, size_t str_len); 82 | 83 | /** 84 | * Initialize thread-local storage and function stubs. 85 | * TODO: move this to internal header. 86 | * 87 | * @return 0 on success; negative error code otherwise. 88 | */ 89 | int init_tls(void); 90 | 91 | /** 92 | * Initialize a locksmith lock. This function is optional. 93 | * 94 | * @param ptr pointer to the lock to initialize 95 | * @param recursive 1 to allow recursive locks; 0 otherwise 96 | * @param sleeper 1 if this lock is a sleeper; 0 otherwise 97 | * 98 | * @return 0 on success; error code otherwise 99 | */ 100 | int lksmith_optional_init(const void *ptr, int recursive, int sleeper); 101 | 102 | /** 103 | * Destroy a lock. 104 | * 105 | * @param ptr pointer to the lock to destroy 106 | * 107 | * @return 0 if the lock was destroyed; 108 | * ENOENT if we're not aware of any such lock 109 | */ 110 | int lksmith_destroy(const void *ptr); 111 | 112 | /** 113 | * Destroy a lock. 114 | * 115 | * @param ptr pointer to the lock to destroy 116 | */ 117 | void lksmith_postdestroy(const void *ptr); 118 | 119 | /** 120 | * Perform some error checking before taking a lock. 121 | * 122 | * @param ptr pointer to the lock 123 | * @param sleeper 1 if this lock is a sleeper; 0 otherwise 124 | * 125 | * @return 0 if we should continue with the lock; error code 126 | * otherwise. We may print an error even if 0 is 127 | * returned. 128 | */ 129 | int lksmith_prelock(const void *ptr, int sleeper); 130 | 131 | /** 132 | * Take a lock. 133 | * 134 | * @param ptr pointer to the lock. 135 | */ 136 | void lksmith_postlock(const void *ptr, int error); 137 | 138 | /** 139 | * Determine if it's safe to release a lock. 140 | * 141 | * @param ptr pointer to the lock. 142 | * 143 | * @return 0 on success, or the error code. 144 | */ 145 | int lksmith_preunlock(const void *ptr); 146 | 147 | /** 148 | * Release a lock. 149 | * 150 | * @param ptr pointer to the lock. 151 | */ 152 | void lksmith_postunlock(const void *ptr); 153 | 154 | /** 155 | * Check if the current thread holds the given lock. 156 | * 157 | * @param ptr pointer to the lock to check for 158 | * 159 | * @return -1 if the thread does not hold the lock; 160 | * 0 if the thread holds the lock; 161 | * a positive error code otherwise. 162 | */ 163 | int lksmith_check_locked(const void *ptr); 164 | 165 | /** 166 | * Register a given condition variable as about to wait. 167 | * 168 | * @param cond pointer to the condition variable 169 | * @param mutex pointer to the mutex to be used with cond 170 | * @param out (out param) on success, a pointer to be used 171 | * with lksmith_cond_postwait. 172 | * 173 | * @return 0 on success; error code otherwise. 174 | */ 175 | int lksmith_cond_prewait(const void *cond, const void *mutex, 176 | struct lksmith_cond **out); 177 | 178 | /** 179 | * Unregister a given condition variable as about to wait. 180 | * 181 | * @param cnd pointer returned from lksmith_cond_prewait 182 | */ 183 | void lksmith_cond_postwait(struct lksmith_cond *cnd); 184 | 185 | /** 186 | * Destroy a given condition variable. 187 | * 188 | * @param cond the condition variable 189 | */ 190 | int lksmith_cond_predestroy(const void *cond); 191 | 192 | /** 193 | * Set the thread name. 194 | * 195 | * @param name The name to use for this thread. 196 | * This string will be deep-copied. 197 | * The copy will be truncated to LKSMITH_THREAD_NAME_MAX 198 | * bytes long, including the terminating null. 199 | * 200 | * @return 0 on success; error code otherwise 201 | */ 202 | int lksmith_set_thread_name(const char * const name); 203 | 204 | /** 205 | * Get the thread name. 206 | * 207 | * @return The thread name. This is allocated as a thread-local 208 | * string. Returns NULL if we failed to allocate 209 | * thread-local data. 210 | */ 211 | const char* lksmith_get_thread_name(void); 212 | 213 | #ifdef __cplusplus 214 | } 215 | #endif 216 | 217 | #endif 218 | -------------------------------------------------------------------------------- /handler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #define LKSMITH_HANDLER_DOT_C 31 | 32 | #include "error.h" 33 | #include "lksmith.h" 34 | #include "util.h" 35 | #include "handler.h" 36 | #include "platform.h" 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | /** 45 | * Handler functions used to redirect pthreads calls to Locksmith. 46 | */ 47 | 48 | /** 49 | * A list of mutex types that are compatible with error checking mutexes. 50 | * Note that recursive mutexes are NOT compatible. 51 | */ 52 | static const int g_compatible_with_errcheck[] = { 53 | #ifdef PTHREAD_MUTEX_TIMED_NP 54 | PTHREAD_MUTEX_TIMED_NP, 55 | #endif 56 | #ifdef PTHREAD_MUTEX_ADAPTIVE_NP 57 | PTHREAD_MUTEX_ADAPTIVE_NP, 58 | #endif 59 | #ifdef PTHREAD_MUTEX_FAST_NP 60 | PTHREAD_MUTEX_FAST_NP, 61 | #endif 62 | PTHREAD_MUTEX_NORMAL, 63 | PTHREAD_MUTEX_DEFAULT 64 | }; 65 | 66 | #define NUM_COMPATIBLE_WITH_ERRCHECK (sizeof(g_compatible_with_errcheck) / \ 67 | sizeof(g_compatible_with_errcheck[0])) 68 | 69 | static int is_compatible_with_errcheck(int ty) 70 | { 71 | unsigned int i; 72 | 73 | for (i = 0; i < NUM_COMPATIBLE_WITH_ERRCHECK; i++) { 74 | if (ty == g_compatible_with_errcheck[i]) 75 | return 1; 76 | } 77 | return 0; 78 | } 79 | 80 | static int pthread_mutex_init_errcheck(pthread_mutex_t *mutex) 81 | { 82 | int ret; 83 | pthread_mutexattr_t attr; 84 | 85 | ret = pthread_mutexattr_init(&attr); 86 | if (ret) { 87 | lksmith_error(ret, "pthread_mutexattr_init failed " 88 | "with error code %d: %s\n", ret, terror(ret)); 89 | goto done; 90 | } 91 | ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); 92 | if (ret) { 93 | lksmith_error(ret, "pthread_mutexattr_settype failed " 94 | "with error code %d: %s\n", ret, terror(ret)); 95 | goto done_free_mutexattr; 96 | } 97 | ret = r_pthread_mutex_init(mutex, &attr); 98 | if (ret) { 99 | lksmith_error(ret, "pthread_mutex_init failed " 100 | "with error code %d: %s\n", ret, terror(ret)); 101 | goto done_free_mutexattr; 102 | } 103 | done_free_mutexattr: 104 | pthread_mutexattr_destroy(&attr); 105 | done: 106 | return ret; 107 | } 108 | 109 | static int pthread_mutex_real_init(pthread_mutex_t *mutex, 110 | pthread_mutexattr_t *attr, int *recursive) 111 | { 112 | int ret, ty = 0; 113 | 114 | *recursive = 0; 115 | if (!attr) { 116 | /* No mutex attributes provided. Initialize this as an error 117 | * checking mutex. */ 118 | return pthread_mutex_init_errcheck(mutex); 119 | } 120 | ret = pthread_mutexattr_gettype(attr, &ty); 121 | if (ret == EINVAL) { 122 | lksmith_error(ret, "pthread_mutexattr_gettype failed " 123 | "with error code %d: %s\n", ret, terror(ret)); 124 | return ret; 125 | } 126 | if (is_compatible_with_errcheck(ty)) { 127 | /* If the requested mutex type is compatible with the error 128 | * checking type, let's use that type instead, for extra 129 | * safety. */ 130 | ret = pthread_mutexattr_settype(attr, 131 | PTHREAD_MUTEX_ERRORCHECK); 132 | if (ret) { 133 | lksmith_error(ret, "pthread_mutexattr_settype failed " 134 | "with error code %d: %s\n", ret, terror(ret)); 135 | return ret; 136 | } 137 | } else { 138 | /* If we don't know about the requested mutex type, we assume 139 | * that it's recursive, to be on the safe side. 140 | */ 141 | *recursive = 1; 142 | } 143 | ret = r_pthread_mutex_init(mutex, attr); 144 | if (ret) { 145 | lksmith_error(ret, "pthread_mutex_init failed " 146 | "with error code %d: %s\n", ret, terror(ret)); 147 | return ret; 148 | } 149 | return ret; 150 | } 151 | 152 | int pthread_mutex_init(pthread_mutex_t *mutex, 153 | const pthread_mutexattr_t *attr) 154 | { 155 | int ret, recursive = 0; 156 | 157 | ret = init_tls(); 158 | if (ret) 159 | return ret; 160 | 161 | /* 162 | * We have to cast away the const here, because the alternative, 163 | * copying the pthread_mutexattr_t, is not feasible. 164 | * pthread_mutexattr_t is an opaque type and no "copy" function is 165 | * provided by pthreads. The set of accessor functions may vary by 166 | * platform, so we can't use those to perform a copy either. 167 | * 168 | * So, we're casting away the const here. This should be safe in 169 | * all cases. C/C++ compilers can't use const as an aide to 170 | * optimization (due to the aliasing problem.) 171 | * We also know that pthread_mutexattr_t is not stored in read-only 172 | * memory, because the only way to initialize it is through 173 | * pthread_mutexattr_init, which modifies it. There is no static 174 | * initializer provided for pthread_mutexattr_t. 175 | */ 176 | ret = pthread_mutex_real_init(mutex, (pthread_mutexattr_t*)attr, 177 | &recursive); 178 | if (ret) { 179 | /* It's nice to log a message when mutex initialization fails. 180 | * It's a very rare scenario and something that a program can 181 | * easily fail to check. */ 182 | lksmith_error(ret, "pthread_mutex_init(mutex=%p): " 183 | "failed with error %s (%d)", mutex, terror(ret), ret); 184 | return ret; 185 | } 186 | ret = lksmith_optional_init((const void*)mutex, recursive, 1); 187 | if (ret) { 188 | pthread_mutex_destroy(mutex); 189 | return ret; 190 | } 191 | return 0; 192 | } 193 | 194 | int pthread_mutex_destroy(pthread_mutex_t *mutex) 195 | { 196 | int ret; 197 | 198 | ret = lksmith_destroy(mutex); 199 | if ((ret != 0) && (ret != ENOENT)) { 200 | /* We ignore ENOENT here because the mutex may have been 201 | * initialized with PTHREAD_MUTEX_INITIALIZER and then 202 | * destroyed after no interactions with Locksmith. 203 | * 204 | * In order to catch this case, we'd have to peek inside the 205 | * mutex, which we don't want to do for portability reasons. 206 | */ 207 | return ret; 208 | } 209 | ret = r_pthread_mutex_destroy(mutex); 210 | if (ret) { 211 | lksmith_error(ret, "pthread_mutex_destroy(mutex=%p): " 212 | "failed with error %s (%d)", mutex, terror(ret), ret); 213 | } 214 | return ret; 215 | } 216 | 217 | int pthread_mutex_trylock(pthread_mutex_t *mutex) 218 | { 219 | int ret = lksmith_prelock(mutex, 1); 220 | if (ret) 221 | return ret; 222 | ret = r_pthread_mutex_trylock(mutex); 223 | lksmith_postlock(mutex, ret); 224 | return ret; 225 | } 226 | 227 | int pthread_mutex_lock(pthread_mutex_t *mutex) 228 | { 229 | int ret = lksmith_prelock(mutex, 1); 230 | if (ret) 231 | return ret; 232 | ret = r_pthread_mutex_lock(mutex); 233 | lksmith_postlock(mutex, ret); 234 | return ret; 235 | } 236 | 237 | int pthread_mutex_timedlock(pthread_mutex_t *__restrict mutex, 238 | __const struct timespec *__restrict ts) 239 | { 240 | int ret = lksmith_prelock(mutex, 1); 241 | if (ret) 242 | return ret; 243 | ret = r_pthread_mutex_timedlock(mutex, ts); 244 | lksmith_postlock(mutex, ret); 245 | return ret; 246 | } 247 | 248 | int pthread_mutex_unlock(pthread_mutex_t *__restrict mutex) 249 | { 250 | int ret = lksmith_preunlock(mutex); 251 | if (ret) 252 | return ret; 253 | ret = r_pthread_mutex_unlock(mutex); 254 | if (ret) 255 | return ret; 256 | lksmith_postunlock(mutex); 257 | return 0; 258 | } 259 | 260 | // TODO: pthread_rwlock stuff 261 | int pthread_spin_init(pthread_spinlock_t *lock, int pshared) 262 | { 263 | int ret; 264 | 265 | ret = lksmith_optional_init((const void*)lock, 0, 0); 266 | if (ret) 267 | return ret; 268 | ret = r_pthread_spin_init(lock, pshared); 269 | if (ret) { 270 | lksmith_error(ret, "pthread_spin_init(mutex=%p): " 271 | "failed with error %s (%d)", lock, terror(ret), ret); 272 | lksmith_destroy((const void*)lock); 273 | return ret; 274 | } 275 | return 0; 276 | } 277 | 278 | int pthread_spin_destroy(pthread_spinlock_t *lock) 279 | { 280 | int ret; 281 | 282 | ret = lksmith_destroy((const void*)lock); 283 | if (ret) 284 | return ret; 285 | return r_pthread_spin_destroy(lock); 286 | } 287 | 288 | int pthread_spin_lock(pthread_spinlock_t *lock) 289 | { 290 | int ret = lksmith_prelock((const void*)lock, 0); 291 | if (ret) 292 | return ret; 293 | ret = r_pthread_spin_lock(lock); 294 | lksmith_postlock((const void*)lock, ret); 295 | return ret; 296 | } 297 | 298 | int pthread_spin_trylock(pthread_spinlock_t *lock) 299 | { 300 | int ret = lksmith_prelock((const void*)lock, 0); 301 | if (ret) 302 | return ret; 303 | ret = r_pthread_spin_trylock(lock); 304 | lksmith_postlock((const void*)lock, ret); 305 | return ret; 306 | } 307 | 308 | int pthread_spin_unlock(pthread_spinlock_t *lock) 309 | { 310 | int ret = lksmith_preunlock((const void*)lock); 311 | if (ret) 312 | return ret; 313 | ret = r_pthread_spin_unlock(lock); 314 | if (ret) 315 | return ret; 316 | lksmith_postunlock((const void*)lock); 317 | return 0; 318 | } 319 | 320 | int pthread_cond_init(pthread_cond_t *__restrict cond, 321 | const pthread_condattr_t *__restrict attr) 322 | { 323 | int ret = r_pthread_cond_init(cond, attr); 324 | if (ret) { 325 | lksmith_error(ret, "pthread_cond_init(mutex=%p): " 326 | "failed with error %s (%d)", cond, terror(ret), ret); 327 | } 328 | return ret; 329 | } 330 | 331 | int pthread_cond_timedwait(pthread_cond_t *__restrict cond, 332 | pthread_mutex_t *__restrict mutex, 333 | const struct timespec *__restrict abstime) 334 | { 335 | struct lksmith_cond *cnd = NULL; 336 | int ret = lksmith_check_locked((const void*)mutex); 337 | if (ret > 0) { 338 | return ret; 339 | } else if (ret == -1) { 340 | lksmith_error(EPERM, "pthread_cond_timedwait(cond=%p, " 341 | "mutex=%p): you called pthread_cond_timedwait on " 342 | "a mutex that you do not currently hold. Please " 343 | "fix this serious error in your program.\n", 344 | cond, mutex); 345 | return EPERM; 346 | } 347 | ret = lksmith_cond_prewait(cond, mutex, &cnd); 348 | if (ret) 349 | return ret; 350 | ret = r_pthread_cond_timedwait(cond, mutex, abstime); 351 | lksmith_cond_postwait(cnd); 352 | return ret; 353 | } 354 | 355 | int pthread_cond_wait(pthread_cond_t *__restrict cond, 356 | pthread_mutex_t *__restrict mutex) 357 | { 358 | struct lksmith_cond *cnd = NULL; 359 | int ret = lksmith_check_locked((const void*)mutex); 360 | if (ret > 0) { 361 | return ret; 362 | } else if (ret == -1) { 363 | lksmith_error(EPERM, "pthread_cond_wait(cond=%p, mutex=%p): " 364 | "you called pthread_cond_wait on a mutex that you " 365 | "do not currently hold. Please fix this serious " 366 | "error in your program.\n", cond, mutex); 367 | return EPERM; 368 | } 369 | ret = lksmith_cond_prewait(cond, mutex, &cnd); 370 | if (ret) 371 | return ret; 372 | ret = r_pthread_cond_wait(cond, mutex); 373 | lksmith_cond_postwait(cnd); 374 | return ret; 375 | } 376 | 377 | int pthread_cond_destroy(pthread_cond_t *cond) 378 | { 379 | int ret = lksmith_cond_predestroy(cond); 380 | if (ret) 381 | return ret; 382 | ret = r_pthread_cond_destroy(cond); 383 | if (ret) { 384 | lksmith_error(ret, "pthread_cond_destroy(cond=%p): " 385 | "failed with error %s (%d)", cond, terror(ret), ret); 386 | } 387 | return ret; 388 | } 389 | 390 | // TODO: support barriers 391 | 392 | #define LOAD_FUNC(fn) do { \ 393 | r_##fn = get_dlsym_next(#fn); \ 394 | if (!r_##fn) { \ 395 | return ELIBACC; \ 396 | } \ 397 | } while (0); 398 | 399 | int lksmith_handler_init(void) 400 | { 401 | LOAD_FUNC(pthread_mutex_init); 402 | LOAD_FUNC(pthread_mutex_destroy); 403 | LOAD_FUNC(pthread_mutex_trylock); 404 | LOAD_FUNC(pthread_mutex_lock); 405 | LOAD_FUNC(pthread_mutex_timedlock); 406 | LOAD_FUNC(pthread_mutex_unlock); 407 | LOAD_FUNC(pthread_spin_init); 408 | LOAD_FUNC(pthread_spin_destroy); 409 | LOAD_FUNC(pthread_spin_lock); 410 | LOAD_FUNC(pthread_spin_trylock); 411 | LOAD_FUNC(pthread_spin_unlock); 412 | LOAD_FUNC(pthread_cond_init); 413 | LOAD_FUNC(pthread_cond_wait); 414 | LOAD_FUNC(pthread_cond_timedwait); 415 | LOAD_FUNC(pthread_cond_destroy); 416 | 417 | return 0; 418 | } 419 | 420 | // TODO: support thread cancellation? ugh... 421 | -------------------------------------------------------------------------------- /error_unit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "lksmith.h" 31 | #include "test.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #define THREAD_WRAPPER_VOID(fn) \ 43 | static void *fn##_wrap(void *v __attribute__((unused))) { \ 44 | return (void*)(intptr_t)fn(); \ 45 | } 46 | 47 | #define THREAD_WRAPPER_INT(fn) \ 48 | static void *fn##_wrap(void *v) { \ 49 | return (void*)(intptr_t)fn((int)(intptr_t)v); \ 50 | } 51 | 52 | static pthread_mutex_t g_lock1 = PTHREAD_MUTEX_INITIALIZER; 53 | static pthread_mutex_t g_lock2 = PTHREAD_MUTEX_INITIALIZER; 54 | 55 | static sem_t g_inver_sem1; 56 | static sem_t g_inver_sem2; 57 | 58 | static int inver_thread_a(void) 59 | { 60 | EXPECT_ZERO(pthread_mutex_lock(&g_lock1)); 61 | EXPECT_ZERO(pthread_mutex_lock(&g_lock2)); 62 | EXPECT_ZERO(pthread_mutex_unlock(&g_lock2)); 63 | EXPECT_ZERO(sem_post(&g_inver_sem1)); 64 | EXPECT_ZERO(sem_wait(&g_inver_sem2)); 65 | EXPECT_ZERO(pthread_mutex_unlock(&g_lock1)); 66 | return 0; 67 | } 68 | 69 | THREAD_WRAPPER_VOID(inver_thread_a); 70 | 71 | static int inver_thread_b(void) 72 | { 73 | EXPECT_ZERO(sem_wait(&g_inver_sem1)); 74 | EXPECT_ZERO(pthread_mutex_lock(&g_lock2)); 75 | EXPECT_EQ(pthread_mutex_trylock(&g_lock1), EBUSY); 76 | EXPECT_ZERO(sem_post(&g_inver_sem2)); 77 | EXPECT_ZERO(pthread_mutex_unlock(&g_lock2)); 78 | return 0; 79 | } 80 | 81 | THREAD_WRAPPER_VOID(inver_thread_b); 82 | 83 | static int test_ab_inversion(void) 84 | { 85 | pthread_t thread_a, thread_b; 86 | void *rval; 87 | 88 | EXPECT_ZERO(sem_init(&g_inver_sem1, 0, 0)); 89 | EXPECT_ZERO(sem_init(&g_inver_sem2, 0, 0)); 90 | EXPECT_ZERO(pthread_create(&thread_a, NULL, inver_thread_a_wrap, NULL)); 91 | EXPECT_ZERO(pthread_create(&thread_b, NULL, inver_thread_b_wrap, NULL)); 92 | EXPECT_ZERO(pthread_join(thread_a, &rval)); 93 | EXPECT_EQ(rval, NULL); 94 | EXPECT_ZERO(pthread_join(thread_b, &rval)); 95 | EXPECT_EQ(rval, NULL); 96 | EXPECT_EQ(find_recorded_error(EDEADLK), 1); 97 | EXPECT_ZERO(sem_destroy(&g_inver_sem1)); 98 | EXPECT_ZERO(sem_destroy(&g_inver_sem2)); 99 | clear_recorded_errors(); 100 | 101 | return 0; 102 | } 103 | 104 | static int test_destroy_while_same_thread_has_locked(void) 105 | { 106 | pthread_mutex_t mutex; 107 | EXPECT_ZERO(pthread_mutex_init(&mutex, NULL)); 108 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 109 | EXPECT_EQ(pthread_mutex_destroy(&mutex), EBUSY); 110 | EXPECT_EQ(find_recorded_error(EBUSY), 1); 111 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 112 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 113 | clear_recorded_errors(); 114 | 115 | return 0; 116 | } 117 | 118 | static sem_t g_test_destroy_sem1; 119 | static sem_t g_test_destroy_sem2; 120 | static pthread_mutex_t g_test_destroy_mutex = PTHREAD_MUTEX_INITIALIZER; 121 | 122 | static int test_destroy_helper1(void) 123 | { 124 | EXPECT_ZERO(pthread_mutex_lock(&g_test_destroy_mutex)); 125 | EXPECT_ZERO(sem_post(&g_test_destroy_sem1)); 126 | EXPECT_ZERO(sem_wait(&g_test_destroy_sem2)); 127 | EXPECT_ZERO(pthread_mutex_unlock(&g_test_destroy_mutex)); 128 | return 0; 129 | } 130 | 131 | THREAD_WRAPPER_VOID(test_destroy_helper1); 132 | 133 | static int test_destroy_while_other_thread_has_locked(void) 134 | { 135 | pthread_t thread_c; 136 | void *rval; 137 | 138 | EXPECT_ZERO(sem_init(&g_test_destroy_sem1, 0, 0)); 139 | EXPECT_ZERO(sem_init(&g_test_destroy_sem2, 0, 0)); 140 | EXPECT_ZERO(pthread_create(&thread_c, NULL, 141 | test_destroy_helper1_wrap, NULL)); 142 | sem_wait(&g_test_destroy_sem1); 143 | EXPECT_EQ(pthread_mutex_destroy(&g_test_destroy_mutex), EBUSY); 144 | EXPECT_EQ(find_recorded_error(EBUSY), 1); 145 | sem_post(&g_test_destroy_sem2); 146 | EXPECT_ZERO(pthread_join(thread_c, &rval)); 147 | EXPECT_EQ(rval, NULL); 148 | EXPECT_ZERO(pthread_mutex_destroy(&g_test_destroy_mutex)); 149 | EXPECT_ZERO(sem_destroy(&g_test_destroy_sem1)); 150 | EXPECT_ZERO(sem_destroy(&g_test_destroy_sem2)); 151 | clear_recorded_errors(); 152 | return 0; 153 | } 154 | 155 | static sem_t g_test_bad_unlock_sem1; 156 | static sem_t g_test_bad_unlock_sem2; 157 | static pthread_mutex_t g_test_bad_unlock_mutex; 158 | 159 | static int test_bad_unlock_helper1(void) 160 | { 161 | EXPECT_ZERO(pthread_mutex_lock(&g_test_bad_unlock_mutex)); 162 | EXPECT_ZERO(sem_post(&g_test_bad_unlock_sem1)); 163 | EXPECT_ZERO(sem_wait(&g_test_bad_unlock_sem2)); 164 | EXPECT_ZERO(pthread_mutex_unlock(&g_test_bad_unlock_mutex)); 165 | return 0; 166 | } 167 | 168 | THREAD_WRAPPER_VOID(test_bad_unlock_helper1); 169 | 170 | static int test_bad_unlock(void) 171 | { 172 | pthread_t thread_d; 173 | void *rval; 174 | 175 | EXPECT_ZERO(sem_init(&g_test_bad_unlock_sem1, 0, 0)); 176 | EXPECT_ZERO(sem_init(&g_test_bad_unlock_sem2, 0, 0)); 177 | EXPECT_ZERO(pthread_mutex_init(&g_test_bad_unlock_mutex, NULL)); 178 | EXPECT_ZERO(pthread_create(&thread_d, NULL, 179 | test_bad_unlock_helper1_wrap, NULL)); 180 | sem_wait(&g_test_bad_unlock_sem1); 181 | EXPECT_EQ(pthread_mutex_unlock(&g_test_bad_unlock_mutex), EPERM); 182 | EXPECT_EQ(find_recorded_error(EPERM), 1); 183 | sem_post(&g_test_bad_unlock_sem2); 184 | EXPECT_ZERO(pthread_join(thread_d, &rval)); 185 | EXPECT_EQ(rval, NULL); 186 | EXPECT_ZERO(pthread_mutex_destroy(&g_test_bad_unlock_mutex)); 187 | EXPECT_ZERO(sem_destroy(&g_test_bad_unlock_sem1)); 188 | EXPECT_ZERO(sem_destroy(&g_test_bad_unlock_sem2)); 189 | clear_recorded_errors(); 190 | return 0; 191 | } 192 | 193 | #define MAX_BIG_INVERSION_LOCKS 1024 194 | 195 | static pthread_mutex_t g_locks[MAX_BIG_INVERSION_LOCKS]; 196 | static sem_t g_inver_sem[3]; 197 | static unsigned int g_bigenv_threads; 198 | 199 | static int big_inversion_thread(int idx) 200 | { 201 | unsigned int i; 202 | int next; 203 | 204 | next = (idx + 1) % g_bigenv_threads; 205 | EXPECT_ZERO(pthread_mutex_lock(&g_locks[idx])); 206 | EXPECT_ZERO(sem_post(&g_inver_sem[0])); 207 | if (idx == 0) { 208 | for (i = 0; i < g_bigenv_threads - 1; i++) { 209 | EXPECT_ZERO(sem_wait(&g_inver_sem[2])); 210 | } 211 | EXPECT_EQ(pthread_mutex_trylock(&g_locks[next]), EBUSY); 212 | EXPECT_EQ(find_recorded_error(EDEADLK), 1); 213 | } else { 214 | EXPECT_ZERO(sem_wait(&g_inver_sem[1])); 215 | EXPECT_EQ(pthread_mutex_trylock(&g_locks[next]), EBUSY); 216 | EXPECT_ZERO(sem_post(&g_inver_sem[2])); 217 | EXPECT_ZERO(pthread_mutex_lock(&g_locks[next])); 218 | EXPECT_ZERO(pthread_mutex_unlock(&g_locks[next])); 219 | } 220 | EXPECT_ZERO(pthread_mutex_unlock(&g_locks[idx])); 221 | return 0; 222 | } 223 | 224 | THREAD_WRAPPER_INT(big_inversion_thread); 225 | 226 | static int test_big_inversion(unsigned int num_threads) 227 | { 228 | unsigned int i; 229 | pthread_t thread[MAX_BIG_INVERSION_LOCKS]; 230 | void *rval; 231 | 232 | g_bigenv_threads = num_threads; 233 | for (i = 0; i < sizeof(g_inver_sem)/sizeof(g_inver_sem[0]); i++) { 234 | EXPECT_ZERO(sem_init(&g_inver_sem[i], 0, 0)); 235 | } 236 | for (i = 0; i < num_threads; i++) { 237 | EXPECT_ZERO(pthread_mutex_init(&g_locks[i], NULL)); 238 | } 239 | for (i = 0; i < num_threads; i++) { 240 | EXPECT_ZERO(pthread_create(&thread[i], NULL, 241 | big_inversion_thread_wrap, (void*)(intptr_t)i)); 242 | } 243 | for (i = 0; i < num_threads; i++) { 244 | EXPECT_ZERO(sem_wait(&g_inver_sem[0])); 245 | } 246 | for (i = 0; i < num_threads - 1; i++) { 247 | EXPECT_ZERO(sem_post(&g_inver_sem[1])); 248 | } 249 | for (i = 0; i < num_threads; i++) { 250 | EXPECT_ZERO(pthread_join(thread[i], &rval)); 251 | EXPECT_EQ(rval, NULL); 252 | } 253 | for (i = 0; i < num_threads; i++) { 254 | EXPECT_ZERO(pthread_mutex_destroy(&g_locks[i])); 255 | } 256 | for (i = 0; i < sizeof(g_inver_sem)/sizeof(g_inver_sem[0]); i++) { 257 | EXPECT_ZERO(sem_destroy(&g_inver_sem[i])); 258 | } 259 | clear_recorded_errors(); 260 | 261 | return 0; 262 | } 263 | 264 | static int test_take_sleeping_lock_while_holding_spin(void) 265 | { 266 | pthread_mutex_t mutex; 267 | pthread_spinlock_t spin; 268 | EXPECT_ZERO(pthread_mutex_init(&mutex, NULL)); 269 | EXPECT_ZERO(pthread_spin_init(&spin, 0)); 270 | 271 | /* taking spin lock while holding mutex-- this is ok */ 272 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 273 | EXPECT_ZERO(pthread_spin_lock(&spin)); 274 | EXPECT_ZERO(pthread_spin_unlock(&spin)); 275 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 276 | EXPECT_EQ(find_recorded_error(EWOULDBLOCK), 0); 277 | 278 | /* taking mutex while holding spin lock-- this is not ok */ 279 | EXPECT_ZERO(pthread_spin_lock(&spin)); 280 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 281 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 282 | EXPECT_ZERO(pthread_spin_unlock(&spin)); 283 | EXPECT_EQ(find_recorded_error(EWOULDBLOCK), 1); 284 | 285 | /* we don't whine about the same problem more than once-- it would 286 | * flood the logs */ 287 | EXPECT_ZERO(pthread_spin_lock(&spin)); 288 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 289 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 290 | EXPECT_ZERO(pthread_spin_unlock(&spin)); 291 | EXPECT_EQ(find_recorded_error(EWOULDBLOCK), 0); 292 | 293 | EXPECT_ZERO(pthread_spin_destroy(&spin)); 294 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 295 | clear_recorded_errors(); 296 | 297 | return 0; 298 | } 299 | 300 | static pthread_cond_t g_cond1; 301 | static pthread_mutex_t g_cslock1; 302 | 303 | static int cond_signaller1(void) 304 | { 305 | EXPECT_ZERO(pthread_mutex_lock(&g_cslock1)); 306 | EXPECT_ZERO(pthread_cond_signal(&g_cond1)); 307 | EXPECT_ZERO(pthread_mutex_unlock(&g_cslock1)); 308 | return 0; 309 | } 310 | 311 | THREAD_WRAPPER_VOID(cond_signaller1); 312 | 313 | static int do_pthread_cond_wait(pthread_cond_t *__restrict cond, 314 | pthread_mutex_t *__restrict mutex, 315 | const struct timespec *__restrict ts) 316 | { 317 | // The main reason for testing both pthread_cond_wait and 318 | // pthread_cond_timedwait here is to shake out bugs in our usage of 319 | // dlsym and friends. 320 | if (!ts) { 321 | return pthread_cond_wait(cond, mutex); 322 | } 323 | return pthread_cond_timedwait(cond, mutex, ts); 324 | } 325 | 326 | static int test_invalid_cond_wait(const struct timespec *ts) 327 | { 328 | pthread_t thread; 329 | 330 | EXPECT_ZERO(pthread_mutex_init(&g_cslock1, NULL)); 331 | EXPECT_ZERO(pthread_cond_init(&g_cond1, NULL)); 332 | 333 | /* We must not call pthread_cond_wait on a mutex we don't actually 334 | * hold. */ 335 | EXPECT_EQ(do_pthread_cond_wait(&g_cond1, &g_cslock1, ts), EPERM); 336 | EXPECT_EQ(find_recorded_error(EPERM), 1); 337 | 338 | /* Here is an example of using the API correctly. */ 339 | EXPECT_ZERO(pthread_mutex_lock(&g_cslock1)); 340 | EXPECT_ZERO(pthread_create(&thread, NULL, 341 | cond_signaller1_wrap, NULL)); 342 | EXPECT_EQ(do_pthread_cond_wait(&g_cond1, &g_cslock1, ts), 0); 343 | EXPECT_ZERO(pthread_mutex_unlock(&g_cslock1)); 344 | EXPECT_ZERO(pthread_join(thread, NULL)); 345 | 346 | /* test that we can destroy the pthread_cond and re-create it. */ 347 | EXPECT_ZERO(pthread_cond_destroy(&g_cond1)); 348 | EXPECT_ZERO(pthread_cond_init(&g_cond1, NULL)); 349 | EXPECT_ZERO(pthread_cond_destroy(&g_cond1)); 350 | EXPECT_ZERO(pthread_mutex_destroy(&g_cslock1)); 351 | return 0; 352 | } 353 | 354 | static int test_recursion_on_nonrecursive(void) 355 | { 356 | pthread_mutexattr_t attr; 357 | pthread_mutex_t mutex; 358 | int ty = PTHREAD_MUTEX_NORMAL; 359 | 360 | EXPECT_ZERO(pthread_mutexattr_init(&attr)); 361 | EXPECT_ZERO(pthread_mutexattr_settype(&attr, ty)); 362 | EXPECT_ZERO(pthread_mutex_init(&mutex, &attr)); 363 | EXPECT_ZERO(pthread_mutex_lock(&mutex)); 364 | EXPECT_EQ(pthread_mutex_trylock(&mutex), EBUSY); 365 | EXPECT_EQ(find_recorded_error(EDEADLK), 1); 366 | EXPECT_ZERO(pthread_mutex_unlock(&mutex)); 367 | EXPECT_ZERO(pthread_mutexattr_destroy(&attr)); 368 | EXPECT_ZERO(pthread_mutex_destroy(&mutex)); 369 | return 0; 370 | } 371 | 372 | static pthread_cond_t g_tbcw_cond = PTHREAD_COND_INITIALIZER; 373 | static pthread_mutex_t g_tbcw_lock1 = PTHREAD_MUTEX_INITIALIZER; 374 | static pthread_mutex_t g_tbcw_lock2 = PTHREAD_MUTEX_INITIALIZER; 375 | 376 | static int tbcw_thread1(void) 377 | { 378 | int ret; 379 | 380 | pthread_mutex_lock(&g_tbcw_lock1); 381 | ret = pthread_cond_wait(&g_tbcw_cond, &g_tbcw_lock1); 382 | pthread_mutex_unlock(&g_tbcw_lock1); 383 | if (ret) { 384 | pthread_mutex_lock(&g_tbcw_lock2); 385 | pthread_cond_signal(&g_tbcw_cond); 386 | pthread_mutex_unlock(&g_tbcw_lock2); 387 | } 388 | return ret; 389 | } 390 | 391 | THREAD_WRAPPER_VOID(tbcw_thread1); 392 | 393 | static int tbcw_thread2(void) 394 | { 395 | int ret; 396 | 397 | pthread_mutex_lock(&g_tbcw_lock2); 398 | ret = pthread_cond_wait(&g_tbcw_cond, &g_tbcw_lock2); 399 | pthread_mutex_unlock(&g_tbcw_lock2); 400 | if (ret) { 401 | pthread_mutex_lock(&g_tbcw_lock1); 402 | pthread_cond_signal(&g_tbcw_cond); 403 | pthread_mutex_unlock(&g_tbcw_lock1); 404 | } 405 | return ret; 406 | } 407 | 408 | THREAD_WRAPPER_VOID(tbcw_thread2); 409 | 410 | static int test_bad_cond_wait(void) 411 | { 412 | pthread_t thread_1, thread_2; 413 | void *rval1 = NULL, *rval2 = NULL; 414 | 415 | EXPECT_ZERO(pthread_create(&thread_1, NULL, tbcw_thread1_wrap, NULL)); 416 | EXPECT_ZERO(pthread_create(&thread_2, NULL, tbcw_thread2_wrap, NULL)); 417 | EXPECT_ZERO(pthread_join(thread_1, &rval1)); 418 | EXPECT_ZERO(pthread_join(thread_2, &rval2)); 419 | if (rval1 == NULL) { 420 | EXPECT_NOT_EQ(rval2, NULL); 421 | } else if (rval2 == NULL) { 422 | EXPECT_NOT_EQ(rval1, NULL); 423 | } else { 424 | fprintf(stderr, "expected one of the threads to fail."); 425 | return EINVAL; 426 | } 427 | EXPECT_EQ(find_recorded_error(EINVAL), 1); 428 | clear_recorded_errors(); 429 | return 0; 430 | } 431 | 432 | int main(void) 433 | { 434 | struct timespec ts; 435 | 436 | set_error_cb(record_error); 437 | EXPECT_ZERO(test_ab_inversion()); 438 | EXPECT_ZERO(test_destroy_while_same_thread_has_locked()); 439 | EXPECT_ZERO(test_destroy_while_other_thread_has_locked()); 440 | EXPECT_ZERO(test_bad_unlock()); 441 | EXPECT_ZERO(test_big_inversion(3)); 442 | EXPECT_ZERO(test_big_inversion(100)); 443 | EXPECT_ZERO(test_take_sleeping_lock_while_holding_spin()); 444 | EXPECT_ZERO(test_invalid_cond_wait(NULL)); 445 | EXPECT_ZERO(test_invalid_cond_wait(NULL)); 446 | 447 | get_current_timespec(&ts); 448 | ts.tv_sec += 600; 449 | EXPECT_ZERO(test_invalid_cond_wait(&ts)); 450 | 451 | EXPECT_ZERO(test_recursion_on_nonrecursive()); 452 | 453 | EXPECT_ZERO(test_bad_cond_wait()); 454 | 455 | return EXIT_SUCCESS; 456 | } 457 | -------------------------------------------------------------------------------- /tree.h: -------------------------------------------------------------------------------- 1 | /* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ 2 | /* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ 3 | /* $FreeBSD: src/sys/sys/tree.h,v 1.9.4.1 2011/09/23 00:51:37 kensmith Exp $ */ 4 | 5 | /*- 6 | * Copyright 2002 Niels Provos 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _SYS_TREE_H_ 31 | #define _SYS_TREE_H_ 32 | 33 | #include 34 | 35 | /* 36 | * This file defines data structures for different types of trees: 37 | * splay trees and red-black trees. 38 | * 39 | * A splay tree is a self-organizing data structure. Every operation 40 | * on the tree causes a splay to happen. The splay moves the requested 41 | * node to the root of the tree and partly rebalances it. 42 | * 43 | * This has the benefit that request locality causes faster lookups as 44 | * the requested nodes move to the top of the tree. On the other hand, 45 | * every lookup causes memory writes. 46 | * 47 | * The Balance Theorem bounds the total access time for m operations 48 | * and n inserts on an initially empty tree as O((m + n)lg n). The 49 | * amortized cost for a sequence of m accesses to a splay tree is O(lg n); 50 | * 51 | * A red-black tree is a binary search tree with the node color as an 52 | * extra attribute. It fulfills a set of conditions: 53 | * - every search path from the root to a leaf consists of the 54 | * same number of black nodes, 55 | * - each red node (except for the root) has a black parent, 56 | * - each leaf node is black. 57 | * 58 | * Every operation on a red-black tree is bounded as O(lg n). 59 | * The maximum height of a red-black tree is 2lg (n+1). 60 | */ 61 | 62 | #define SPLAY_HEAD(name, type) \ 63 | struct name { \ 64 | struct type *sph_root; /* root of the tree */ \ 65 | } 66 | 67 | #define SPLAY_INITIALIZER(root) \ 68 | { NULL } 69 | 70 | #define SPLAY_INIT(root) do { \ 71 | (root)->sph_root = NULL; \ 72 | } while (/*CONSTCOND*/ 0) 73 | 74 | #define SPLAY_ENTRY(type) \ 75 | struct { \ 76 | struct type *spe_left; /* left element */ \ 77 | struct type *spe_right; /* right element */ \ 78 | } 79 | 80 | #define SPLAY_LEFT(elm, field) (elm)->field.spe_left 81 | #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right 82 | #define SPLAY_ROOT(head) (head)->sph_root 83 | #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) 84 | 85 | /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ 86 | #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ 87 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ 88 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 89 | (head)->sph_root = tmp; \ 90 | } while (/*CONSTCOND*/ 0) 91 | 92 | #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ 93 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ 94 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 95 | (head)->sph_root = tmp; \ 96 | } while (/*CONSTCOND*/ 0) 97 | 98 | #define SPLAY_LINKLEFT(head, tmp, field) do { \ 99 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 100 | tmp = (head)->sph_root; \ 101 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 102 | } while (/*CONSTCOND*/ 0) 103 | 104 | #define SPLAY_LINKRIGHT(head, tmp, field) do { \ 105 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 106 | tmp = (head)->sph_root; \ 107 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 108 | } while (/*CONSTCOND*/ 0) 109 | 110 | #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ 111 | SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ 112 | SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ 113 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ 114 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ 115 | } while (/*CONSTCOND*/ 0) 116 | 117 | /* Generates prototypes and inline functions */ 118 | 119 | #define SPLAY_PROTOTYPE(name, type, field, cmp) \ 120 | void name##_SPLAY(struct name *, struct type *); \ 121 | void name##_SPLAY_MINMAX(struct name *, int); \ 122 | struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ 123 | struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ 124 | \ 125 | /* Finds the node with the same key as elm */ \ 126 | static __inline struct type * \ 127 | name##_SPLAY_FIND(struct name *head, struct type *elm) \ 128 | { \ 129 | if (SPLAY_EMPTY(head)) \ 130 | return(NULL); \ 131 | name##_SPLAY(head, elm); \ 132 | if ((cmp)(elm, (head)->sph_root) == 0) \ 133 | return (head->sph_root); \ 134 | return (NULL); \ 135 | } \ 136 | \ 137 | static __inline struct type * \ 138 | name##_SPLAY_NEXT(struct name *head, struct type *elm) \ 139 | { \ 140 | name##_SPLAY(head, elm); \ 141 | if (SPLAY_RIGHT(elm, field) != NULL) { \ 142 | elm = SPLAY_RIGHT(elm, field); \ 143 | while (SPLAY_LEFT(elm, field) != NULL) { \ 144 | elm = SPLAY_LEFT(elm, field); \ 145 | } \ 146 | } else \ 147 | elm = NULL; \ 148 | return (elm); \ 149 | } \ 150 | \ 151 | static __inline struct type * \ 152 | name##_SPLAY_MIN_MAX(struct name *head, int val) \ 153 | { \ 154 | name##_SPLAY_MINMAX(head, val); \ 155 | return (SPLAY_ROOT(head)); \ 156 | } 157 | 158 | /* Main splay operation. 159 | * Moves node close to the key of elm to top 160 | */ 161 | #define SPLAY_GENERATE(name, type, field, cmp) \ 162 | struct type * \ 163 | name##_SPLAY_INSERT(struct name *head, struct type *elm) \ 164 | { \ 165 | if (SPLAY_EMPTY(head)) { \ 166 | SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ 167 | } else { \ 168 | int __comp; \ 169 | name##_SPLAY(head, elm); \ 170 | __comp = (cmp)(elm, (head)->sph_root); \ 171 | if(__comp < 0) { \ 172 | SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ 173 | SPLAY_RIGHT(elm, field) = (head)->sph_root; \ 174 | SPLAY_LEFT((head)->sph_root, field) = NULL; \ 175 | } else if (__comp > 0) { \ 176 | SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ 177 | SPLAY_LEFT(elm, field) = (head)->sph_root; \ 178 | SPLAY_RIGHT((head)->sph_root, field) = NULL; \ 179 | } else \ 180 | return ((head)->sph_root); \ 181 | } \ 182 | (head)->sph_root = (elm); \ 183 | return (NULL); \ 184 | } \ 185 | \ 186 | struct type * \ 187 | name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ 188 | { \ 189 | struct type *__tmp; \ 190 | if (SPLAY_EMPTY(head)) \ 191 | return (NULL); \ 192 | name##_SPLAY(head, elm); \ 193 | if ((cmp)(elm, (head)->sph_root) == 0) { \ 194 | if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ 195 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ 196 | } else { \ 197 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 198 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ 199 | name##_SPLAY(head, elm); \ 200 | SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ 201 | } \ 202 | return (elm); \ 203 | } \ 204 | return (NULL); \ 205 | } \ 206 | \ 207 | void \ 208 | name##_SPLAY(struct name *head, struct type *elm) \ 209 | { \ 210 | struct type __node, *__left, *__right, *__tmp; \ 211 | int __comp; \ 212 | \ 213 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 214 | __left = __right = &__node; \ 215 | \ 216 | while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ 217 | if (__comp < 0) { \ 218 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 219 | if (__tmp == NULL) \ 220 | break; \ 221 | if ((cmp)(elm, __tmp) < 0){ \ 222 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 223 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 224 | break; \ 225 | } \ 226 | SPLAY_LINKLEFT(head, __right, field); \ 227 | } else if (__comp > 0) { \ 228 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 229 | if (__tmp == NULL) \ 230 | break; \ 231 | if ((cmp)(elm, __tmp) > 0){ \ 232 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 233 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 234 | break; \ 235 | } \ 236 | SPLAY_LINKRIGHT(head, __left, field); \ 237 | } \ 238 | } \ 239 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 240 | } \ 241 | \ 242 | /* Splay with either the minimum or the maximum element \ 243 | * Used to find minimum or maximum element in tree. \ 244 | */ \ 245 | void name##_SPLAY_MINMAX(struct name *head, int __comp) \ 246 | { \ 247 | struct type __node, *__left, *__right, *__tmp; \ 248 | \ 249 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 250 | __left = __right = &__node; \ 251 | \ 252 | while (1) { \ 253 | if (__comp < 0) { \ 254 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 255 | if (__tmp == NULL) \ 256 | break; \ 257 | if (__comp < 0){ \ 258 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 259 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 260 | break; \ 261 | } \ 262 | SPLAY_LINKLEFT(head, __right, field); \ 263 | } else if (__comp > 0) { \ 264 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 265 | if (__tmp == NULL) \ 266 | break; \ 267 | if (__comp > 0) { \ 268 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 269 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 270 | break; \ 271 | } \ 272 | SPLAY_LINKRIGHT(head, __left, field); \ 273 | } \ 274 | } \ 275 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 276 | } 277 | 278 | #define SPLAY_NEGINF -1 279 | #define SPLAY_INF 1 280 | 281 | #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) 282 | #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) 283 | #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) 284 | #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) 285 | #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ 286 | : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) 287 | #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ 288 | : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) 289 | 290 | #define SPLAY_FOREACH(x, name, head) \ 291 | for ((x) = SPLAY_MIN(name, head); \ 292 | (x) != NULL; \ 293 | (x) = SPLAY_NEXT(name, head, x)) 294 | 295 | /* Macros that define a red-black tree */ 296 | #define RB_HEAD(name, type) \ 297 | struct name { \ 298 | struct type *rbh_root; /* root of the tree */ \ 299 | } 300 | 301 | #define RB_INITIALIZER(root) \ 302 | { NULL } 303 | 304 | #define RB_INIT(root) do { \ 305 | (root)->rbh_root = NULL; \ 306 | } while (/*CONSTCOND*/ 0) 307 | 308 | #define RB_BLACK 0 309 | #define RB_RED 1 310 | #define RB_ENTRY(type) \ 311 | struct { \ 312 | struct type *rbe_left; /* left element */ \ 313 | struct type *rbe_right; /* right element */ \ 314 | struct type *rbe_parent; /* parent element */ \ 315 | int rbe_color; /* node color */ \ 316 | } 317 | 318 | #define RB_LEFT(elm, field) (elm)->field.rbe_left 319 | #define RB_RIGHT(elm, field) (elm)->field.rbe_right 320 | #define RB_PARENT(elm, field) (elm)->field.rbe_parent 321 | #define RB_COLOR(elm, field) (elm)->field.rbe_color 322 | #define RB_ROOT(head) (head)->rbh_root 323 | #define RB_EMPTY(head) (RB_ROOT(head) == NULL) 324 | 325 | #define RB_SET(elm, parent, field) do { \ 326 | RB_PARENT(elm, field) = parent; \ 327 | RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ 328 | RB_COLOR(elm, field) = RB_RED; \ 329 | } while (/*CONSTCOND*/ 0) 330 | 331 | #define RB_SET_BLACKRED(black, red, field) do { \ 332 | RB_COLOR(black, field) = RB_BLACK; \ 333 | RB_COLOR(red, field) = RB_RED; \ 334 | } while (/*CONSTCOND*/ 0) 335 | 336 | #ifndef RB_AUGMENT 337 | #define RB_AUGMENT(x) do {} while (0) 338 | #endif 339 | 340 | #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ 341 | (tmp) = RB_RIGHT(elm, field); \ 342 | if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ 343 | RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ 344 | } \ 345 | RB_AUGMENT(elm); \ 346 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ 347 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 348 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 349 | else \ 350 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 351 | } else \ 352 | (head)->rbh_root = (tmp); \ 353 | RB_LEFT(tmp, field) = (elm); \ 354 | RB_PARENT(elm, field) = (tmp); \ 355 | RB_AUGMENT(tmp); \ 356 | if ((RB_PARENT(tmp, field))) \ 357 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 358 | } while (/*CONSTCOND*/ 0) 359 | 360 | #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ 361 | (tmp) = RB_LEFT(elm, field); \ 362 | if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ 363 | RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ 364 | } \ 365 | RB_AUGMENT(elm); \ 366 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ 367 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 368 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 369 | else \ 370 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 371 | } else \ 372 | (head)->rbh_root = (tmp); \ 373 | RB_RIGHT(tmp, field) = (elm); \ 374 | RB_PARENT(elm, field) = (tmp); \ 375 | RB_AUGMENT(tmp); \ 376 | if ((RB_PARENT(tmp, field))) \ 377 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 378 | } while (/*CONSTCOND*/ 0) 379 | 380 | /* Generates prototypes and inline functions */ 381 | #define RB_PROTOTYPE(name, type, field, cmp) \ 382 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) 383 | #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ 384 | RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) 385 | #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ 386 | attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ 387 | attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ 388 | attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ 389 | attr struct type *name##_RB_INSERT(struct name *, struct type *); \ 390 | attr struct type *name##_RB_FIND(struct name *, struct type *); \ 391 | attr struct type *name##_RB_NFIND(struct name *, struct type *); \ 392 | attr struct type *name##_RB_NEXT(struct type *); \ 393 | attr struct type *name##_RB_PREV(struct type *); \ 394 | attr struct type *name##_RB_MINMAX(struct name *, int); \ 395 | \ 396 | 397 | /* Main rb operation. 398 | * Moves node close to the key of elm to top 399 | */ 400 | #define RB_GENERATE(name, type, field, cmp) \ 401 | RB_GENERATE_INTERNAL(name, type, field, cmp,) 402 | #define RB_GENERATE_STATIC(name, type, field, cmp) \ 403 | RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) 404 | #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ 405 | attr void \ 406 | name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ 407 | { \ 408 | struct type *parent, *gparent, *tmp; \ 409 | while ((parent = RB_PARENT(elm, field)) != NULL && \ 410 | RB_COLOR(parent, field) == RB_RED) { \ 411 | gparent = RB_PARENT(parent, field); \ 412 | if (parent == RB_LEFT(gparent, field)) { \ 413 | tmp = RB_RIGHT(gparent, field); \ 414 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 415 | RB_COLOR(tmp, field) = RB_BLACK; \ 416 | RB_SET_BLACKRED(parent, gparent, field);\ 417 | elm = gparent; \ 418 | continue; \ 419 | } \ 420 | if (RB_RIGHT(parent, field) == elm) { \ 421 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 422 | tmp = parent; \ 423 | parent = elm; \ 424 | elm = tmp; \ 425 | } \ 426 | RB_SET_BLACKRED(parent, gparent, field); \ 427 | RB_ROTATE_RIGHT(head, gparent, tmp, field); \ 428 | } else { \ 429 | tmp = RB_LEFT(gparent, field); \ 430 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 431 | RB_COLOR(tmp, field) = RB_BLACK; \ 432 | RB_SET_BLACKRED(parent, gparent, field);\ 433 | elm = gparent; \ 434 | continue; \ 435 | } \ 436 | if (RB_LEFT(parent, field) == elm) { \ 437 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 438 | tmp = parent; \ 439 | parent = elm; \ 440 | elm = tmp; \ 441 | } \ 442 | RB_SET_BLACKRED(parent, gparent, field); \ 443 | RB_ROTATE_LEFT(head, gparent, tmp, field); \ 444 | } \ 445 | } \ 446 | RB_COLOR(head->rbh_root, field) = RB_BLACK; \ 447 | } \ 448 | \ 449 | attr void \ 450 | name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ 451 | { \ 452 | struct type *tmp; \ 453 | while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ 454 | elm != RB_ROOT(head)) { \ 455 | if (RB_LEFT(parent, field) == elm) { \ 456 | tmp = RB_RIGHT(parent, field); \ 457 | if (RB_COLOR(tmp, field) == RB_RED) { \ 458 | RB_SET_BLACKRED(tmp, parent, field); \ 459 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 460 | tmp = RB_RIGHT(parent, field); \ 461 | } \ 462 | if ((RB_LEFT(tmp, field) == NULL || \ 463 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 464 | (RB_RIGHT(tmp, field) == NULL || \ 465 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 466 | RB_COLOR(tmp, field) = RB_RED; \ 467 | elm = parent; \ 468 | parent = RB_PARENT(elm, field); \ 469 | } else { \ 470 | if (RB_RIGHT(tmp, field) == NULL || \ 471 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ 472 | struct type *oleft; \ 473 | if ((oleft = RB_LEFT(tmp, field)) \ 474 | != NULL) \ 475 | RB_COLOR(oleft, field) = RB_BLACK;\ 476 | RB_COLOR(tmp, field) = RB_RED; \ 477 | RB_ROTATE_RIGHT(head, tmp, oleft, field);\ 478 | tmp = RB_RIGHT(parent, field); \ 479 | } \ 480 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 481 | RB_COLOR(parent, field) = RB_BLACK; \ 482 | if (RB_RIGHT(tmp, field)) \ 483 | RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ 484 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 485 | elm = RB_ROOT(head); \ 486 | break; \ 487 | } \ 488 | } else { \ 489 | tmp = RB_LEFT(parent, field); \ 490 | if (RB_COLOR(tmp, field) == RB_RED) { \ 491 | RB_SET_BLACKRED(tmp, parent, field); \ 492 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 493 | tmp = RB_LEFT(parent, field); \ 494 | } \ 495 | if ((RB_LEFT(tmp, field) == NULL || \ 496 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 497 | (RB_RIGHT(tmp, field) == NULL || \ 498 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 499 | RB_COLOR(tmp, field) = RB_RED; \ 500 | elm = parent; \ 501 | parent = RB_PARENT(elm, field); \ 502 | } else { \ 503 | if (RB_LEFT(tmp, field) == NULL || \ 504 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ 505 | struct type *oright; \ 506 | if ((oright = RB_RIGHT(tmp, field)) \ 507 | != NULL) \ 508 | RB_COLOR(oright, field) = RB_BLACK;\ 509 | RB_COLOR(tmp, field) = RB_RED; \ 510 | RB_ROTATE_LEFT(head, tmp, oright, field);\ 511 | tmp = RB_LEFT(parent, field); \ 512 | } \ 513 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 514 | RB_COLOR(parent, field) = RB_BLACK; \ 515 | if (RB_LEFT(tmp, field)) \ 516 | RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ 517 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 518 | elm = RB_ROOT(head); \ 519 | break; \ 520 | } \ 521 | } \ 522 | } \ 523 | if (elm) \ 524 | RB_COLOR(elm, field) = RB_BLACK; \ 525 | } \ 526 | \ 527 | attr struct type * \ 528 | name##_RB_REMOVE(struct name *head, struct type *elm) \ 529 | { \ 530 | struct type *child, *parent, *old = elm; \ 531 | int color; \ 532 | if (RB_LEFT(elm, field) == NULL) \ 533 | child = RB_RIGHT(elm, field); \ 534 | else if (RB_RIGHT(elm, field) == NULL) \ 535 | child = RB_LEFT(elm, field); \ 536 | else { \ 537 | struct type *left; \ 538 | elm = RB_RIGHT(elm, field); \ 539 | while ((left = RB_LEFT(elm, field)) != NULL) \ 540 | elm = left; \ 541 | child = RB_RIGHT(elm, field); \ 542 | parent = RB_PARENT(elm, field); \ 543 | color = RB_COLOR(elm, field); \ 544 | if (child) \ 545 | RB_PARENT(child, field) = parent; \ 546 | if (parent) { \ 547 | if (RB_LEFT(parent, field) == elm) \ 548 | RB_LEFT(parent, field) = child; \ 549 | else \ 550 | RB_RIGHT(parent, field) = child; \ 551 | RB_AUGMENT(parent); \ 552 | } else \ 553 | RB_ROOT(head) = child; \ 554 | if (RB_PARENT(elm, field) == old) \ 555 | parent = elm; \ 556 | (elm)->field = (old)->field; \ 557 | if (RB_PARENT(old, field)) { \ 558 | if (RB_LEFT(RB_PARENT(old, field), field) == old)\ 559 | RB_LEFT(RB_PARENT(old, field), field) = elm;\ 560 | else \ 561 | RB_RIGHT(RB_PARENT(old, field), field) = elm;\ 562 | RB_AUGMENT(RB_PARENT(old, field)); \ 563 | } else \ 564 | RB_ROOT(head) = elm; \ 565 | RB_PARENT(RB_LEFT(old, field), field) = elm; \ 566 | if (RB_RIGHT(old, field)) \ 567 | RB_PARENT(RB_RIGHT(old, field), field) = elm; \ 568 | if (parent) { \ 569 | left = parent; \ 570 | do { \ 571 | RB_AUGMENT(left); \ 572 | } while ((left = RB_PARENT(left, field)) != NULL); \ 573 | } \ 574 | goto color; \ 575 | } \ 576 | parent = RB_PARENT(elm, field); \ 577 | color = RB_COLOR(elm, field); \ 578 | if (child) \ 579 | RB_PARENT(child, field) = parent; \ 580 | if (parent) { \ 581 | if (RB_LEFT(parent, field) == elm) \ 582 | RB_LEFT(parent, field) = child; \ 583 | else \ 584 | RB_RIGHT(parent, field) = child; \ 585 | RB_AUGMENT(parent); \ 586 | } else \ 587 | RB_ROOT(head) = child; \ 588 | color: \ 589 | if (color == RB_BLACK) \ 590 | name##_RB_REMOVE_COLOR(head, parent, child); \ 591 | return (old); \ 592 | } \ 593 | \ 594 | /* Inserts a node into the RB tree */ \ 595 | attr struct type * \ 596 | name##_RB_INSERT(struct name *head, struct type *elm) \ 597 | { \ 598 | struct type *tmp; \ 599 | struct type *parent = NULL; \ 600 | int comp = 0; \ 601 | tmp = RB_ROOT(head); \ 602 | while (tmp) { \ 603 | parent = tmp; \ 604 | comp = (cmp)(elm, parent); \ 605 | if (comp < 0) \ 606 | tmp = RB_LEFT(tmp, field); \ 607 | else if (comp > 0) \ 608 | tmp = RB_RIGHT(tmp, field); \ 609 | else \ 610 | return (tmp); \ 611 | } \ 612 | RB_SET(elm, parent, field); \ 613 | if (parent != NULL) { \ 614 | if (comp < 0) \ 615 | RB_LEFT(parent, field) = elm; \ 616 | else \ 617 | RB_RIGHT(parent, field) = elm; \ 618 | RB_AUGMENT(parent); \ 619 | } else \ 620 | RB_ROOT(head) = elm; \ 621 | name##_RB_INSERT_COLOR(head, elm); \ 622 | return (NULL); \ 623 | } \ 624 | \ 625 | /* Finds the node with the same key as elm */ \ 626 | attr struct type * \ 627 | name##_RB_FIND(struct name *head, struct type *elm) \ 628 | { \ 629 | struct type *tmp = RB_ROOT(head); \ 630 | int comp; \ 631 | while (tmp) { \ 632 | comp = cmp(elm, tmp); \ 633 | if (comp < 0) \ 634 | tmp = RB_LEFT(tmp, field); \ 635 | else if (comp > 0) \ 636 | tmp = RB_RIGHT(tmp, field); \ 637 | else \ 638 | return (tmp); \ 639 | } \ 640 | return (NULL); \ 641 | } \ 642 | \ 643 | /* Finds the first node greater than or equal to the search key */ \ 644 | attr struct type * \ 645 | name##_RB_NFIND(struct name *head, struct type *elm) \ 646 | { \ 647 | struct type *tmp = RB_ROOT(head); \ 648 | struct type *res = NULL; \ 649 | int comp; \ 650 | while (tmp) { \ 651 | comp = cmp(elm, tmp); \ 652 | if (comp < 0) { \ 653 | res = tmp; \ 654 | tmp = RB_LEFT(tmp, field); \ 655 | } \ 656 | else if (comp > 0) \ 657 | tmp = RB_RIGHT(tmp, field); \ 658 | else \ 659 | return (tmp); \ 660 | } \ 661 | return (res); \ 662 | } \ 663 | \ 664 | /* ARGSUSED */ \ 665 | attr struct type * \ 666 | name##_RB_NEXT(struct type *elm) \ 667 | { \ 668 | if (RB_RIGHT(elm, field)) { \ 669 | elm = RB_RIGHT(elm, field); \ 670 | while (RB_LEFT(elm, field)) \ 671 | elm = RB_LEFT(elm, field); \ 672 | } else { \ 673 | if (RB_PARENT(elm, field) && \ 674 | (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ 675 | elm = RB_PARENT(elm, field); \ 676 | else { \ 677 | while (RB_PARENT(elm, field) && \ 678 | (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ 679 | elm = RB_PARENT(elm, field); \ 680 | elm = RB_PARENT(elm, field); \ 681 | } \ 682 | } \ 683 | return (elm); \ 684 | } \ 685 | \ 686 | /* ARGSUSED */ \ 687 | attr struct type * \ 688 | name##_RB_PREV(struct type *elm) \ 689 | { \ 690 | if (RB_LEFT(elm, field)) { \ 691 | elm = RB_LEFT(elm, field); \ 692 | while (RB_RIGHT(elm, field)) \ 693 | elm = RB_RIGHT(elm, field); \ 694 | } else { \ 695 | if (RB_PARENT(elm, field) && \ 696 | (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ 697 | elm = RB_PARENT(elm, field); \ 698 | else { \ 699 | while (RB_PARENT(elm, field) && \ 700 | (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ 701 | elm = RB_PARENT(elm, field); \ 702 | elm = RB_PARENT(elm, field); \ 703 | } \ 704 | } \ 705 | return (elm); \ 706 | } \ 707 | \ 708 | attr struct type * \ 709 | name##_RB_MINMAX(struct name *head, int val) \ 710 | { \ 711 | struct type *tmp = RB_ROOT(head); \ 712 | struct type *parent = NULL; \ 713 | while (tmp) { \ 714 | parent = tmp; \ 715 | if (val < 0) \ 716 | tmp = RB_LEFT(tmp, field); \ 717 | else \ 718 | tmp = RB_RIGHT(tmp, field); \ 719 | } \ 720 | return (parent); \ 721 | } 722 | 723 | #define RB_NEGINF -1 724 | #define RB_INF 1 725 | 726 | #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) 727 | #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) 728 | #define RB_FIND(name, x, y) name##_RB_FIND(x, y) 729 | #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) 730 | #define RB_NEXT(name, x, y) name##_RB_NEXT(y) 731 | #define RB_PREV(name, x, y) name##_RB_PREV(y) 732 | #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) 733 | #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) 734 | 735 | #define RB_FOREACH(x, name, head) \ 736 | for ((x) = RB_MIN(name, head); \ 737 | (x) != NULL; \ 738 | (x) = name##_RB_NEXT(x)) 739 | 740 | #define RB_FOREACH_FROM(x, name, y) \ 741 | for ((x) = (y); \ 742 | ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ 743 | (x) = (y)) 744 | 745 | #define RB_FOREACH_SAFE(x, name, head, y) \ 746 | for ((x) = RB_MIN(name, head); \ 747 | ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ 748 | (x) = (y)) 749 | 750 | #define RB_FOREACH_REVERSE(x, name, head) \ 751 | for ((x) = RB_MAX(name, head); \ 752 | (x) != NULL; \ 753 | (x) = name##_RB_PREV(x)) 754 | 755 | #define RB_FOREACH_REVERSE_FROM(x, name, y) \ 756 | for ((x) = (y); \ 757 | ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ 758 | (x) = (y)) 759 | 760 | #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ 761 | for ((x) = RB_MAX(name, head); \ 762 | ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ 763 | (x) = (y)) 764 | 765 | #endif /* _SYS_TREE_H_ */ 766 | -------------------------------------------------------------------------------- /lksmith.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vim: ts=8:sw=8:tw=79:noet 3 | * 4 | * Copyright (c) 2011-2012, the Locksmith authors. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "backtrace.h" 31 | #include "config.h" 32 | #include "error.h" 33 | #include "handler.h" 34 | #include "lksmith.h" 35 | #include "platform.h" 36 | #include "tree.h" 37 | #include "util.h" 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | /****************************************************************** 51 | * Locksmith private data structures 52 | *****************************************************************/ 53 | #define MAX_NLOCK 0x1fffffffffffffffULL 54 | 55 | struct lksmith_lock_props { 56 | /** The number of times this mutex has been locked. */ 57 | uint64_t nlock : 61; 58 | /** 1 if we should allow recursive locks. */ 59 | uint64_t recursive : 1; 60 | /** 1 if this mutex is a sleeping lock */ 61 | uint64_t sleeper : 1; 62 | /** 1 if we have already warned about taking this lock while 63 | * a spin lock is held. */ 64 | uint64_t spin_warn : 1; 65 | }; 66 | 67 | struct lksmith_holder { 68 | /** Name of the thread holding the lock */ 69 | char name[LKSMITH_THREAD_NAME_MAX]; 70 | /** Stack frames */ 71 | char** bt_frames; 72 | /** Number of stack frames */ 73 | int bt_len; 74 | /** Next in singly-linked list */ 75 | struct lksmith_holder *next; 76 | }; 77 | 78 | struct lksmith_lock { 79 | RB_ENTRY(lksmith_lock) entry; 80 | /** The lock pointer */ 81 | const void *ptr; 82 | struct lksmith_lock_props props; 83 | /** The color that this node has been painted (used in traversal) */ 84 | uint64_t color; 85 | /** Lock holders */ 86 | struct lksmith_holder *holders; 87 | /** Size of the before list. */ 88 | int before_size; 89 | /** list of locks that have been taken before this lock */ 90 | struct lksmith_lock **before; 91 | }; 92 | 93 | struct lksmith_cond { 94 | RB_ENTRY(lksmith_cond) entry; 95 | /** The condition variable pointer */ 96 | const void *ptr; 97 | /** When a pthread_cond_wait is in progress on this condition variable, 98 | * this is the lock that is being used. */ 99 | const void *lock; 100 | /** Number of waiters */ 101 | uint64_t refcnt; 102 | }; 103 | 104 | struct lksmith_tls { 105 | /** The name of this thread. */ 106 | char name[LKSMITH_THREAD_NAME_MAX]; 107 | /** Size of the held list. */ 108 | unsigned int num_held; 109 | /** Unsorted list of locks held */ 110 | const void **held; 111 | /** Number of spin locks currently held. */ 112 | uint64_t num_spins : 63; 113 | /** 1 if we should intercept pthreads calls; 0 otherwise */ 114 | uint64_t intercept : 1; 115 | /** scratch area for backtraces */ 116 | void **backtrace_scratch; 117 | /** length of scratch area for backtraces */ 118 | int backtrace_scratch_len; 119 | }; 120 | 121 | /****************************************************************** 122 | * Locksmith prototypes 123 | *****************************************************************/ 124 | static int lksmith_lock_compare(const struct lksmith_lock *a, 125 | const struct lksmith_lock *b) __attribute__((const)); 126 | RB_HEAD(lock_tree, lksmith_lock); 127 | RB_GENERATE(lock_tree, lksmith_lock, entry, lksmith_lock_compare); 128 | static int lksmith_cond_compare(const struct lksmith_cond *a, 129 | const struct lksmith_cond *b) __attribute__((const)); 130 | RB_HEAD(cond_tree, lksmith_cond); 131 | RB_GENERATE(cond_tree, lksmith_cond, entry, lksmith_cond_compare); 132 | static void lksmith_tls_destroy(void *v); 133 | static void lk_dump_to_stderr(struct lksmith_lock *lk) __attribute__((unused)); 134 | static void tree_print(void) __attribute__((unused)); 135 | static int compare_strings(const void *a, const void *b) 136 | __attribute__((const)); 137 | 138 | /****************************************************************** 139 | * Locksmith globals 140 | *****************************************************************/ 141 | /** 142 | * 1 if the library has been initialized. 143 | */ 144 | static int g_initialized; 145 | 146 | /** 147 | * Protects the initialization state. 148 | */ 149 | static int g_init_state_lock; 150 | 151 | /** 152 | * The key that allows us to retrieve thread-local data. 153 | * Protected by g_init_state_lock. 154 | */ 155 | static pthread_key_t g_tls_key; 156 | 157 | /** 158 | * Mutex which protects g_tree 159 | */ 160 | static pthread_mutex_t g_tree_lock; 161 | 162 | /** 163 | * Tree of mutexes sorted by pointer 164 | */ 165 | struct lock_tree g_tree; 166 | 167 | /** 168 | * Mutex which protects g_cond_tree 169 | */ 170 | static pthread_mutex_t g_cond_tree_lock; 171 | 172 | /** 173 | * Tree of condition variables sorted by pointer 174 | */ 175 | struct cond_tree g_cond_tree; 176 | 177 | /** 178 | * The latest color that has been used in graph traversal 179 | */ 180 | static uint64_t g_color; 181 | 182 | /** 183 | * A sorted list of frames to ignore. 184 | */ 185 | static char **g_ignored_frames; 186 | 187 | /** 188 | * The number of ignored frames. 189 | */ 190 | static int g_num_ignored_frames; 191 | 192 | /** 193 | * A list of frame patterns to ignore. 194 | */ 195 | static char **g_ignored_frame_patterns; 196 | 197 | /** 198 | * The number of ignored frame patterns. 199 | */ 200 | static int g_num_ignored_frame_patterns; 201 | 202 | /****************************************************************** 203 | * Initialization 204 | *****************************************************************/ 205 | static int compare_strings(const void *a, const void *b) 206 | { 207 | const char *sa = *(const char **)a; 208 | const char *sb = *(const char **)b; 209 | return strcmp(sa, sb); 210 | } 211 | 212 | static int lksmith_init_ignored(const char *env, char ***out, int *out_len) 213 | { 214 | int ret, num_ignored = 0; 215 | const char *ignored_env; 216 | char *ignored = NULL, **ignored_arr = 0, *saveptr = NULL; 217 | const char *str; 218 | 219 | ignored_env = getenv(env); 220 | if (!ignored_env) { 221 | ret = 0; 222 | goto done; 223 | } 224 | ignored = strdup(ignored_env); 225 | if (!ignored) { 226 | ret = ENOMEM; 227 | goto done; 228 | } 229 | for (str = strtok_r(ignored, ":", &saveptr); 230 | str; str = strtok_r(NULL, ":", &saveptr)) { 231 | num_ignored++; 232 | } 233 | strcpy(ignored, ignored_env); 234 | ignored_arr = calloc(num_ignored, sizeof(char*)); 235 | if (!ignored_arr) { 236 | ret = ENOMEM; 237 | goto done; 238 | } 239 | num_ignored = 0; 240 | for (str = strtok_r(ignored, ":", &saveptr); 241 | str; str = strtok_r(NULL, ":", &saveptr)) { 242 | ignored_arr[num_ignored] = strdup(str); 243 | if (!ignored_arr[num_ignored]) { 244 | ret = ENOMEM; 245 | goto done; 246 | } 247 | num_ignored++; 248 | } 249 | qsort(ignored_arr, num_ignored, sizeof(char*), compare_strings); 250 | ret = 0; 251 | 252 | done: 253 | free(ignored); 254 | if (ret) { 255 | if (ignored_arr) { 256 | char **i; 257 | for (i = ignored_arr; *i; i++) { 258 | free(*i); 259 | } 260 | free(ignored_arr); 261 | } 262 | return ret; 263 | } 264 | *out = ignored_arr; 265 | *out_len = num_ignored; 266 | return 0; 267 | } 268 | 269 | /** 270 | * Initialize the locksmith library. 271 | */ 272 | static void lksmith_init(void) 273 | { 274 | int ret; 275 | 276 | ret = lksmith_handler_init(); 277 | if (ret) { 278 | /* can't use lksmith_error before handler */ 279 | fprintf(stderr, "lksmith_init: lksmith_handler_init failed. " 280 | "Can't find the real pthreads functions.\n"); 281 | abort(); 282 | } 283 | ret = lksmith_init_ignored("LKSMITH_IGNORED_FRAMES", 284 | &g_ignored_frames, &g_num_ignored_frames); 285 | if (ret) { 286 | lksmith_error(ret, "lksmith_init: lksmith_init_ignored_frames(" 287 | "frames) failed: error %d: %s\n", ret, terror(ret)); 288 | abort(); 289 | } 290 | ret = lksmith_init_ignored("LKSMITH_IGNORED_FRAME_PATTERNS", 291 | &g_ignored_frame_patterns, 292 | &g_num_ignored_frame_patterns); 293 | if (ret) { 294 | lksmith_error(ret, "lksmith_init: lksmith_init_ignored_frames(" 295 | "patterns) failed: error %d: %s\n", ret, terror(ret)); 296 | abort(); 297 | } 298 | ret = pthread_key_create(&g_tls_key, lksmith_tls_destroy); 299 | if (ret) { 300 | lksmith_error(ret, "lksmith_init: pthread_key_create(" 301 | "g_tls_key) failed: error %d: %s\n", ret, terror(ret)); 302 | abort(); 303 | } 304 | ret = r_pthread_mutex_init(&g_tree_lock, NULL); 305 | if (ret) { 306 | lksmith_error(ret, "lksmith_init: pthread_mutex_init " 307 | "g_tree_lock) failed: error %d: %s\n", ret, terror(ret)); 308 | abort(); 309 | } 310 | ret = r_pthread_mutex_init(&g_cond_tree_lock, NULL); 311 | if (ret) { 312 | lksmith_error(ret, "lksmith_init: pthread_mutex_init " 313 | "g_cond_tree_lock) failed: error %d: %s\n", 314 | ret, terror(ret)); 315 | abort(); 316 | } 317 | lksmith_error(0, "Locksmith has been initialized for process %lld\n", 318 | (long long)getpid()); 319 | g_initialized = 1; 320 | } 321 | 322 | /****************************************************************** 323 | * Thread-local storage 324 | *****************************************************************/ 325 | /** 326 | * Callback which destroys thread-local storage. 327 | * 328 | * This callback will be invoked whenever a thread exits. 329 | * 330 | * @param v The TLS object. 331 | */ 332 | static void lksmith_tls_destroy(void *v) 333 | { 334 | struct lksmith_tls *tls = v; 335 | free(tls->held); 336 | free(tls); 337 | } 338 | 339 | /** 340 | * Get or create the thread-local storage associated with this thread. 341 | * 342 | * The problem with POSIX thread-local storage is that in order to use it, 343 | * you must first initialize a 'key'. But how do you initialize this key 344 | * prior to use? There's no good way to do it. 345 | * 346 | * gcc provides the non-portable __attribute__((constructor)), which seems like 347 | * it could be useful. Unfortunately, the order in which these constructor 348 | * functions are called between shared libraries is not defined. 349 | * If another shared library defines some constructor functions which invoke 350 | * pthreads functions, and it gets run before we initialize, it's game over. 351 | * C++ global constructors have the same issues. 352 | * 353 | * We could force Locksmith users to call an init function before calling any 354 | * other Locksmith functions. Then, this init function could initialize the 355 | * key. But that would be painful for many users. This is especially true 356 | * in C++, where global constructors often make use of mutexes long before 357 | * main() runs. 358 | * 359 | * The other approach, which we have taken here, is to protect the key with a 360 | * mutex. This is somewhat slow, since it means that we have to take this 361 | * mutex before every access to thread-local data. Luckily, on platforms 362 | * that support the __thread keyword, we can bypass this slowness. 363 | * Thread-local variables declared using __thread don't need to be manually 364 | * intialized before use. They're ready to go before any code has been run. 365 | * 366 | * One advantage that POSIX thread-local variables have over __thread 367 | * variables is that the former can declare "destructors" which are run when 368 | * the thread is destroyed. (These have no relation to the C++ concept of 369 | * the same name.) We make use of that ability here, to clean up our 370 | * malloc()ed thread-local data when the thread in question exits. 371 | * By combining the __thread keyword and POSIX thread-local variables, we can 372 | * get the best of each. 373 | * 374 | * @return NULL on OOM, the TLS otherwise. 375 | */ 376 | static struct lksmith_tls *get_or_create_tls(void) 377 | { 378 | int ret = 0; 379 | struct lksmith_tls *tls; 380 | 381 | #ifdef HAVE_IMPROVED_TLS 382 | static __thread struct lksmith_tls *t_improved_tls = NULL; 383 | if (t_improved_tls) { 384 | return t_improved_tls; 385 | } 386 | #endif 387 | simple_spin_lock(&g_init_state_lock); 388 | if (!g_initialized) { 389 | lksmith_init(); 390 | } 391 | simple_spin_unlock(&g_init_state_lock); 392 | #ifndef HAVE_IMPROVED_TLS 393 | tls = pthread_getspecific(g_tls_key); 394 | if (tls) { 395 | return tls; 396 | } 397 | #endif 398 | tls = calloc(1, sizeof(*tls)); 399 | if (!tls) { 400 | lksmith_error(ENOMEM, 401 | "get_or_create_tls(): failed to allocate " 402 | "memory for thread-local storage.\n"); 403 | return NULL; 404 | } 405 | tls->intercept = 1; 406 | platform_create_thread_name(tls->name, LKSMITH_THREAD_NAME_MAX); 407 | ret = pthread_setspecific(g_tls_key, tls); 408 | if (ret) { 409 | free(tls->held); 410 | free(tls); 411 | lksmith_error(ENOMEM, 412 | "get_or_create_tls(): pthread_setspecific " 413 | "failed with error %d: %s\n", ret, terror(ret)); 414 | return NULL; 415 | } 416 | #ifdef HAVE_IMPROVED_TLS 417 | t_improved_tls = tls; 418 | #endif 419 | return tls; 420 | } 421 | 422 | int init_tls(void) 423 | { 424 | struct lksmith_tls *tls; 425 | 426 | tls = get_or_create_tls(); 427 | if (!tls) 428 | return -ENOMEM; 429 | return 0; 430 | } 431 | 432 | /** 433 | * Add a lock ID to the end of the list of lock IDs we hold. 434 | * 435 | * NOTE: lock IDs can be added more than once to this list! 436 | * This is so that we can support recursive mutexes. 437 | * 438 | * @param tls The thread-local data. 439 | * @param ptr the lock to add to the list. 440 | * 441 | * @return 0 on success; ENOMEM if we ran out of memory. 442 | */ 443 | static int tls_append_held(struct lksmith_tls *tls, const void *ptr) 444 | { 445 | const void **held; 446 | 447 | held = realloc(tls->held, sizeof(uintptr_t) * (tls->num_held + 1)); 448 | if (!held) 449 | return ENOMEM; 450 | tls->held = held; 451 | held[tls->num_held++] = ptr; 452 | return 0; 453 | } 454 | 455 | /** 456 | * Remove a lock ID from the list of lock IDs we hold. 457 | * 458 | * @param tls The thread-local data. 459 | * @param ptr the lock ID to add to the list. 460 | * 461 | * @return 0 on success; ENOENT if we are not holding the 462 | * lock ID. 463 | */ 464 | static int tls_remove_held(struct lksmith_tls *tls, const void *ptr) 465 | { 466 | signed int i; 467 | const void **held; 468 | 469 | for (i = tls->num_held - 1; i >= 0; i--) { 470 | if (tls->held[i] == ptr) 471 | break; 472 | } 473 | if (i < 0) 474 | return ENOENT; 475 | memmove(&tls->held[i], &tls->held[i + 1], 476 | sizeof(struct lksmith_held*) * (tls->num_held - i - 1)); 477 | held = realloc(tls->held, sizeof(uintptr_t) * (--tls->num_held)); 478 | if (held || (tls->num_held == 0)) { 479 | tls->held = held; 480 | } 481 | return 0; 482 | } 483 | 484 | /** 485 | * Determine if we are holding a lock. 486 | * 487 | * @param tls The thread-local data. 488 | * @param ptr The lock ID to find. 489 | * 490 | * @return 1 if we hold the lock; 0 otherwise. 491 | */ 492 | static int tls_contains_lid(struct lksmith_tls *tls, const void *ptr) 493 | { 494 | unsigned int i; 495 | 496 | for (i = 0; i < tls->num_held; i++) { 497 | if (tls->held[i] == ptr) 498 | return 1; 499 | } 500 | return 0; 501 | } 502 | 503 | static void lksmith_error_with_ti(struct lksmith_tls *tls, int err, 504 | const char *fmt, ...) 505 | __attribute__((format(printf, 3, 4))); 506 | 507 | /** 508 | * Locksmith error with thread information (including a backtrace) 509 | * 510 | * @param tls Our thread-local storage object 511 | * @param err the locksmith error code 512 | * @param fmt printf-style format string 513 | */ 514 | static void lksmith_error_with_ti(struct lksmith_tls *tls, int err, 515 | const char *fmt, ...) 516 | { 517 | va_list ap; 518 | int nframes, prev_intercept; 519 | char **frames = NULL; 520 | 521 | if (!tls) { 522 | tls = get_or_create_tls(); 523 | if (!tls) { 524 | va_start(ap, fmt); 525 | lksmith_errora(err, fmt, ap); 526 | va_end(ap); 527 | return; 528 | } 529 | } 530 | prev_intercept = tls->intercept; 531 | tls->intercept = 0; 532 | nframes = bt_frames_create(&tls->backtrace_scratch, 533 | &tls->backtrace_scratch_len, &frames); 534 | tls->intercept = prev_intercept; 535 | va_start(ap, fmt); 536 | // lksmith_errora_with_bt handles nframes < 0 (the error case) 537 | lksmith_errora_with_bt(err, frames, nframes, fmt, ap); 538 | va_end(ap); 539 | bt_frames_free(frames); 540 | } 541 | 542 | /****************************************************************** 543 | * Lock holder functions 544 | *****************************************************************/ 545 | /** 546 | * Dump out the contents of a lock holder structure 547 | * 548 | * @param holder The lock holder 549 | * @param buf (out param) the buffer to write to 550 | * @param off (inout param) current position in the buffer 551 | * @param buf_len length of buf 552 | */ 553 | static void holder_dump(const struct lksmith_holder *holder, 554 | char *buf, size_t *off, size_t buf_len) 555 | { 556 | const char *prefix = ""; 557 | int i; 558 | 559 | fwdprintf(buf, off, buf_len, "{name=%s, " 560 | "bt_frames=[", holder->name); 561 | for (i = 0; i < holder->bt_len; i++) { 562 | fwdprintf(buf, off, buf_len, "%s%s", prefix, 563 | holder->bt_frames[i]); 564 | prefix = ", "; 565 | } 566 | fwdprintf(buf, off, buf_len, "]}"); 567 | } 568 | 569 | /** 570 | * Create a lock holder. 571 | * 572 | * @param tls The thread-local storage for the current thread. 573 | * 574 | * @return The lock holder on success; NULL otherwise. 575 | */ 576 | static struct lksmith_holder* holder_create(struct lksmith_tls *tls) 577 | { 578 | struct lksmith_holder *holder; 579 | int intercept, ret; 580 | 581 | holder = calloc(1, sizeof(*holder)); 582 | if (!holder) 583 | return NULL; 584 | snprintf(holder->name, sizeof(holder->name), "%s", tls->name); 585 | intercept = tls->intercept; 586 | tls->intercept = 0; 587 | ret = bt_frames_create(&tls->backtrace_scratch, 588 | &tls->backtrace_scratch_len, &holder->bt_frames); 589 | tls->intercept = intercept; 590 | if (ret < 0) { 591 | free(holder); 592 | return NULL; 593 | } 594 | holder->bt_len = ret; 595 | return holder; 596 | } 597 | 598 | /** 599 | * Free a lock holder structure 600 | * 601 | * @param holder The lock holder 602 | */ 603 | static void holder_free(struct lksmith_holder *holder) 604 | { 605 | bt_frames_free(holder->bt_frames); 606 | free(holder); 607 | } 608 | 609 | /****************************************************************** 610 | * Lock functions 611 | *****************************************************************/ 612 | static int lksmith_lock_compare(const struct lksmith_lock *a, 613 | const struct lksmith_lock *b) 614 | { 615 | const void *pa = a->ptr; 616 | const void *pb = b->ptr; 617 | if (pa < pb) 618 | return -1; 619 | else if (pa > pb) 620 | return 1; 621 | else 622 | return 0; 623 | } 624 | 625 | /** 626 | * Add an element to a sorted array, if it's not already there. 627 | * 628 | * @param arr (inout) the array 629 | * @param num (inout) the array length 630 | * @param lid The lock ID to add. 631 | * 632 | * @return 0 on success; ENOMEM if we ran out of memory. 633 | */ 634 | static int lk_add_sorted(struct lksmith_lock ** __restrict * __restrict arr, 635 | int * __restrict num, struct lksmith_lock *lk) 636 | { 637 | int i; 638 | struct lksmith_lock **narr; 639 | 640 | for (i = 0; i < *num; i++) { 641 | if ((*arr)[i] == lk) 642 | return 0; 643 | else if ((*arr)[i] > lk) 644 | break; 645 | } 646 | narr = realloc(*arr, sizeof(struct lksmith_lock*) * (*num + 1)); 647 | if (!narr) 648 | return ENOMEM; 649 | *arr = narr; 650 | memmove(&narr[i + 1], &narr[i], sizeof(uintptr_t) * (*num - i)); 651 | narr[i] = lk; 652 | *num = *num + 1; 653 | return 0; 654 | } 655 | 656 | /** 657 | * Remove an element from a sorted array. 658 | * We assume it appears only once in that array. 659 | * TODO: be smarter here-- use bsearch 660 | * 661 | * @param arr (inout) the array 662 | * @param num (inout) the array length 663 | * @param lid The lock ID to remove. 664 | */ 665 | static void lk_remove_sorted(struct lksmith_lock ** __restrict * __restrict arr, 666 | int * __restrict num, struct lksmith_lock *ak) 667 | { 668 | int i; 669 | struct lksmith_lock **narr; 670 | 671 | if (*arr == NULL) 672 | return; 673 | for (i = 0; i < *num; i++) { 674 | if ((*arr)[i] == ak) 675 | break; 676 | else if ((*arr)[i] > ak) 677 | return; 678 | } 679 | if (i == *num) 680 | return; 681 | if (i < (*num -1)) 682 | memmove(&(*arr)[i], &(*arr)[i + 1], 683 | sizeof(struct lksmith_lock*) * (*num - i - 1)); 684 | narr = realloc(*arr, sizeof(struct lksmith_lock*) * (--*num)); 685 | if (narr || (*num == 0)) 686 | *arr = narr; 687 | } 688 | 689 | /** 690 | * Add a lock to the 'before' set of this lock data. 691 | * Note: you must call this function with the info->lock held. 692 | * 693 | * @param lk The lock data. 694 | * @param lid The lock ID to add. 695 | * 696 | * @return 0 on success; ENOMEM if we ran out of memory. 697 | */ 698 | static int lk_add_before(struct lksmith_lock *lk, struct lksmith_lock *ak) 699 | { 700 | return lk_add_sorted(&lk->before, &lk->before_size, ak); 701 | } 702 | 703 | /** 704 | * Remove a lock from the 'after' set of this lock data. 705 | * Note: you must call this function with the info->lock held. 706 | * 707 | * @param lk The lock data. 708 | * @param lid The lock ID to remove. 709 | */ 710 | static void lk_remove_before(struct lksmith_lock *lk, struct lksmith_lock *ak) 711 | { 712 | lk_remove_sorted(&lk->before, &lk->before_size, ak); 713 | } 714 | 715 | /** 716 | * Add a lock holder to the lock. 717 | * Note: you must call this function with the info->lock held. 718 | * 719 | * @param lk The lock data. 720 | * @param holder The lock holder to add. 721 | */ 722 | static void lk_holder_add(struct lksmith_lock *lk, 723 | struct lksmith_holder *holder) 724 | { 725 | holder->next = lk->holders; 726 | lk->holders = holder; 727 | } 728 | 729 | /** 730 | * Remove a lock holder from the lock. 731 | * Note: you must call this function with the info->lock held. 732 | * 733 | * @param lk The lock data. 734 | * @param tls The thread-local storage for the current thread. 735 | * 736 | * @return 0 on success; -ENOENT if the lock holder wasn't found. 737 | */ 738 | static int lk_holder_remove(struct lksmith_lock *lk, 739 | struct lksmith_tls *tls) 740 | { 741 | struct lksmith_holder **holder, *next; 742 | 743 | /* By iterating forward through the list, we ensure that holders are 744 | * taken out in the reverse order that they were put in (since we also 745 | * insert to the head of the list.) This is important when dealing 746 | * with recursive locks, where one thread can take the same lock over 747 | * and over. */ 748 | holder = &lk->holders; 749 | while (*holder) { 750 | if (!strcmp(tls->name, (*holder)->name)) 751 | break; 752 | holder = &(*holder)->next; 753 | } 754 | if (!holder) 755 | return -ENOENT; 756 | next = (*holder)->next; 757 | holder_free(*holder); 758 | *holder = next; 759 | return 0; 760 | } 761 | 762 | /** 763 | * Dump out the contents of a lock data structure. 764 | * 765 | * @param lk The lock data 766 | * @param buf (out param) the buffer to write to 767 | * @param off (inout param) current position in the buffer 768 | * @param buf_len length of buf 769 | */ 770 | static void lk_dump(const struct lksmith_lock *lk, 771 | char *buf, size_t *off, size_t buf_len) 772 | { 773 | int i; 774 | const char *prefix = ""; 775 | struct lksmith_holder *holder; 776 | 777 | fwdprintf(buf, off, buf_len, "lk{ptr=%p, " 778 | "nlock=%"PRId64", recursive=%d, sleeper=%d," 779 | "color=%"PRId64", before={", 780 | (void*)lk->ptr, (uint64_t)lk->props.nlock, 781 | lk->props.recursive, lk->props.sleeper, 782 | lk->color); 783 | for (i = 0; i < lk->before_size; i++) { 784 | fwdprintf(buf, off, buf_len, "%s%p", 785 | prefix, lk->before[i]); 786 | prefix = " "; 787 | } 788 | fwdprintf(buf, off, buf_len, "}, holders=["); 789 | prefix = ""; 790 | holder = lk->holders; 791 | while (holder) { 792 | fwdprintf(buf, off, buf_len, "%s", prefix); 793 | holder_dump(holder, buf, off, buf_len); 794 | prefix = ", "; 795 | holder = holder->next; 796 | } 797 | fwdprintf(buf, off, buf_len, "]}"); 798 | } 799 | 800 | static void lk_dump_to_stderr(struct lksmith_lock *lk) 801 | { 802 | char buf[16384]; 803 | size_t off = 0; 804 | 805 | lk_dump(lk, buf, &off, sizeof(buf)); 806 | fputs(buf, stderr); 807 | fputs("\n", stderr); 808 | } 809 | 810 | static void tree_print(void) 811 | { 812 | char buf[8196]; 813 | struct lksmith_lock *lk; 814 | size_t off; 815 | const char *prefix = ""; 816 | 817 | fprintf(stderr, "g_lock_tree: {"); 818 | RB_FOREACH(lk, lock_tree, &g_tree) { 819 | off = 0; 820 | lk_dump(lk, buf, &off, sizeof(buf)); 821 | fprintf(stderr, "%s%s", prefix, buf); 822 | prefix = ",\n"; 823 | } 824 | fprintf(stderr, "\n}\n"); 825 | } 826 | 827 | static int lksmith_insert(const void *ptr, int recursive, 828 | int sleeper, struct lksmith_lock **lk) 829 | { 830 | struct lksmith_lock *ak, *bk; 831 | ak = calloc(1, sizeof(*ak)); 832 | if (!ak) { 833 | return ENOMEM; 834 | } 835 | ak->ptr = ptr; 836 | ak->props.recursive = !!recursive; 837 | ak->props.sleeper = !!sleeper; 838 | ak->holders = NULL; 839 | bk = RB_INSERT(lock_tree, &g_tree, ak); 840 | if (bk) { 841 | free(ak); 842 | return EEXIST; 843 | } 844 | *lk = ak; 845 | return 0; 846 | } 847 | 848 | static struct lksmith_lock *lksmith_find(const void *ptr) 849 | { 850 | struct lksmith_lock exemplar; 851 | memset(&exemplar, 0, sizeof(exemplar)); 852 | exemplar.ptr = ptr; 853 | return RB_FIND(lock_tree, &g_tree, &exemplar); 854 | } 855 | 856 | /****************************************************************** 857 | * Cond functions 858 | *****************************************************************/ 859 | static int lksmith_cond_compare(const struct lksmith_cond *a, 860 | const struct lksmith_cond *b) 861 | { 862 | const void *pa = a->ptr; 863 | const void *pb = b->ptr; 864 | if (pa < pb) 865 | return -1; 866 | else if (pa > pb) 867 | return 1; 868 | else 869 | return 0; 870 | } 871 | 872 | static struct lksmith_cond *lksmith_cond_find(const void *ptr) 873 | { 874 | struct lksmith_cond exemplar; 875 | memset(&exemplar, 0, sizeof(exemplar)); 876 | exemplar.ptr = ptr; 877 | return RB_FIND(cond_tree, &g_cond_tree, &exemplar); 878 | } 879 | 880 | static int lksmith_cond_insert(const void *ptr, struct lksmith_cond **cond) 881 | { 882 | struct lksmith_cond *cnd, *and; 883 | cnd = calloc(1, sizeof(*cnd)); 884 | if (!cnd) { 885 | return ENOMEM; 886 | } 887 | cnd->ptr = ptr; 888 | and = RB_INSERT(cond_tree, &g_cond_tree, cnd); 889 | if (and) { 890 | free(cnd); 891 | return EEXIST; 892 | } 893 | *cond = cnd; 894 | return 0; 895 | } 896 | 897 | /****************************************************************** 898 | * API functions 899 | *****************************************************************/ 900 | int lksmith_optional_init(const void *ptr, int recursive, int sleeper) 901 | { 902 | struct lksmith_tls *tls; 903 | struct lksmith_lock *lk; 904 | int ret; 905 | 906 | tls = get_or_create_tls(); 907 | if (!tls) { 908 | lksmith_error(ENOMEM, "lksmith_optional_init(lock=%p): " 909 | "failed to allocate thread-local storage.\n", ptr); 910 | return ENOMEM; 911 | } 912 | if (!tls->intercept) 913 | return 0; 914 | r_pthread_mutex_lock(&g_tree_lock); 915 | ret = lksmith_insert(ptr, recursive, sleeper, &lk); 916 | r_pthread_mutex_unlock(&g_tree_lock); 917 | if (ret) { 918 | lksmith_error(ret, "lksmith_optional_init(lock=%p, " 919 | "thread=%s): failed to allocate lock data: " 920 | "error %d: %s\n", ptr, tls->name, ret, terror(ret)); 921 | return ret; 922 | } 923 | return 0; 924 | } 925 | 926 | int lksmith_destroy(const void *ptr) 927 | { 928 | int ret; 929 | struct lksmith_lock *lk, *ak; 930 | struct lksmith_tls *tls; 931 | 932 | tls = get_or_create_tls(); 933 | if (!tls) { 934 | lksmith_error(ENOMEM, "lksmith_destroy(lock=%p): failed to " 935 | "allocate thread-local storage.\n", ptr); 936 | ret = ENOMEM; 937 | goto done; 938 | } 939 | if (!tls->intercept) 940 | return 0; 941 | r_pthread_mutex_lock(&g_tree_lock); 942 | lk = lksmith_find(ptr); 943 | if (!lk) { 944 | /* This might not be an error, if we used 945 | * PTHREAD_MUTEX_INITIALIZER and then never did anything else 946 | * with the lock prior to destroying it. */ 947 | ret = ENOENT; 948 | goto done_unlock; 949 | } 950 | if (lk->holders != NULL) { 951 | if (tls_contains_lid(tls, ptr) == 1) { 952 | lksmith_error(EBUSY, "lksmith_destroy(lock=%p, " 953 | "thread=%s): you must unlock this mutex " 954 | "before destroying it.", ptr, tls->name); 955 | } else { 956 | lksmith_error(EBUSY, "lksmith_destroy(lock=%p, " 957 | "thread=%s): this mutex is currently in use " 958 | "and so cannot be destroyed.", ptr, tls->name); 959 | } 960 | ret = EBUSY; 961 | goto done_unlock; 962 | } 963 | RB_REMOVE(lock_tree, &g_tree, lk); 964 | /* TODO: could probably avoid traversing the whole tree by using both 965 | * before and after pointers inside locks, or some such? */ 966 | RB_FOREACH(ak, lock_tree, &g_tree) { 967 | lk_remove_before(ak, lk); 968 | } 969 | free(lk->before); 970 | free(lk); 971 | ret = 0; 972 | done_unlock: 973 | r_pthread_mutex_unlock(&g_tree_lock); 974 | done: 975 | return ret; 976 | } 977 | 978 | static int lksmith_search(struct lksmith_lock *lk, const void *start) 979 | { 980 | int ret, i; 981 | 982 | if (lk->ptr == start) 983 | return 1; 984 | if (lk->color == g_color) 985 | return 0; 986 | lk->color = g_color; 987 | for (i = 0; i < lk->before_size; i++) { 988 | ret = lksmith_search(lk->before[i], start); 989 | if (ret) 990 | return ret; 991 | } 992 | return 0; 993 | } 994 | 995 | static void lksmith_prelock_process_depends(struct lksmith_tls *tls, 996 | struct lksmith_lock *lk, const void *ptr) 997 | { 998 | unsigned int i; 999 | const void *held; 1000 | struct lksmith_lock *ak; 1001 | 1002 | g_color++; 1003 | for (i = 0; i < tls->num_held; i++) { 1004 | held = tls->held[i]; 1005 | ak = lksmith_find(held); 1006 | if (!ak) { 1007 | lksmith_error_with_ti(tls, ENOMEM, "lksmith_prelock(" 1008 | "lock=%p, thread=%s): thread holds unknown " 1009 | "lock %p.\n", ptr, tls->name, held); 1010 | continue; 1011 | } 1012 | if (ak == lk) { 1013 | if (ak->props.recursive) 1014 | continue; 1015 | lksmith_error_with_ti(tls, EDEADLK, "lksmith_prelock(" 1016 | "lock=%p, thread=%s): this thread already holds " 1017 | "this lock, and it is not a recursive lock.\n", 1018 | ptr, tls->name); 1019 | continue; 1020 | } 1021 | if (lksmith_search(ak, ptr)) { 1022 | lksmith_error_with_ti(tls, EDEADLK, "lksmith_prelock(" 1023 | "lock=%p, thread=%s): lock inversion! This " 1024 | "lock should have been taken before lock %p, " 1025 | "which this thread already holds.\n", 1026 | ptr, tls->name, held); 1027 | continue; 1028 | } 1029 | lk_add_before(lk, ak); 1030 | } 1031 | } 1032 | 1033 | /** 1034 | * Returns true if lksmith_prelock should skip dependency processing. 1035 | * 1036 | * We search the current backtrace for any element that is in the ignore 1037 | * list. 1038 | */ 1039 | static int should_skip_dependency_processing(struct lksmith_holder *holder) 1040 | { 1041 | int bt_idx, ip_idx; 1042 | char *match; 1043 | 1044 | for (bt_idx = 0; bt_idx < holder->bt_len; bt_idx++) { 1045 | const char *frame = holder->bt_frames[bt_idx]; 1046 | match = bsearch(&frame, g_ignored_frames, g_num_ignored_frames, 1047 | sizeof(char*), compare_strings); 1048 | if (match) { 1049 | return 1; 1050 | } 1051 | for (ip_idx = 0; ip_idx < g_num_ignored_frame_patterns; 1052 | ip_idx++) { 1053 | if (!fnmatch(g_ignored_frame_patterns[ip_idx], 1054 | frame, 0)) { 1055 | return 1; 1056 | } 1057 | } 1058 | } 1059 | return 0; 1060 | } 1061 | 1062 | int lksmith_prelock(const void *ptr, int sleeper) 1063 | { 1064 | struct lksmith_lock *lk; 1065 | struct lksmith_tls *tls; 1066 | int ret; 1067 | struct lksmith_holder *holder = NULL; 1068 | 1069 | tls = get_or_create_tls(); 1070 | if (!tls) { 1071 | lksmith_error(ENOMEM, "lksmith_prelock(lock=%p): failed to " 1072 | "allocate thread-local storage.\n", ptr); 1073 | ret = ENOMEM; 1074 | goto done; 1075 | } 1076 | if (!tls->intercept) 1077 | return 0; 1078 | holder = holder_create(tls); 1079 | if (!holder) { 1080 | lksmith_error(ENOMEM, "lksmith_prelock(lock=%p): failed to " 1081 | "allocate lock holder data.\n", ptr); 1082 | ret = ENOMEM; 1083 | goto done; 1084 | } 1085 | r_pthread_mutex_lock(&g_tree_lock); 1086 | lk = lksmith_find(ptr); 1087 | if (!lk) { 1088 | /* If the lock hasn't been explicitly initialized using 1089 | * lksmith_optional_init, we allow it to be recursive. 1090 | * It might have been statically initialized with 1091 | * PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP. 1092 | */ 1093 | ret = lksmith_insert(ptr, 1, sleeper, &lk); 1094 | if (ret) { 1095 | lksmith_error(ret, "lksmith_prelock(lock=%p, " 1096 | "thread=%s): failed to allocate lock data: " 1097 | "error %d: %s\n", ptr, tls->name, ret, terror(ret)); 1098 | goto done_unlock; 1099 | } 1100 | } 1101 | if (!should_skip_dependency_processing(holder)) { 1102 | lksmith_prelock_process_depends(tls, lk, ptr); 1103 | } 1104 | lk_holder_add(lk, holder); 1105 | 1106 | holder = NULL; 1107 | ret = 0; 1108 | done_unlock: 1109 | r_pthread_mutex_unlock(&g_tree_lock); 1110 | done: 1111 | if (holder) { 1112 | holder_free(holder); 1113 | } 1114 | return ret; 1115 | } 1116 | 1117 | void lksmith_postlock(const void *ptr, int error) 1118 | { 1119 | struct lksmith_tls *tls; 1120 | struct lksmith_lock *lk; 1121 | int ret; 1122 | 1123 | tls = get_or_create_tls(); 1124 | if (!tls) { 1125 | lksmith_error(ENOMEM, "lksmith_postlock(lock=%p): failed " 1126 | "to allocate thread-local storage.\n", ptr); 1127 | goto done; 1128 | } 1129 | if (!tls->intercept) 1130 | return; 1131 | r_pthread_mutex_lock(&g_tree_lock); 1132 | lk = lksmith_find(ptr); 1133 | if (!lk) { 1134 | lksmith_error(EIO, "lksmith_postlock(lock=%p, thread=%s): " 1135 | "logic error: prelock didn't create the lock data?\n", 1136 | ptr, tls->name); 1137 | goto done_unlock; 1138 | } 1139 | if (error) { 1140 | lk_holder_remove(lk, tls); 1141 | goto done_unlock; 1142 | } 1143 | if (lk->props.nlock < MAX_NLOCK) { 1144 | lk->props.nlock++; 1145 | } 1146 | ret = tls_append_held(tls, ptr); 1147 | if (ret) { 1148 | lksmith_error(ENOMEM, "lksmith_postlock(lock=%p, " 1149 | "thread=%s): failed to allocate space to store " 1150 | "another thread id.\n", ptr, tls->name); 1151 | goto done_unlock; 1152 | } 1153 | if (!lk->props.sleeper) { 1154 | tls->num_spins++; 1155 | } else if ((tls->num_spins > 0) && (!lk->props.spin_warn)) { 1156 | lksmith_error_with_ti(tls, EWOULDBLOCK, "lksmith_postlock(" 1157 | "lock=%p, thread=%s): performance problem: you are " 1158 | "taking a sleeping lock while holding a spin lock.\n", 1159 | ptr, tls->name); 1160 | lk->props.spin_warn = 1; 1161 | } 1162 | done_unlock: 1163 | r_pthread_mutex_unlock(&g_tree_lock); 1164 | done: 1165 | return; 1166 | } 1167 | 1168 | int lksmith_preunlock(const void *ptr) 1169 | { 1170 | struct lksmith_tls *tls; 1171 | struct lksmith_lock *lk; 1172 | int sleeper; 1173 | 1174 | tls = get_or_create_tls(); 1175 | if (!tls) { 1176 | lksmith_error(ENOMEM, "lksmith_preunlock(lock=%p): failed " 1177 | "to allocate thread-local storage.\n", ptr); 1178 | return ENOMEM; 1179 | } 1180 | if (!tls->intercept) 1181 | return 0; 1182 | r_pthread_mutex_lock(&g_tree_lock); 1183 | lk = lksmith_find(ptr); 1184 | if (!lk) { 1185 | lksmith_error_with_ti(tls, ENOENT, "lksmith_preunlock(lock=%p, " 1186 | "thread=%s): attempted to unlock an unknown lock.\n", 1187 | ptr, tls->name); 1188 | r_pthread_mutex_unlock(&g_tree_lock); 1189 | return ENOENT; 1190 | } 1191 | sleeper = lk->props.sleeper; 1192 | r_pthread_mutex_unlock(&g_tree_lock); 1193 | if (tls_contains_lid(tls, ptr) == 0) { 1194 | lksmith_error_with_ti(tls, EPERM, "lksmith_preunlock(lock=%p, " 1195 | "thread=%s): attempted to unlock a lock that this " 1196 | "thread does not currently hold.\n", ptr, tls->name); 1197 | return EPERM; 1198 | } 1199 | if (!sleeper) { 1200 | tls->num_spins--; 1201 | } 1202 | return 0; 1203 | } 1204 | 1205 | void lksmith_postunlock(const void *ptr) 1206 | { 1207 | struct lksmith_tls *tls; 1208 | struct lksmith_lock *lk; 1209 | int ret; 1210 | 1211 | tls = get_or_create_tls(); 1212 | if (!tls) { 1213 | lksmith_error(ENOMEM, "lksmith_postunlock(lock=%p): failed " 1214 | "to allocate thread-local storage.\n", ptr); 1215 | return; 1216 | } 1217 | if (!tls->intercept) 1218 | return; 1219 | ret = tls_remove_held(tls, ptr); 1220 | if (ret) { 1221 | lksmith_error(EIO, "lksmith_postunlock(lock=%p, " 1222 | "thread=%s): logic error: preunlock check told us " 1223 | "we had the lock, but we don't?\n", ptr, tls->name); 1224 | return; 1225 | } 1226 | r_pthread_mutex_lock(&g_tree_lock); 1227 | lk = lksmith_find(ptr); 1228 | if (!lk) { 1229 | lksmith_error_with_ti(tls, EIO, "lksmith_preunlock(lock=%p, " 1230 | "thread=%s): logic error: attempted to unlock an " 1231 | "unknown lock.\n", ptr, tls->name); 1232 | r_pthread_mutex_unlock(&g_tree_lock); 1233 | return; 1234 | } 1235 | ret = lk_holder_remove(lk, tls); 1236 | if (ret) { 1237 | lksmith_error(EIO, "lksmith_preunlock(lock=%p, thread=%s): " 1238 | "logic error: failed to find backtrace for this " 1239 | "thread in the list of stored backtraces for this " 1240 | "lock (error %d).\n", ptr, tls->name, ret); 1241 | r_pthread_mutex_unlock(&g_tree_lock); 1242 | return; 1243 | } 1244 | r_pthread_mutex_unlock(&g_tree_lock); 1245 | } 1246 | 1247 | int lksmith_check_locked(const void *ptr) 1248 | { 1249 | struct lksmith_tls *tls; 1250 | 1251 | tls = get_or_create_tls(); 1252 | if (!tls) { 1253 | lksmith_error(ENOMEM, "lksmith_check_locked(lock=%p): failed " 1254 | "to allocate thread-local storage.\n", ptr); 1255 | return ENOMEM; 1256 | } 1257 | if (!tls->intercept) 1258 | return 0; 1259 | return tls_contains_lid(tls, ptr) ? 0 : -1; 1260 | } 1261 | 1262 | int lksmith_cond_prewait(const void *cond, const void *mutex, 1263 | struct lksmith_cond **out) 1264 | { 1265 | struct lksmith_cond *cnd; 1266 | int ret; 1267 | 1268 | r_pthread_mutex_lock(&g_cond_tree_lock); 1269 | cnd = lksmith_cond_find(cond); 1270 | if (!cnd) { 1271 | ret = lksmith_cond_insert(cond, &cnd); 1272 | if (ret) { 1273 | r_pthread_mutex_unlock(&g_cond_tree_lock); 1274 | lksmith_error(ret, "lksmith_cond_insert(cond=%p," 1275 | "mutex=%p): failed ", cond, mutex); 1276 | return ret; 1277 | } 1278 | } 1279 | if (!cnd->lock) { 1280 | cnd->lock = mutex; 1281 | } else if (cnd->lock != mutex) { 1282 | r_pthread_mutex_unlock(&g_cond_tree_lock); 1283 | ret = EINVAL; 1284 | lksmith_error_with_ti(NULL, ret, "lksmith_cond_prewait(cond=%p," 1285 | "mutex=%p): you are currently waiting (or are about " 1286 | "to wait) on this condition variable with a different " 1287 | "lock, %p.", cond, mutex, cnd->lock); 1288 | return ret; 1289 | } 1290 | cnd->refcnt++; 1291 | *out = cnd; 1292 | r_pthread_mutex_unlock(&g_cond_tree_lock); 1293 | return 0; 1294 | } 1295 | 1296 | void lksmith_cond_postwait(struct lksmith_cond *cnd) 1297 | { 1298 | r_pthread_mutex_lock(&g_cond_tree_lock); 1299 | if (--cnd->refcnt == 0) { 1300 | cnd->lock = NULL; 1301 | } 1302 | r_pthread_mutex_unlock(&g_cond_tree_lock); 1303 | } 1304 | 1305 | int lksmith_cond_predestroy(const void *cond) 1306 | { 1307 | struct lksmith_cond *cnd; 1308 | uint64_t refcnt; 1309 | int ret; 1310 | 1311 | r_pthread_mutex_lock(&g_cond_tree_lock); 1312 | cnd = lksmith_cond_find(cond); 1313 | if (cnd) { 1314 | refcnt = cnd->refcnt; 1315 | } else { 1316 | refcnt = 0; 1317 | } 1318 | r_pthread_mutex_unlock(&g_cond_tree_lock); 1319 | if (refcnt != 0) { 1320 | ret = EINVAL; 1321 | lksmith_error_with_ti(NULL, ret, "lksmith_cond_predestroy(cond=%p): " 1322 | "you are trying to destroy a condition variable " 1323 | "that is in use!", cond); 1324 | return ret; 1325 | } 1326 | return 0; 1327 | } 1328 | 1329 | int lksmith_set_thread_name(const char *const name) 1330 | { 1331 | struct lksmith_tls *tls = get_or_create_tls(); 1332 | 1333 | if (!tls) { 1334 | lksmith_error(ENOMEM, "lksmith_set_thread_name(name=%s): " 1335 | "failed to allocate thread-local storage.\n", name); 1336 | return ENOMEM; 1337 | } 1338 | snprintf(tls->name, LKSMITH_THREAD_NAME_MAX, "%s", name); 1339 | return 0; 1340 | } 1341 | 1342 | const char* lksmith_get_thread_name(void) 1343 | { 1344 | struct lksmith_tls *tls = get_or_create_tls(); 1345 | 1346 | if (!tls) { 1347 | lksmith_error(ENOMEM, "lksmith_get_thread_name(): failed " 1348 | "to allocate thread-local storage.\n"); 1349 | return NULL; 1350 | } 1351 | return tls->name; 1352 | } 1353 | 1354 | int lksmith_get_ignored_frames(char *** ignored, int *num_ignored) 1355 | { 1356 | struct lksmith_tls *tls = get_or_create_tls(); 1357 | 1358 | if (!tls) { 1359 | lksmith_error(ENOMEM, "lksmith_get_ignored_frames(): failed " 1360 | "to allocate thread-local storage.\n"); 1361 | return ENOMEM; 1362 | } 1363 | *ignored = g_ignored_frames; 1364 | *num_ignored = g_num_ignored_frames; 1365 | return 0; 1366 | } 1367 | 1368 | int lksmith_get_ignored_frame_patterns(char *** ignored, int *num_ignored) 1369 | { 1370 | struct lksmith_tls *tls = get_or_create_tls(); 1371 | 1372 | if (!tls) { 1373 | lksmith_error(ENOMEM, "lksmith_get_ignored_frame_patterns(): " 1374 | "failed to allocate thread-local storage.\n"); 1375 | return ENOMEM; 1376 | } 1377 | *ignored = g_ignored_frame_patterns; 1378 | *num_ignored = g_num_ignored_frame_patterns; 1379 | return 0; 1380 | } 1381 | --------------------------------------------------------------------------------