1
0
mirror of https://github.com/ioacademy-jikim/debugging synced 2026-08-31 09:49:38 +00:00

first commit

This commit is contained in:
jikim
2015-12-13 22:34:58 +09:00
commit 0b589c7986
9455 changed files with 4350134 additions and 0 deletions
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1,54 @@
helgrind_x86_linux-hg_addrdescr.o: hg_addrdescr.c \
/usr/include/stdc-predef.h ../include/pub_tool_basics.h \
../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcprint.h ../include/pub_tool_libcassert.h \
../include/pub_tool_wordfm.h ../include/pub_tool_xarray.h \
../include/pub_tool_execontext.h ../include/pub_tool_debuginfo.h \
../include/pub_tool_xarray.h ../include/pub_tool_threadstate.h \
../include/pub_tool_aspacemgr.h ../include/pub_tool_addrinfo.h \
../include/pub_tool_aspacemgr.h hg_basics.h hg_wordset.h \
hg_lock_n_thread.h hg_addrdescr.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_wordfm.h:
../include/pub_tool_xarray.h:
../include/pub_tool_execontext.h:
../include/pub_tool_debuginfo.h:
../include/pub_tool_xarray.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_aspacemgr.h:
../include/pub_tool_addrinfo.h:
../include/pub_tool_aspacemgr.h:
hg_basics.h:
hg_wordset.h:
hg_lock_n_thread.h:
hg_addrdescr.h:
@@ -0,0 +1,26 @@
helgrind_x86_linux-hg_basics.o: hg_basics.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcassert.h ../include/pub_tool_mallocfree.h \
../include/pub_tool_threadstate.h hg_basics.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_mallocfree.h:
../include/pub_tool_threadstate.h:
hg_basics.h:
@@ -0,0 +1,75 @@
helgrind_x86_linux-hg_errors.o: hg_errors.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcassert.h ../include/pub_tool_libcprint.h \
../include/pub_tool_stacktrace.h ../include/pub_tool_execontext.h \
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
../include/pub_tool_wordfm.h ../include/pub_tool_xarray.h \
../include/pub_tool_debuginfo.h ../include/pub_tool_xarray.h \
../include/pub_tool_threadstate.h ../include/pub_tool_options.h \
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
../VEX/pub/libvex_ir.h ../include/pub_tool_aspacemgr.h \
../include/pub_tool_addrinfo.h ../include/pub_tool_aspacemgr.h \
hg_basics.h hg_addrdescr.h hg_wordset.h hg_lock_n_thread.h libhb.h \
hg_errors.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_stacktrace.h:
../include/pub_tool_execontext.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../include/pub_tool_wordfm.h:
../include/pub_tool_xarray.h:
../include/pub_tool_debuginfo.h:
../include/pub_tool_xarray.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_options.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_aspacemgr.h:
../include/pub_tool_addrinfo.h:
../include/pub_tool_aspacemgr.h:
hg_basics.h:
hg_addrdescr.h:
hg_wordset.h:
hg_lock_n_thread.h:
libhb.h:
hg_errors.h:
@@ -0,0 +1,34 @@
helgrind_x86_linux-hg_lock_n_thread.o: hg_lock_n_thread.c \
/usr/include/stdc-predef.h ../include/pub_tool_basics.h \
../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcassert.h ../include/pub_tool_execontext.h \
../include/pub_tool_threadstate.h ../include/pub_tool_wordfm.h \
hg_basics.h hg_wordset.h hg_lock_n_thread.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_execontext.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_wordfm.h:
hg_basics.h:
hg_wordset.h:
hg_lock_n_thread.h:
@@ -0,0 +1,148 @@
helgrind_x86_linux-hg_main.o: hg_main.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_gdbserver.h ../include/pub_tool_basics.h \
../include/pub_tool_libcassert.h ../include/pub_tool_libcbase.h \
../include/pub_tool_libcprint.h ../include/pub_tool_threadstate.h \
../include/pub_tool_tooliface.h ../include/pub_tool_errormgr.h \
../include/pub_tool_execontext.h ../VEX/pub/libvex.h \
../VEX/pub/libvex_basictypes.h ../VEX/pub/libvex_ir.h \
../include/pub_tool_hashtable.h ../include/pub_tool_replacemalloc.h \
../include/pub_tool_machine.h ../include/pub_tool_options.h \
../include/pub_tool_xarray.h ../include/pub_tool_stacktrace.h \
../include/pub_tool_wordfm.h ../include/pub_tool_debuginfo.h \
../include/pub_tool_xarray.h ../include/pub_tool_redir.h ../config.h \
../include/pub_tool_vki.h ../include/vki/vki-linux.h \
../include/vki/vki-posixtypes-x86-linux.h ../include/vki/vki-x86-linux.h \
../include/vki/vki-linux-drm.h ../include/vki/vki-xen.h \
../include/vki/vki-xen-x86.h ../include/vki/vki-xen-domctl.h \
../include/vki/vki-xen-sysctl.h ../include/vki/vki-xen-mmuext.h \
../include/vki/vki-xen-schedop.h ../include/vki/vki-xen-memory.h \
../include/vki/vki-xen-evtchn.h ../include/vki/vki-xen-gnttab.h \
../include/vki/vki-xen-version.h ../include/vki/vki-xen-hvm.h \
../include/vki/vki-xen-tmem.h ../include/vki/vki-xen-xsm.h \
../include/vki/vki-xen-physdev.h ../include/pub_tool_libcproc.h \
../include/pub_tool_vki.h ../include/pub_tool_aspacemgr.h \
../include/pub_tool_poolalloc.h ../include/pub_tool_addrinfo.h \
../include/pub_tool_aspacemgr.h hg_basics.h hg_wordset.h hg_addrdescr.h \
hg_lock_n_thread.h hg_errors.h libhb.h helgrind.h ../include/valgrind.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_gdbserver.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_hashtable.h:
../include/pub_tool_replacemalloc.h:
../include/pub_tool_machine.h:
../include/pub_tool_options.h:
../include/pub_tool_xarray.h:
../include/pub_tool_stacktrace.h:
../include/pub_tool_wordfm.h:
../include/pub_tool_debuginfo.h:
../include/pub_tool_xarray.h:
../include/pub_tool_redir.h:
../config.h:
../include/pub_tool_vki.h:
../include/vki/vki-linux.h:
../include/vki/vki-posixtypes-x86-linux.h:
../include/vki/vki-x86-linux.h:
../include/vki/vki-linux-drm.h:
../include/vki/vki-xen.h:
../include/vki/vki-xen-x86.h:
../include/vki/vki-xen-domctl.h:
../include/vki/vki-xen-sysctl.h:
../include/vki/vki-xen-mmuext.h:
../include/vki/vki-xen-schedop.h:
../include/vki/vki-xen-memory.h:
../include/vki/vki-xen-evtchn.h:
../include/vki/vki-xen-gnttab.h:
../include/vki/vki-xen-version.h:
../include/vki/vki-xen-hvm.h:
../include/vki/vki-xen-tmem.h:
../include/vki/vki-xen-xsm.h:
../include/vki/vki-xen-physdev.h:
../include/pub_tool_libcproc.h:
../include/pub_tool_vki.h:
../include/pub_tool_aspacemgr.h:
../include/pub_tool_poolalloc.h:
../include/pub_tool_addrinfo.h:
../include/pub_tool_aspacemgr.h:
hg_basics.h:
hg_wordset.h:
hg_addrdescr.h:
hg_lock_n_thread.h:
hg_errors.h:
libhb.h:
helgrind.h:
../include/valgrind.h:
@@ -0,0 +1,31 @@
helgrind_x86_linux-hg_wordset.o: hg_wordset.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcassert.h ../include/pub_tool_basics.h \
../include/pub_tool_libcbase.h ../include/pub_tool_libcprint.h \
../include/pub_tool_threadstate.h ../include/pub_tool_wordfm.h \
hg_basics.h hg_wordset.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_wordfm.h:
hg_basics.h:
hg_wordset.h:
@@ -0,0 +1,72 @@
helgrind_x86_linux-libhb_core.o: libhb_core.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_poolalloc.h ../include/pub_tool_basics.h \
../include/pub_tool_libcassert.h ../include/pub_tool_libcbase.h \
../include/pub_tool_libcprint.h ../include/pub_tool_mallocfree.h \
../include/pub_tool_wordfm.h ../include/pub_tool_hashtable.h \
../include/pub_tool_xarray.h ../include/pub_tool_oset.h \
../include/pub_tool_threadstate.h ../include/pub_tool_aspacemgr.h \
../include/pub_tool_stacktrace.h ../include/pub_tool_execontext.h \
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
../include/pub_tool_options.h ../VEX/pub/libvex.h \
../VEX/pub/libvex_basictypes.h ../VEX/pub/libvex_ir.h hg_basics.h \
hg_wordset.h hg_lock_n_thread.h hg_errors.h libhb.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_poolalloc.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_mallocfree.h:
../include/pub_tool_wordfm.h:
../include/pub_tool_hashtable.h:
../include/pub_tool_xarray.h:
../include/pub_tool_oset.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_aspacemgr.h:
../include/pub_tool_stacktrace.h:
../include/pub_tool_execontext.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../include/pub_tool_options.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
hg_basics.h:
hg_wordset.h:
hg_lock_n_thread.h:
hg_errors.h:
libhb.h:
@@ -0,0 +1,188 @@
vgpreload_helgrind_x86_linux_so-hg_intercepts.o: hg_intercepts.c \
/usr/include/stdc-predef.h ../include/pub_tool_basics.h \
../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_redir.h ../config.h ../include/pub_tool_clreq.h \
../include/valgrind.h helgrind.h ../include/valgrind.h \
/usr/include/stdio.h /usr/include/features.h \
/usr/include/i386-linux-gnu/sys/cdefs.h \
/usr/include/i386-linux-gnu/bits/wordsize.h \
/usr/include/i386-linux-gnu/gnu/stubs.h \
/usr/include/i386-linux-gnu/gnu/stubs-32.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stddef.h \
/usr/include/i386-linux-gnu/bits/types.h \
/usr/include/i386-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/i386-linux-gnu/bits/stdio_lim.h \
/usr/include/i386-linux-gnu/bits/sys_errlist.h \
/usr/include/i386-linux-gnu/bits/stdio.h \
/usr/include/i386-linux-gnu/bits/stdio2.h /usr/include/assert.h \
/usr/include/errno.h /usr/include/i386-linux-gnu/bits/errno.h \
/usr/include/linux/errno.h /usr/include/i386-linux-gnu/asm/errno.h \
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
/usr/include/pthread.h /usr/include/endian.h \
/usr/include/i386-linux-gnu/bits/endian.h \
/usr/include/i386-linux-gnu/bits/byteswap.h \
/usr/include/i386-linux-gnu/bits/byteswap-16.h /usr/include/sched.h \
/usr/include/time.h /usr/include/i386-linux-gnu/bits/sched.h \
/usr/include/i386-linux-gnu/bits/time.h \
/usr/include/i386-linux-gnu/bits/timex.h /usr/include/xlocale.h \
/usr/include/i386-linux-gnu/bits/pthreadtypes.h \
/usr/include/i386-linux-gnu/bits/setjmp.h /usr/include/semaphore.h \
/usr/include/i386-linux-gnu/sys/types.h \
/usr/include/i386-linux-gnu/sys/select.h \
/usr/include/i386-linux-gnu/bits/select.h \
/usr/include/i386-linux-gnu/bits/sigset.h \
/usr/include/i386-linux-gnu/bits/select2.h \
/usr/include/i386-linux-gnu/sys/sysmacros.h \
/usr/include/i386-linux-gnu/bits/semaphore.h /usr/include/fcntl.h \
/usr/include/i386-linux-gnu/bits/fcntl.h \
/usr/include/i386-linux-gnu/bits/fcntl-linux.h \
/usr/include/i386-linux-gnu/bits/uio.h \
/usr/include/i386-linux-gnu/bits/stat.h \
/usr/include/i386-linux-gnu/bits/fcntl2.h ../shared/vg_replace_strmem.c \
../include/pub_tool_poolalloc.h ../include/pub_tool_basics.h \
../include/pub_tool_hashtable.h ../include/pub_tool_tooliface.h \
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
../VEX/pub/libvex_ir.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_redir.h:
../config.h:
../include/pub_tool_clreq.h:
../include/valgrind.h:
helgrind.h:
../include/valgrind.h:
/usr/include/stdio.h:
/usr/include/features.h:
/usr/include/i386-linux-gnu/sys/cdefs.h:
/usr/include/i386-linux-gnu/bits/wordsize.h:
/usr/include/i386-linux-gnu/gnu/stubs.h:
/usr/include/i386-linux-gnu/gnu/stubs-32.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stddef.h:
/usr/include/i386-linux-gnu/bits/types.h:
/usr/include/i386-linux-gnu/bits/typesizes.h:
/usr/include/libio.h:
/usr/include/_G_config.h:
/usr/include/wchar.h:
/usr/include/i386-linux-gnu/bits/stdio_lim.h:
/usr/include/i386-linux-gnu/bits/sys_errlist.h:
/usr/include/i386-linux-gnu/bits/stdio.h:
/usr/include/i386-linux-gnu/bits/stdio2.h:
/usr/include/assert.h:
/usr/include/errno.h:
/usr/include/i386-linux-gnu/bits/errno.h:
/usr/include/linux/errno.h:
/usr/include/i386-linux-gnu/asm/errno.h:
/usr/include/asm-generic/errno.h:
/usr/include/asm-generic/errno-base.h:
/usr/include/pthread.h:
/usr/include/endian.h:
/usr/include/i386-linux-gnu/bits/endian.h:
/usr/include/i386-linux-gnu/bits/byteswap.h:
/usr/include/i386-linux-gnu/bits/byteswap-16.h:
/usr/include/sched.h:
/usr/include/time.h:
/usr/include/i386-linux-gnu/bits/sched.h:
/usr/include/i386-linux-gnu/bits/time.h:
/usr/include/i386-linux-gnu/bits/timex.h:
/usr/include/xlocale.h:
/usr/include/i386-linux-gnu/bits/pthreadtypes.h:
/usr/include/i386-linux-gnu/bits/setjmp.h:
/usr/include/semaphore.h:
/usr/include/i386-linux-gnu/sys/types.h:
/usr/include/i386-linux-gnu/sys/select.h:
/usr/include/i386-linux-gnu/bits/select.h:
/usr/include/i386-linux-gnu/bits/sigset.h:
/usr/include/i386-linux-gnu/bits/select2.h:
/usr/include/i386-linux-gnu/sys/sysmacros.h:
/usr/include/i386-linux-gnu/bits/semaphore.h:
/usr/include/fcntl.h:
/usr/include/i386-linux-gnu/bits/fcntl.h:
/usr/include/i386-linux-gnu/bits/fcntl-linux.h:
/usr/include/i386-linux-gnu/bits/uio.h:
/usr/include/i386-linux-gnu/bits/stat.h:
/usr/include/i386-linux-gnu/bits/fcntl2.h:
../shared/vg_replace_strmem.c:
../include/pub_tool_poolalloc.h:
../include/pub_tool_basics.h:
../include/pub_tool_hashtable.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
include $(top_srcdir)/Makefile.tool.am
EXTRA_DIST = \
docs/hg-manual.xml \
README_MSMProp2.txt \
README_YARD.txt
#----------------------------------------------------------------------------
# Headers, etc
#----------------------------------------------------------------------------
pkginclude_HEADERS = helgrind.h
noinst_HEADERS = \
hg_addrdescr.h \
hg_basics.h \
hg_errors.h \
hg_lock_n_thread.h \
hg_wordset.h \
libhb.h
#----------------------------------------------------------------------------
# helgrind-<platform>
#----------------------------------------------------------------------------
noinst_PROGRAMS = helgrind-@VGCONF_ARCH_PRI@-@VGCONF_OS@
if VGCONF_HAVE_PLATFORM_SEC
noinst_PROGRAMS += helgrind-@VGCONF_ARCH_SEC@-@VGCONF_OS@
endif
HELGRIND_SOURCES_COMMON = \
hg_addrdescr.c \
hg_basics.c \
hg_errors.c \
hg_lock_n_thread.c \
hg_main.c \
hg_wordset.c \
libhb_core.c
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES = \
$(HELGRIND_SOURCES_COMMON)
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = \
$(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) -O2
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_DEPENDENCIES = \
$(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@)
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = \
$(TOOL_LDADD_@VGCONF_PLATFORM_PRI_CAPS@)
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = \
$(TOOL_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LINK = \
$(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \
@VALT_LOAD_ADDRESS_PRI@ \
$(LINK) \
$(helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS) \
$(helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS)
if VGCONF_HAVE_PLATFORM_SEC
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_SOURCES = \
$(HELGRIND_SOURCES_COMMON)
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS = \
$(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) -O2
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_DEPENDENCIES = \
$(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_SEC_CAPS@)
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDADD = \
$(TOOL_LDADD_@VGCONF_PLATFORM_SEC_CAPS@)
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS = \
$(TOOL_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LINK = \
$(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \
@VALT_LOAD_ADDRESS_SEC@ \
$(LINK) \
$(helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS) \
$(helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS)
endif
#----------------------------------------------------------------------------
# vgpreload_helgrind-<platform>.so
#----------------------------------------------------------------------------
noinst_PROGRAMS += vgpreload_helgrind-@VGCONF_ARCH_PRI@-@VGCONF_OS@.so
if VGCONF_HAVE_PLATFORM_SEC
noinst_PROGRAMS += vgpreload_helgrind-@VGCONF_ARCH_SEC@-@VGCONF_OS@.so
endif
if VGCONF_OS_IS_DARWIN
noinst_DSYMS = $(noinst_PROGRAMS)
endif
VGPRELOAD_HELGRIND_SOURCES_COMMON = hg_intercepts.c
vgpreload_helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_SOURCES = \
$(VGPRELOAD_HELGRIND_SOURCES_COMMON)
vgpreload_helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
vgpreload_helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CFLAGS = \
$(AM_CFLAGS_PSO_@VGCONF_PLATFORM_PRI_CAPS@)
vgpreload_helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_DEPENDENCIES = \
$(LIBREPLACEMALLOC_@VGCONF_PLATFORM_PRI_CAPS@)
vgpreload_helgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_LDFLAGS = \
$(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) \
$(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
if VGCONF_HAVE_PLATFORM_SEC
vgpreload_helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_SOURCES = \
$(VGPRELOAD_HELGRIND_SOURCES_COMMON)
vgpreload_helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
vgpreload_helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CFLAGS = \
$(AM_CFLAGS_PSO_@VGCONF_PLATFORM_SEC_CAPS@)
vgpreload_helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_DEPENDENCIES = \
$(LIBREPLACEMALLOC_@VGCONF_PLATFORM_SEC_CAPS@)
vgpreload_helgrind_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_LDFLAGS = \
$(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) \
$(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,156 @@
MSMProp2, a simplified but functionally equivalent version of MSMProp1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Julian Seward, OpenWorks Ltd, 19 August 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note that this file does NOT describe the state machine used in the
svn://svn.valgrind.org/branches/YARD version of Helgrind. That state
machine is different again from any previously described machine.
See the file README_YARD.txt for more details on YARD.
----------------------
In early 2008 Konstantin Serebryany proposed "MSMProp1", a memory
state machine for data race detection. It is described at
http://code.google.com/p/data-race-test/wiki/MSMProp1
Implementation experiences show MSMProp1 is useful, but difficult to
implement efficiently. In particular keeping the memory usage under
control is complex and difficult.
This note points out a key simplification of MSMProp1, which makes it
easier to implement without changing the functionality.
The idea
~~~~~~~~
The core of the idea pertains to the "Condition" entry for MSMProp1
state machine rules E5 and E6(r). These are, respectively:
HB(SS, currS) and its negation
! HB(SS, currS).
Here, SS is a set of segments, and currS is a single segment. Each
segment contains a vector timestamp. The expression "HB(SS, currS)"
is intended to denote
for each segment S in SS . happens_before(S,currS)
where happens_before(S,T) means that S's vector timestamp is ordered
before-or-equal to T's vector timestamp.
In words, the expression
for each segment S in SS . happens_before(S,currS)
is equivalent to saying that currS has a timestamp which is
greater-than-equal to the timestamps of all the segments in SS.
The key observation is that this is equivalent to
happens_before( JOIN(SS), currS )
where JOIN is the lattice-theoretic "max" or "least upper bound"
operation on vector clocks. Given the definition of HB,
happens_before and (binary) JOIN, this is easy to prove.
The consequences
~~~~~~~~~~~~~~~~
With that observation in place, it is a short step to observe that
storing segment sets in MSMProp1 is unnecessary. Instead of
storing a segment set in each shadow value, just store and
update a single vector timestamp. The following two equivalences
hold:
MSMProp1 MSMProp2
adding a segment S join-ing S's vector timestamp
to the segment-set to the current vector timestamp
HB(SS,currS) happens_before(
currS's timestamp,
current vector timestamp )
Once it is no longer necessary to represent segment sets, it then
also becomes unnecessary to represent segments. This constitutes
a significant simplication to the implementation.
The resulting state machine, MSMProp2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MSMProp2 is isomorphic to MSMProp1, with the following changes:
States are New, Read(VTS,LS), Write(VTS,LS)
where LS is a lockset (as before) and VTS is a vector timestamp.
For a thread T with current lockset 'currLS' and current VTS 'currVTS'
making a memory access, the new rules are
Name Old-State Op Guard New-State Race-If
E1 New rd True Read(currVTS,currLS) False
E2 New wr True Write(currVTS,currLS) False
E3 Read(oldVTS,oldLS) rd True Read(newVTS,newLS) False
E4 Read(oldVTS,oldLS) wr True Write(newVTS,newLS) #newLS == 0
&& !hb(oldVTS,currVTS)
E5 Write(oldVTS,oldLS) rd hb(oldVTS, Read(currVTS,currLS) False
currVTS)
E6r Write(oldVTS,oldLS) rd !hb(oldVTS, Write(newVTS,newLS) #newLS == 0
currVTS) && !hb(oldVTS,currVTS)
E6w Write(oldVTS,oldLS) wr True Write(newVTS,newLS) #newLS == 0
&& !hb(oldVTS,currVTS)
where newVTS = join2(oldVTS,currVTS)
newLS = if hb(oldVTS,currVTS)
then currLS
else intersect(oldLS,currLS)
hb(vts1, vts2) = vts1 happens before or is equal to vts2
Interpretation of the states
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I always found the state names in MSMProp1 confusing. Both MSMProp1
and MSMProp2 are easier to understand if the states Read and Write are
renamed, like this:
old name new name
Read WriteConstraint
Write AllConstraint
The effect of a state Read(VTS,LS) is to constrain all later-observed
writes so that either (1) the writing thread holds at least one lock
in common with LS, or (2) those writes must happen-after VTS. If
neither of those two conditions hold, a race is reported.
Hence a Read state places a constraint on writes.
The effect of a state Write(VTS,LS) is similar, but it applies to all
later-observed accesses: either (1) the accessing thread holds at
least one lock in common with LS, or (2) those accesses must
happen-after VTS. If neither of those two conditions hold, a race is
reported.
Hence a Write state places a constraint on all accesses.
If we ignore the LS component of these states, the intuitive
interpretation of the VTS component is that it states the earliest
vector-time that the next write / access may safely happen.
@@ -0,0 +1,34 @@
YARD, Yet Another Race Detector, built on the Helgrind framework
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Julian Seward, OpenWorks Ltd, 19 August 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The YARD race detector lives in svn://svn.valgrind.org/branches/YARD.
It uses a new and relatively simple race detection engine, based on
the idea of shadowing each memory location with two vector timestamps,
indicating respectively the "earliest safe read point" and "earliest
safe write point". As far as I know this is a novel approach. Some
features of the implementation:
* Modularity. The entire race detection engine is placed in a
standalone library (libhb_core.c) with a simple interface (libhb.h).
This makes it easier to debug and verify the engine; indeed it can
be built as a standalone executable with test harness using "make -f
Makefile_sa".
* Simplified and scalable storage management, so that large programs,
with many synchronisation events, can be handled.
* Ability to report both call stacks involved in a race, without
excessive time or space overhead.
* Pure happens before operation, so as not to give any false
positives.
To use, build as usual and run as "--tool=helgrind".
You can disable lock order checking with --track-lockorders=no, as it
sometimes produces an annoying amount of output.
File diff suppressed because it is too large Load Diff
Binary file not shown.
@@ -0,0 +1,798 @@
/*
----------------------------------------------------------------
Notice that the above BSD-style license applies to this one file
(helgrind.h) only. The entire rest of Valgrind is licensed under
the terms of the GNU General Public License, version 2. See the
COPYING file in the source distribution for details.
----------------------------------------------------------------
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks LLP
info@open-works.co.uk
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
3. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
4. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------
Notice that the above BSD-style license applies to this one file
(helgrind.h) only. The entire rest of Valgrind is licensed under
the terms of the GNU General Public License, version 2. See the
COPYING file in the source distribution for details.
----------------------------------------------------------------
*/
#ifndef __HELGRIND_H
#define __HELGRIND_H
#include "valgrind.h"
/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
This enum comprises an ABI exported by Valgrind to programs
which use client requests. DO NOT CHANGE THE ORDER OF THESE
ENTRIES, NOR DELETE ANY -- add new ones at the end. */
typedef
enum {
VG_USERREQ__HG_CLEAN_MEMORY = VG_USERREQ_TOOL_BASE('H','G'),
/* The rest are for Helgrind's internal use. Not for end-user
use. Do not use them unless you are a Valgrind developer. */
/* Notify the tool what this thread's pthread_t is. */
_VG_USERREQ__HG_SET_MY_PTHREAD_T = VG_USERREQ_TOOL_BASE('H','G')
+ 256,
_VG_USERREQ__HG_PTH_API_ERROR, /* char*, int */
_VG_USERREQ__HG_PTHREAD_JOIN_POST, /* pthread_t of quitter */
_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, /* pth_mx_t*, long mbRec */
_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, /* pth_mx_t*, long isInit */
_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, /* pth_mx_t* */
_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, /* pth_mx_t* */
_VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_PRE, /* void*, long isTryLock */
_VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_POST, /* void* */
_VG_USERREQ__HG_PTHREAD_COND_SIGNAL_PRE, /* pth_cond_t* */
_VG_USERREQ__HG_PTHREAD_COND_BROADCAST_PRE, /* pth_cond_t* */
_VG_USERREQ__HG_PTHREAD_COND_WAIT_PRE, /* pth_cond_t*, pth_mx_t* */
_VG_USERREQ__HG_PTHREAD_COND_WAIT_POST, /* pth_cond_t*, pth_mx_t* */
_VG_USERREQ__HG_PTHREAD_COND_DESTROY_PRE, /* pth_cond_t*, long isInit */
_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, /* pth_rwlk_t* */
_VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, /* pth_rwlk_t* */
_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE, /* pth_rwlk_t*, long isW */
_VG_USERREQ__HG_PTHREAD_RWLOCK_ACQUIRED, /* void*, long isW */
_VG_USERREQ__HG_PTHREAD_RWLOCK_RELEASED, /* void* */
_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_POST, /* pth_rwlk_t* */
_VG_USERREQ__HG_POSIX_SEM_INIT_POST, /* sem_t*, ulong value */
_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, /* sem_t* */
_VG_USERREQ__HG_POSIX_SEM_RELEASED, /* void* */
_VG_USERREQ__HG_POSIX_SEM_ACQUIRED, /* void* */
_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, /* pth_bar_t*, ulong, ulong */
_VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, /* pth_bar_t* */
_VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, /* pth_bar_t* */
_VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE, /* pth_slk_t* */
_VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST, /* pth_slk_t* */
_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE, /* pth_slk_t* */
_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST, /* pth_slk_t* */
_VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE, /* pth_slk_t* */
_VG_USERREQ__HG_CLIENTREQ_UNIMP, /* char* */
_VG_USERREQ__HG_USERSO_SEND_PRE, /* arbitrary UWord SO-tag */
_VG_USERREQ__HG_USERSO_RECV_POST, /* arbitrary UWord SO-tag */
_VG_USERREQ__HG_USERSO_FORGET_ALL, /* arbitrary UWord SO-tag */
_VG_USERREQ__HG_RESERVED2, /* Do not use */
_VG_USERREQ__HG_RESERVED3, /* Do not use */
_VG_USERREQ__HG_RESERVED4, /* Do not use */
_VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, /* Addr a, ulong len */
_VG_USERREQ__HG_ARANGE_MAKE_TRACKED, /* Addr a, ulong len */
_VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, /* pth_bar_t*, ulong */
_VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK, /* Addr start_of_block */
_VG_USERREQ__HG_PTHREAD_COND_INIT_POST, /* pth_cond_t*, pth_cond_attr_t*/
_VG_USERREQ__HG_GNAT_MASTER_HOOK, /* void*d,void*m,Word ml */
_VG_USERREQ__HG_GNAT_MASTER_COMPLETED_HOOK, /* void*s,Word ml */
_VG_USERREQ__HG_GET_ABITS, /* Addr a,Addr abits, ulong len */
_VG_USERREQ__HG_PTHREAD_CREATE_BEGIN,
_VG_USERREQ__HG_PTHREAD_CREATE_END,
_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE, /* pth_mx_t*,long isTryLock */
_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST, /* pth_mx_t *,long tookLock */
_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST, /* pth_rwlk_t*,long isW,long */
_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE, /* pth_rwlk_t* */
_VG_USERREQ__HG_POSIX_SEM_POST_PRE, /* sem_t* */
_VG_USERREQ__HG_POSIX_SEM_POST_POST, /* sem_t* */
_VG_USERREQ__HG_POSIX_SEM_WAIT_PRE, /* sem_t* */
_VG_USERREQ__HG_POSIX_SEM_WAIT_POST, /* sem_t*, long tookLock */
_VG_USERREQ__HG_PTHREAD_COND_SIGNAL_POST, /* pth_cond_t* */
_VG_USERREQ__HG_PTHREAD_COND_BROADCAST_POST,/* pth_cond_t* */
_VG_USERREQ__HG_RTLD_BIND_GUARD, /* int flags */
_VG_USERREQ__HG_RTLD_BIND_CLEAR /* int flags */
} Vg_TCheckClientRequest;
/*----------------------------------------------------------------*/
/*--- ---*/
/*--- Implementation-only facilities. Not for end-user use. ---*/
/*--- For end-user facilities see below (the next section in ---*/
/*--- this file.) ---*/
/*--- ---*/
/*----------------------------------------------------------------*/
/* Do a client request. These are macros rather than a functions so
as to avoid having an extra frame in stack traces.
NB: these duplicate definitions in hg_intercepts.c. But here, we
have to make do with weaker typing (no definition of Word etc) and
no assertions, whereas in helgrind.h we can use those facilities.
Obviously it's important the two sets of definitions are kept in
sync.
The commented-out asserts should actually hold, but unfortunately
they can't be allowed to be visible here, because that would
require the end-user code to #include <assert.h>.
*/
#define DO_CREQ_v_W(_creqF, _ty1F,_arg1F) \
do { \
long int _arg1; \
/* assert(sizeof(_ty1F) == sizeof(long int)); */ \
_arg1 = (long int)(_arg1F); \
VALGRIND_DO_CLIENT_REQUEST_STMT( \
(_creqF), \
_arg1, 0,0,0,0); \
} while (0)
#define DO_CREQ_W_W(_resF, _dfltF, _creqF, _ty1F,_arg1F) \
do { \
long int _arg1; \
/* assert(sizeof(_ty1F) == sizeof(long int)); */ \
_arg1 = (long int)(_arg1F); \
_qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR( \
(_dfltF), \
(_creqF), \
_arg1, 0,0,0,0); \
_resF = _qzz_res; \
} while (0)
#define DO_CREQ_v_WW(_creqF, _ty1F,_arg1F, _ty2F,_arg2F) \
do { \
long int _arg1, _arg2; \
/* assert(sizeof(_ty1F) == sizeof(long int)); */ \
/* assert(sizeof(_ty2F) == sizeof(long int)); */ \
_arg1 = (long int)(_arg1F); \
_arg2 = (long int)(_arg2F); \
VALGRIND_DO_CLIENT_REQUEST_STMT( \
(_creqF), \
_arg1,_arg2,0,0,0); \
} while (0)
#define DO_CREQ_v_WWW(_creqF, _ty1F,_arg1F, \
_ty2F,_arg2F, _ty3F, _arg3F) \
do { \
long int _arg1, _arg2, _arg3; \
/* assert(sizeof(_ty1F) == sizeof(long int)); */ \
/* assert(sizeof(_ty2F) == sizeof(long int)); */ \
/* assert(sizeof(_ty3F) == sizeof(long int)); */ \
_arg1 = (long int)(_arg1F); \
_arg2 = (long int)(_arg2F); \
_arg3 = (long int)(_arg3F); \
VALGRIND_DO_CLIENT_REQUEST_STMT( \
(_creqF), \
_arg1,_arg2,_arg3,0,0); \
} while (0)
#define DO_CREQ_W_WWW(_resF, _dfltF, _creqF, _ty1F,_arg1F, \
_ty2F,_arg2F, _ty3F, _arg3F) \
do { \
long int _qzz_res; \
long int _arg1, _arg2, _arg3; \
/* assert(sizeof(_ty1F) == sizeof(long int)); */ \
_arg1 = (long int)(_arg1F); \
_arg2 = (long int)(_arg2F); \
_arg3 = (long int)(_arg3F); \
_qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR( \
(_dfltF), \
(_creqF), \
_arg1,_arg2,_arg3,0,0); \
_resF = _qzz_res; \
} while (0)
#define _HG_CLIENTREQ_UNIMP(_qzz_str) \
DO_CREQ_v_W(_VG_USERREQ__HG_CLIENTREQ_UNIMP, \
(char*),(_qzz_str))
/*----------------------------------------------------------------*/
/*--- ---*/
/*--- Helgrind-native requests. These allow access to ---*/
/*--- the same set of annotation primitives that are used ---*/
/*--- to build the POSIX pthread wrappers. ---*/
/*--- ---*/
/*----------------------------------------------------------------*/
/* ----------------------------------------------------------
For describing ordinary mutexes (non-rwlocks). For rwlock
descriptions see ANNOTATE_RWLOCK_* below.
---------------------------------------------------------- */
/* Notify here immediately after mutex creation. _mbRec == 0 for a
non-recursive mutex, 1 for a recursive mutex. */
#define VALGRIND_HG_MUTEX_INIT_POST(_mutex, _mbRec) \
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, \
void*,(_mutex), long,(_mbRec))
/* Notify here immediately before mutex acquisition. _isTryLock == 0
for a normal acquisition, 1 for a "try" style acquisition. */
#define VALGRIND_HG_MUTEX_LOCK_PRE(_mutex, _isTryLock) \
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_PRE, \
void*,(_mutex), long,(_isTryLock))
/* Notify here immediately after a successful mutex acquisition. */
#define VALGRIND_HG_MUTEX_LOCK_POST(_mutex) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_POST, \
void*,(_mutex))
/* Notify here immediately before a mutex release. */
#define VALGRIND_HG_MUTEX_UNLOCK_PRE(_mutex) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, \
void*,(_mutex))
/* Notify here immediately after a mutex release. */
#define VALGRIND_HG_MUTEX_UNLOCK_POST(_mutex) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, \
void*,(_mutex))
/* Notify here immediately before mutex destruction. */
#define VALGRIND_HG_MUTEX_DESTROY_PRE(_mutex) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, \
void*,(_mutex))
/* ----------------------------------------------------------
For describing semaphores.
---------------------------------------------------------- */
/* Notify here immediately after semaphore creation. */
#define VALGRIND_HG_SEM_INIT_POST(_sem, _value) \
DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST, \
void*, (_sem), unsigned long, (_value))
/* Notify here immediately after a semaphore wait (an acquire-style
operation) */
#define VALGRIND_HG_SEM_WAIT_POST(_sem) \
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_ACQUIRED, \
void*,(_sem))
/* Notify here immediately before semaphore post (a release-style
operation) */
#define VALGRIND_HG_SEM_POST_PRE(_sem) \
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_RELEASED, \
void*,(_sem))
/* Notify here immediately before semaphore destruction. */
#define VALGRIND_HG_SEM_DESTROY_PRE(_sem) \
DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, \
void*, (_sem))
/* ----------------------------------------------------------
For describing barriers.
---------------------------------------------------------- */
/* Notify here immediately before barrier creation. _count is the
capacity. _resizable == 0 means the barrier may not be resized, 1
means it may be. */
#define VALGRIND_HG_BARRIER_INIT_PRE(_bar, _count, _resizable) \
DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, \
void*,(_bar), \
unsigned long,(_count), \
unsigned long,(_resizable))
/* Notify here immediately before arrival at a barrier. */
#define VALGRIND_HG_BARRIER_WAIT_PRE(_bar) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, \
void*,(_bar))
/* Notify here immediately before a resize (change of barrier
capacity). If _newcount >= the existing capacity, then there is no
change in the state of any threads waiting at the barrier. If
_newcount < the existing capacity, and >= _newcount threads are
currently waiting at the barrier, then this notification is
considered to also have the effect of telling the checker that all
waiting threads have now moved past the barrier. (I can't think of
any other sane semantics.) */
#define VALGRIND_HG_BARRIER_RESIZE_PRE(_bar, _newcount) \
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, \
void*,(_bar), \
unsigned long,(_newcount))
/* Notify here immediately before barrier destruction. */
#define VALGRIND_HG_BARRIER_DESTROY_PRE(_bar) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, \
void*,(_bar))
/* ----------------------------------------------------------
For describing memory ownership changes.
---------------------------------------------------------- */
/* Clean memory state. This makes Helgrind forget everything it knew
about the specified memory range. Effectively this announces that
the specified memory range now "belongs" to the calling thread, so
that: (1) the calling thread can access it safely without
synchronisation, and (2) all other threads must sync with this one
to access it safely. This is particularly useful for memory
allocators that wish to recycle memory. */
#define VALGRIND_HG_CLEAN_MEMORY(_qzz_start, _qzz_len) \
DO_CREQ_v_WW(VG_USERREQ__HG_CLEAN_MEMORY, \
void*,(_qzz_start), \
unsigned long,(_qzz_len))
/* The same, but for the heap block starting at _qzz_blockstart. This
allows painting when we only know the address of an object, but not
its size, which is sometimes the case in C++ code involving
inheritance, and in which RTTI is not, for whatever reason,
available. Returns the number of bytes painted, which can be zero
for a zero-sized block. Hence, return values >= 0 indicate success
(the block was found), and the value -1 indicates block not
found, and -2 is returned when not running on Helgrind. */
#define VALGRIND_HG_CLEAN_MEMORY_HEAPBLOCK(_qzz_blockstart) \
(__extension__ \
({long int _npainted; \
DO_CREQ_W_W(_npainted, (-2)/*default*/, \
_VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK, \
void*,(_qzz_blockstart)); \
_npainted; \
}))
/* ----------------------------------------------------------
For error control.
---------------------------------------------------------- */
/* Tell H that an address range is not to be "tracked" until further
notice. This puts it in the NOACCESS state, in which case we
ignore all reads and writes to it. Useful for ignoring ranges of
memory where there might be races we don't want to see. If the
memory is subsequently reallocated via malloc/new/stack allocation,
then it is put back in the trackable state. Hence it is safe in
the situation where checking is disabled, the containing area is
deallocated and later reallocated for some other purpose. */
#define VALGRIND_HG_DISABLE_CHECKING(_qzz_start, _qzz_len) \
DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, \
void*,(_qzz_start), \
unsigned long,(_qzz_len))
/* And put it back into the normal "tracked" state, that is, make it
once again subject to the normal race-checking machinery. This
puts it in the same state as new memory allocated by this thread --
that is, basically owned exclusively by this thread. */
#define VALGRIND_HG_ENABLE_CHECKING(_qzz_start, _qzz_len) \
DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_TRACKED, \
void*,(_qzz_start), \
unsigned long,(_qzz_len))
/* Checks the accessibility bits for addresses [zza..zza+zznbytes-1].
If zzabits array is provided, copy the accessibility bits in zzabits.
Return values:
-2 if not running on helgrind
-1 if any parts of zzabits is not addressable
>= 0 : success.
When success, it returns the nr of addressable bytes found.
So, to check that a whole range is addressable, check
VALGRIND_HG_GET_ABITS(addr,NULL,len) == len
In addition, if you want to examine the addressability of each
byte of the range, you need to provide a non NULL ptr as
second argument, pointing to an array of unsigned char
of length len.
Addressable bytes are indicated with 0xff.
Non-addressable bytes are indicated with 0x00.
*/
#define VALGRIND_HG_GET_ABITS(zza,zzabits,zznbytes) \
(__extension__ \
({long int _res; \
DO_CREQ_W_WWW(_res, (-2)/*default*/, \
_VG_USERREQ__HG_GET_ABITS, \
void*,(zza), void*,(zzabits), \
unsigned long,(zznbytes)); \
_res; \
}))
/*----------------------------------------------------------------*/
/*--- ---*/
/*--- ThreadSanitizer-compatible requests ---*/
/*--- (mostly unimplemented) ---*/
/*--- ---*/
/*----------------------------------------------------------------*/
/* A quite-broad set of annotations, as used in the ThreadSanitizer
project. This implementation aims to be a (source-level)
compatible implementation of the macros defined in:
http://code.google.com/p/data-race-test/source
/browse/trunk/dynamic_annotations/dynamic_annotations.h
(some of the comments below are taken from the above file)
The implementation here is very incomplete, and intended as a
starting point. Many of the macros are unimplemented. Rather than
allowing unimplemented macros to silently do nothing, they cause an
assertion. Intention is to implement them on demand.
The major use of these macros is to make visible to race detectors,
the behaviour (effects) of user-implemented synchronisation
primitives, that the detectors could not otherwise deduce from the
normal observation of pthread etc calls.
Some of the macros are no-ops in Helgrind. That's because Helgrind
is a pure happens-before detector, whereas ThreadSanitizer uses a
hybrid lockset and happens-before scheme, which requires more
accurate annotations for correct operation.
The macros are listed in the same order as in dynamic_annotations.h
(URL just above).
I should point out that I am less than clear about the intended
semantics of quite a number of them. Comments and clarifications
welcomed!
*/
/* ----------------------------------------------------------------
These four allow description of user-level condition variables,
apparently in the style of POSIX's pthread_cond_t. Currently
unimplemented and will assert.
----------------------------------------------------------------
*/
/* Report that wait on the condition variable at address CV has
succeeded and the lock at address LOCK is now held. CV and LOCK
are completely arbitrary memory addresses which presumably mean
something to the application, but are meaningless to Helgrind. */
#define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_LOCK_WAIT")
/* Report that wait on the condition variable at CV has succeeded.
Variant w/o lock. */
#define ANNOTATE_CONDVAR_WAIT(cv) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_WAIT")
/* Report that we are about to signal on the condition variable at
address CV. */
#define ANNOTATE_CONDVAR_SIGNAL(cv) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_SIGNAL")
/* Report that we are about to signal_all on the condition variable at
CV. */
#define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_SIGNAL_ALL")
/* ----------------------------------------------------------------
Create completely arbitrary happens-before edges between threads.
If threads T1 .. Tn all do ANNOTATE_HAPPENS_BEFORE(obj) and later
(w.r.t. some notional global clock for the computation) thread Tm
does ANNOTATE_HAPPENS_AFTER(obj), then Helgrind will regard all
memory accesses done by T1 .. Tn before the ..BEFORE.. call as
happening-before all memory accesses done by Tm after the
..AFTER.. call. Hence Helgrind won't complain about races if Tm's
accesses afterwards are to the same locations as accesses before by
any of T1 .. Tn.
OBJ is a machine word (unsigned long, or void*), is completely
arbitrary, and denotes the identity of some synchronisation object
you're modelling.
You must do the _BEFORE call just before the real sync event on the
signaller's side, and _AFTER just after the real sync event on the
waiter's side.
If none of the rest of these macros make sense to you, at least
take the time to understand these two. They form the very essence
of describing arbitrary inter-thread synchronisation events to
Helgrind. You can get a long way just with them alone.
See also, extensive discussion on semantics of this in
https://bugs.kde.org/show_bug.cgi?id=243935
ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) is interim until such time
as bug 243935 is fully resolved. It instructs Helgrind to forget
about any ANNOTATE_HAPPENS_BEFORE calls on the specified object, in
effect putting it back in its original state. Once in that state,
a use of ANNOTATE_HAPPENS_AFTER on it has no effect on the calling
thread.
An implementation may optionally release resources it has
associated with 'obj' when ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj)
happens. Users are recommended to use
ANNOTATE_HAPPENS_BEFORE_FORGET_ALL to indicate when a
synchronisation object is no longer needed, so as to avoid
potential indefinite resource leaks.
----------------------------------------------------------------
*/
#define ANNOTATE_HAPPENS_BEFORE(obj) \
DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_SEND_PRE, void*,(obj))
#define ANNOTATE_HAPPENS_AFTER(obj) \
DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_RECV_POST, void*,(obj))
#define ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) \
DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_FORGET_ALL, void*,(obj))
/* ----------------------------------------------------------------
Memory publishing. The TSan sources say:
Report that the bytes in the range [pointer, pointer+size) are about
to be published safely. The race checker will create a happens-before
arc from the call ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) to
subsequent accesses to this memory.
I'm not sure I understand what this means exactly, nor whether it
is relevant for a pure h-b detector. Leaving unimplemented for
now.
----------------------------------------------------------------
*/
#define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_PUBLISH_MEMORY_RANGE")
/* DEPRECATED. Don't use it. */
/* #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) */
/* DEPRECATED. Don't use it. */
/* #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) */
/* ----------------------------------------------------------------
TSan sources say:
Instruct the tool to create a happens-before arc between
MU->Unlock() and MU->Lock(). This annotation may slow down the
race detector; normally it is used only when it would be
difficult to annotate each of the mutex's critical sections
individually using the annotations above.
If MU is a posix pthread_mutex_t then Helgrind will do this anyway.
In any case, leave as unimp for now. I'm unsure about the intended
behaviour.
----------------------------------------------------------------
*/
#define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX")
/* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */
/* #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) */
/* ----------------------------------------------------------------
TSan sources say:
Annotations useful when defining memory allocators, or when
memory that was protected in one way starts to be protected in
another.
Report that a new memory at "address" of size "size" has been
allocated. This might be used when the memory has been retrieved
from a free list and is about to be reused, or when a the locking
discipline for a variable changes.
AFAICS this is the same as VALGRIND_HG_CLEAN_MEMORY.
----------------------------------------------------------------
*/
#define ANNOTATE_NEW_MEMORY(address, size) \
VALGRIND_HG_CLEAN_MEMORY((address), (size))
/* ----------------------------------------------------------------
TSan sources say:
Annotations useful when defining FIFO queues that transfer data
between threads.
All unimplemented. Am not claiming to understand this (yet).
----------------------------------------------------------------
*/
/* Report that the producer-consumer queue object at address PCQ has
been created. The ANNOTATE_PCQ_* annotations should be used only
for FIFO queues. For non-FIFO queues use ANNOTATE_HAPPENS_BEFORE
(for put) and ANNOTATE_HAPPENS_AFTER (for get). */
#define ANNOTATE_PCQ_CREATE(pcq) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_CREATE")
/* Report that the queue at address PCQ is about to be destroyed. */
#define ANNOTATE_PCQ_DESTROY(pcq) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_DESTROY")
/* Report that we are about to put an element into a FIFO queue at
address PCQ. */
#define ANNOTATE_PCQ_PUT(pcq) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_PUT")
/* Report that we've just got an element from a FIFO queue at address
PCQ. */
#define ANNOTATE_PCQ_GET(pcq) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_GET")
/* ----------------------------------------------------------------
Annotations that suppress errors. It is usually better to express
the program's synchronization using the other annotations, but
these can be used when all else fails.
Currently these are all unimplemented. I can't think of a simple
way to implement them without at least some performance overhead.
----------------------------------------------------------------
*/
/* Report that we may have a benign race at "pointer", with size
"sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the
point where "pointer" has been allocated, preferably close to the point
where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC.
XXX: what's this actually supposed to do? And what's the type of
DESCRIPTION? When does the annotation stop having an effect?
*/
#define ANNOTATE_BENIGN_RACE(pointer, description) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_BENIGN_RACE")
/* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
the memory range [address, address+size). */
#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
VALGRIND_HG_DISABLE_CHECKING(address, size)
/* Request the analysis tool to ignore all reads in the current thread
until ANNOTATE_IGNORE_READS_END is called. Useful to ignore
intentional racey reads, while still checking other reads and all
writes. */
#define ANNOTATE_IGNORE_READS_BEGIN() \
_HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_READS_BEGIN")
/* Stop ignoring reads. */
#define ANNOTATE_IGNORE_READS_END() \
_HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_READS_END")
/* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */
#define ANNOTATE_IGNORE_WRITES_BEGIN() \
_HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_WRITES_BEGIN")
/* Stop ignoring writes. */
#define ANNOTATE_IGNORE_WRITES_END() \
_HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_WRITES_END")
/* Start ignoring all memory accesses (reads and writes). */
#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
do { \
ANNOTATE_IGNORE_READS_BEGIN(); \
ANNOTATE_IGNORE_WRITES_BEGIN(); \
} while (0)
/* Stop ignoring all memory accesses. */
#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
do { \
ANNOTATE_IGNORE_WRITES_END(); \
ANNOTATE_IGNORE_READS_END(); \
} while (0)
/* ----------------------------------------------------------------
Annotations useful for debugging.
Again, so for unimplemented, partly for performance reasons.
----------------------------------------------------------------
*/
/* Request to trace every access to ADDRESS. */
#define ANNOTATE_TRACE_MEMORY(address) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_TRACE_MEMORY")
/* Report the current thread name to a race detector. */
#define ANNOTATE_THREAD_NAME(name) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_THREAD_NAME")
/* ----------------------------------------------------------------
Annotations for describing behaviour of user-implemented lock
primitives. In all cases, the LOCK argument is a completely
arbitrary machine word (unsigned long, or void*) and can be any
value which gives a unique identity to the lock objects being
modelled.
We just pretend they're ordinary posix rwlocks. That'll probably
give some rather confusing wording in error messages, claiming that
the arbitrary LOCK values are pthread_rwlock_t*'s, when in fact
they are not. Ah well.
----------------------------------------------------------------
*/
/* Report that a lock has just been created at address LOCK. */
#define ANNOTATE_RWLOCK_CREATE(lock) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, \
void*,(lock))
/* Report that the lock at address LOCK is about to be destroyed. */
#define ANNOTATE_RWLOCK_DESTROY(lock) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, \
void*,(lock))
/* Report that the lock at address LOCK has just been acquired.
is_w=1 for writer lock, is_w=0 for reader lock. */
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_RWLOCK_ACQUIRED, \
void*,(lock), unsigned long,(is_w))
/* Report that the lock at address LOCK is about to be released. */
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_RELEASED, \
void*,(lock)) /* is_w is ignored */
/* -------------------------------------------------------------
Annotations useful when implementing barriers. They are not
normally needed by modules that merely use barriers.
The "barrier" argument is a pointer to the barrier object.
----------------------------------------------------------------
*/
/* Report that the "barrier" has been initialized with initial
"count". If 'reinitialization_allowed' is true, initialization is
allowed to happen multiple times w/o calling barrier_destroy() */
#define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_INIT")
/* Report that we are about to enter barrier_wait("barrier"). */
#define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY")
/* Report that we just exited barrier_wait("barrier"). */
#define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY")
/* Report that the "barrier" has been destroyed. */
#define ANNOTATE_BARRIER_DESTROY(barrier) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY")
/* ----------------------------------------------------------------
Annotations useful for testing race detectors.
----------------------------------------------------------------
*/
/* Report that we expect a race on the variable at ADDRESS. Use only
in unit tests for a race detector. */
#define ANNOTATE_EXPECT_RACE(address, description) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_EXPECT_RACE")
/* A no-op. Insert where you like to test the interceptors. */
#define ANNOTATE_NO_OP(arg) \
_HG_CLIENTREQ_UNIMP("ANNOTATE_NO_OP")
/* Force the race detector to flush its state. The actual effect depends on
* the implementation of the detector. */
#define ANNOTATE_FLUSH_STATE() \
_HG_CLIENTREQ_UNIMP("ANNOTATE_FLUSH_STATE")
#endif /* __HELGRIND_H */
@@ -0,0 +1,121 @@
/*--------------------------------------------------------------------*/
/*--- Address Description. ---*/
/*--- hg_addrdescr.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcprint.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_wordfm.h"
#include "pub_tool_xarray.h"
#include "pub_tool_execontext.h"
#include "pub_tool_debuginfo.h"
#include "pub_tool_threadstate.h"
#include "pub_tool_aspacemgr.h"
#include "pub_tool_addrinfo.h"
#include "hg_basics.h"
#include "hg_wordset.h"
#include "hg_lock_n_thread.h"
#include "hg_addrdescr.h" /* self */
void HG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai )
{
tl_assert(ai->tag == Addr_Undescribed);
/* hctxt/tnr/haddr/hszB describe the addr if it is a heap block. */
ExeContext* hctxt;
UInt tnr;
Addr haddr;
SizeT hszB;
/* First, see if it's in any heap block. Unfortunately this
means a linear search through all allocated heap blocks. The
assertion says that if it's detected as a heap block, then we
must have an allocation context for it, since all heap blocks
should have an allocation context. */
Bool is_heapblock
= HG_(mm_find_containing_block)(
&hctxt,
&tnr,
&haddr,
&hszB,
a
);
if (is_heapblock) {
tl_assert(is_heapblock == (hctxt != NULL));
ai->tag = Addr_Block;
ai->Addr.Block.block_kind = Block_Mallocd;
ai->Addr.Block.block_desc = "block";
ai->Addr.Block.block_szB = hszB;
ai->Addr.Block.rwoffset = (Word)(a) - (Word)(haddr);
ai->Addr.Block.allocated_at = hctxt;
VG_(initThreadInfo) (&ai->Addr.Block.alloc_tinfo);
ai->Addr.Block.alloc_tinfo.tnr = tnr;
ai->Addr.Block.freed_at = VG_(null_ExeContext)();;
} else {
/* No block found. Search a non-heap block description. */
VG_(describe_addr) (a, ai);
/* In case ai contains a tid, set tnr to the corresponding helgrind
thread number. */
if (ai->tag == Addr_Stack) {
Thread* thr = get_admin_threads();
tl_assert(ai->Addr.Stack.tinfo.tid);
while (thr) {
if (thr->coretid == ai->Addr.Stack.tinfo.tid) {
ai->Addr.Stack.tinfo.tnr = thr->errmsg_index;
break;
}
thr = thr->admin;
}
}
}
}
Bool HG_(get_and_pp_addrdescr) (Addr addr)
{
Bool ret;
AddrInfo glai;
glai.tag = Addr_Undescribed;
HG_(describe_addr) (addr, &glai);
VG_(pp_addrinfo) (addr, &glai);
ret = glai.tag != Addr_Unknown;
VG_(clear_addrinfo) (&glai);
return ret;
}
/*--------------------------------------------------------------------*/
/*--- end hg_addrdescr.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,66 @@
/*---------------------------------------------------------------------*/
/*--- Address Description, used e.g. to describe addresses involved ---*/
/*--- in race conditions, locks. ---*/
/*--- hg_addrdescr.h ---*/
/*---------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __HG_ADDRDESCR_H
#define __HG_ADDRDESCR_H
/* Describe an address as best you can, for error messages or
lock description, putting the result in ai.
This might allocate some memory in ai, to be cleared with
VG_(clear_addrinfo). */
extern void HG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai );
/* Get a readable description of addr, then print it using HG_(pp_addrdescr)
using xml False and VG_(printf) to emit the characters.
Returns True if a description was found/printed, False otherwise. */
extern Bool HG_(get_and_pp_addrdescr) (Addr a);
/* For error creation/address description:
map 'data_addr' to a malloc'd chunk, if any.
Slow linear search accelerated in some special cases normal hash
search of the mallocmeta table. This is an abuse of the normal file
structure since this is exported by hg_main.c, not hg_addrdesc.c. Oh
Well. Returns True if found, False if not. Zero-sized blocks are
considered to contain the searched-for address if they equal that
address. */
Bool HG_(mm_find_containing_block)( /*OUT*/ExeContext** where,
/*OUT*/UInt* tnr,
/*OUT*/Addr* payload,
/*OUT*/SizeT* szB,
Addr data_addr );
#endif /* ! __HG_ADDRDESCR_H */
/*--------------------------------------------------------------------*/
/*--- end hg_addrdescr.h ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,90 @@
/*--------------------------------------------------------------------*/
/*--- Basic definitions for all of Helgrind. ---*/
/*--- hg_basics.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_mallocfree.h"
#include "pub_tool_threadstate.h"
#include "hg_basics.h" /* self */
/*----------------------------------------------------------------*/
/*--- Very basic stuff ---*/
/*----------------------------------------------------------------*/
void* HG_(zalloc) ( const HChar* cc, SizeT n )
{
void* p;
tl_assert(n > 0);
p = VG_(malloc)( cc, n );
VG_(memset)(p, 0, n);
return p;
}
void HG_(free) ( void* p )
{
tl_assert(p);
VG_(free)(p);
}
HChar* HG_(strdup) ( const HChar* cc, const HChar* s )
{
return VG_(strdup)( cc, s );
}
/*----------------------------------------------------------------*/
/*--- Command line options ---*/
/*----------------------------------------------------------------*/
/* Description of these flags is in hg_basics.h. */
Bool HG_(clo_track_lockorders) = True;
Bool HG_(clo_cmp_race_err_addrs) = False;
UWord HG_(clo_history_level) = 2;
UWord HG_(clo_conflict_cache_size) = 2000000;
UWord HG_(clo_sanity_flags) = 0;
Bool HG_(clo_free_is_write) = False;
UWord HG_(clo_vts_pruning) = 1;
Bool HG_(clo_check_stack_refs) = True;
/*--------------------------------------------------------------------*/
/*--- end hg_basics.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,133 @@
/*--------------------------------------------------------------------*/
/*--- Basic definitions for all of Helgrind. ---*/
/*--- hg_basics.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __HG_BASICS_H
#define __HG_BASICS_H
/*----------------------------------------------------------------*/
/*--- Very basic stuff ---*/
/*----------------------------------------------------------------*/
#define HG_(str) VGAPPEND(vgHelgrind_,str)
void* HG_(zalloc) ( const HChar* cc, SizeT n );
void HG_(free) ( void* p );
HChar* HG_(strdup) ( const HChar* cc, const HChar* s );
static inline Bool HG_(is_sane_ThreadId) ( ThreadId coretid ) {
return coretid >= 0 && coretid < VG_N_THREADS;
}
/*----------------------------------------------------------------*/
/*--- Command line options ---*/
/*----------------------------------------------------------------*/
/* Flags for controlling for which events sanity checking is done */
#define SCE_THREADS (1<<0) // Sanity check at thread create/join
#define SCE_LOCKS (1<<1) // Sanity check at lock events
#define SCE_BIGRANGE (1<<2) // Sanity check at big mem range events
#define SCE_ACCESS (1<<3) // Sanity check at mem accesses
#define SCE_LAOG (1<<4) // Sanity check at significant LAOG events
#define SCE_BIGRANGE_T 256 // big mem range minimum size
/* Enable/disable lock order checking. Sometimes it produces a lot of
errors, possibly genuine, which nevertheless can be very
annoying. */
extern Bool HG_(clo_track_lockorders);
/* When comparing race errors for equality, should the race address be
taken into account? For users, no, but for verification purposes
(regtesting) this is sometimes important. */
extern Bool HG_(clo_cmp_race_err_addrs);
/* Controls how much history to collect, in order to show conflicting
accesses. There are three levels:
0: "none": don't collect any history. Fastest, but means we can
only show one of the two stacks in a race.
1: "approx": collect one stack trace per (notional) segment, that
is, collect a stack trace for a thread every time its vector
clock changes. This facilitates showing the bounds of the
conflicting segment(s), with relatively small overhead.
2: "full": collect a stack trace every time the constraints for a
location change. This facilitates showing both stack traces in
a race, although can be very expensive in time and space
(depends on the rate that threads "drag" locations across
vector-clock-change boundaries ("dragovers"). This involves
collecting and storing large numbers of call stacks just in case
we might need to show them later, and so is expensive (although
very useful). */
extern UWord HG_(clo_history_level);
/* When doing "full" history collection, this determines the size of
the conflicting-access cache, measured in terms of maximum possible
number of elements in the previous-access map. Must be between 10k
amd 10 million. Default is 1 million. */
extern UWord HG_(clo_conflict_cache_size);
/* Sanity check level. This is an or-ing of
SCE_{THREADS,LOCKS,BIGRANGE,ACCESS,LAOG}. */
extern UWord HG_(clo_sanity_flags);
/* Treat heap frees as if the memory was written immediately prior to
the free. This shakes out races in which memory is referenced by
one thread, and freed by another, and there's no observable
synchronisation event to guarantee that the reference happens
before the free. */
extern Bool HG_(clo_free_is_write);
/* Controls the application of VTS pruning. There are three levels:
0: "never": VTS pruning is never done
1: "auto": (the default): VTS pruning is sometimes done after VTS
GCs, just frequently enough to keep space use under control, as
determined by heuristics in libhb_core.c.
2: "always": VTS pruning is done after every VTS GC. This is
mostly a big time waster, but minimises space use. */
extern UWord HG_(clo_vts_pruning);
/* When False, race checking ignores memory references which are to
the stack, which speeds things up a bit. Default: True. */
extern Bool HG_(clo_check_stack_refs);
#endif /* ! __HG_BASICS_H */
/*--------------------------------------------------------------------*/
/*--- end hg_basics.h ---*/
/*--------------------------------------------------------------------*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,94 @@
/*--------------------------------------------------------------------*/
/*--- Error management for Helgrind. ---*/
/*--- hg_errors.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __HG_ERRORS_H
#define __HG_ERRORS_H
/* The standard bundle of error management functions that we are
required to present to the core/tool interface at startup. */
Bool HG_(eq_Error) ( VgRes not_used, const Error* e1, const Error* e2 );
void HG_(before_pp_Error) ( const Error* err );
void HG_(pp_Error) ( const Error* err );
UInt HG_(update_extra) ( const Error* err );
Bool HG_(recognised_suppression) ( const HChar* name, Supp *su );
Bool HG_(read_extra_suppression_info) ( Int fd, HChar** bufpp, SizeT* nBufp,
Int* lineno, Supp* su );
Bool HG_(error_matches_suppression) ( const Error* err, const Supp* su );
const HChar* HG_(get_error_name) ( const Error* err );
SizeT HG_(get_extra_suppression_info) ( const Error* err,
/*OUT*/HChar* buf, Int nBuf );
SizeT HG_(print_extra_suppression_use) ( const Supp* su,
/*OUT*/HChar* buf, Int nBuf );
void HG_(update_extra_suppression_use) ( const Error* err, const Supp* su );
/* Functions for recording various kinds of errors. */
void HG_(record_error_Race) ( Thread* thr,
Addr data_addr, Int szB, Bool isWrite,
Thread* h1_confthr,
ExeContext* h1_ct_segstart,
ExeContext* h1_ct_mbsegend );
void HG_(record_error_UnlockUnlocked) ( Thread*, Lock* );
void HG_(record_error_UnlockForeign) ( Thread*, Thread*, Lock* );
void HG_(record_error_UnlockBogus) ( Thread*, Addr );
void HG_(record_error_PthAPIerror) ( Thread*, const HChar*, Word,
const HChar* );
/* Function for printing the details about an access */
void HG_(print_access) (StackTrace ips, UInt n_ips,
Thr* thr_a,
Addr ga,
SizeT SzB,
Bool isW,
WordSetID locksHeldW );
/* see the implementation for meaning of these params */
void HG_(record_error_LockOrder) ( Thread*, Lock*, Lock*,
ExeContext*, ExeContext*,
ExeContext* );
void HG_(record_error_Misc_w_aux) ( Thread*, const HChar* errstr,
const HChar* auxstr,
ExeContext* auxctx );
void HG_(record_error_Misc) ( Thread* thr, const HChar* errstr );
/* Statistics pertaining to error management. */
extern ULong HG_(stats__LockN_to_P_queries);
extern ULong HG_(stats__LockN_to_P_get_map_size) ( void );
extern ULong HG_(stats__string_table_queries);
extern ULong HG_(stats__string_table_get_map_size) ( void );
#endif /* ! __HG_ERRORS_H */
/*--------------------------------------------------------------------*/
/*--- end hg_errors.h ---*/
/*--------------------------------------------------------------------*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,123 @@
/*--------------------------------------------------------------------*/
/*--- Definitions for Locks and Threads. ---*/
/*--- hg_lock_n_thread.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_execontext.h"
#include "pub_tool_threadstate.h"
#include "pub_tool_wordfm.h"
#include "hg_basics.h"
#include "hg_wordset.h"
#include "hg_lock_n_thread.h" /* self */
/*----------------------------------------------------------------*/
/*--- Sanity checking ---*/
/*----------------------------------------------------------------*/
inline Bool HG_(is_sane_Thread) ( Thread* thr ) {
return thr != NULL && thr->magic == Thread_MAGIC;
}
static Bool is_sane_Bag_of_Threads ( WordBag* bag )
{
Thread* thr;
UWord count;
VG_(initIterBag)( bag );
while (VG_(nextIterBag)( bag, (UWord*)&thr, &count )) {
if (count < 1) return False;
if (!HG_(is_sane_Thread)(thr)) return False;
}
VG_(doneIterBag)( bag );
return True;
}
static Bool is_sane_Lock_BASE ( Lock* lock )
{
if (lock == NULL
|| (lock->magic != LockN_MAGIC && lock->magic != LockP_MAGIC))
return False;
switch (lock->kind) {
case LK_mbRec: case LK_nonRec: case LK_rdwr: break;
default: return False;
}
if (lock->heldBy == NULL) {
if (lock->acquired_at != NULL) return False;
/* Unheld. We arbitrarily require heldW to be False. */
return !lock->heldW;
} else {
if (lock->acquired_at == NULL) return False;
}
/* If heldBy is non-NULL, we require it to contain at least one
thread. */
if (VG_(isEmptyBag)(lock->heldBy))
return False;
/* Lock is either r- or w-held. */
if (!is_sane_Bag_of_Threads(lock->heldBy))
return False;
if (lock->heldW) {
/* Held in write-mode */
if ((lock->kind == LK_nonRec || lock->kind == LK_rdwr)
&& !VG_(isSingletonTotalBag)(lock->heldBy))
return False;
} else {
/* Held in read-mode */
if (lock->kind != LK_rdwr) return False;
}
return True;
}
Bool HG_(is_sane_LockP) ( Lock* lock ) {
return lock != NULL
&& lock->magic == LockP_MAGIC
&& lock->hbso == NULL
&& is_sane_Lock_BASE(lock);
}
Bool HG_(is_sane_LockN) ( Lock* lock ) {
return lock != NULL
&& lock->magic == LockN_MAGIC
&& lock->hbso != NULL
&& is_sane_Lock_BASE(lock);
}
Bool HG_(is_sane_LockNorP) ( Lock* lock ) {
return is_sane_Lock_BASE(lock);
}
/*--------------------------------------------------------------------*/
/*--- end hg_lock_n_thread.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,198 @@
/*--------------------------------------------------------------------*/
/*--- Definitions for Locks and Threads. ---*/
/*--- hg_lock_n_thread.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __HG_LOCK_N_THREAD_H
#define __HG_LOCK_N_THREAD_H
/*----------------------------------------------------------------*/
/*--- Primary data definitions ---*/
/*----------------------------------------------------------------*/
/* Magic numbers, for doing assertions that structures really are of
the right type. Useful as some of the code can get a bit
complex. */
#define Thread_MAGIC 0x504fc5e5
#define LockN_MAGIC 0x6545b557 /* normal nonpersistent locks */
#define LockP_MAGIC 0x755b5456 /* persistent (copied) locks */
/* These are handles for Word sets. CONSTRAINTS: must be small ints
numbered from zero, since 32-bit versions of them are used to
encode lock-sets in libhb's history records (Thr_n_RCEC). */
typedef WordSet WordSetID;
/* Synchronisation Objects, exported abstractly by libhb. */
typedef struct _SO SO;
/* Thr, libhb's private thread record, exported abstractly. Thr's are
allocated and never deallocated (simply leaked). Also ThrID, which
is a small integer which uniquely identifies a Thr and which is
used in ScalarTS because it is smaller than a Thr*. There is a 1-1
mapping between Thr's and ThrIDs. */
typedef struct _Thr Thr;
typedef UInt ThrID;
/* Stores information about a thread. Addresses of these also serve
as unique thread identifiers and so are never freed, so they should
be as small as possible. Freeing Thread structures makes the
storage management just too complex, and most programs don't create
many threads, so tolerating this leak seems like a not-bad
tradeoff.
Since these are never freed, the .coretid field only indicates the
core's ThreadId associated with this Thread whilst it is alive.
Once the thread finishes, the ThreadId is set to
VG_INVALID_THREADID.
The core may later re-use the same ThreadId for what is a logically
completely different thread, which of course must have a different
Thread structure. */
typedef
struct _Thread {
/* ADMIN */
struct _Thread* admin;
UInt magic;
Thr* hbthr; /* which in turn points back here .. */
ThreadId coretid; /* .. via its hgthread field */
/* USEFUL */
WordSetID locksetA; /* WordSet of Lock* currently held by thread */
WordSetID locksetW; /* subset of locksetA held in w-mode */
/* EXPOSITION */
/* Place where parent was when this thread was created. */
ExeContext* created_at;
Bool announced;
/* Index for generating references in error messages. */
Int errmsg_index;
/* Nesting level of pthread_create(). New memory allocated is untracked
when this value is > 0: race reporting is suppressed there. DRD does
the same thing implicitly. This is necessary because for example
Solaris libc caches many objects and reuses them for different threads
and that confuses Helgrind. With libvki it would be possible to
explictly use VG_USERREQ__HG_CLEAN_MEMORY on such objects.
Also mutex activity is ignored so that they do not impose false
ordering between creator and created thread. */
Int pthread_create_nesting_level;
/* Nesting level of synchronization functions called by the client.
Loads and stores are ignored when its value > 0.
Currently this is used solely for suppressing races of primitive
synchronization objects themselves - mutexes, condition variables,
read-write locks and their associated sleep queues.
See also documentation for command line option
--ignore-thread-creation. */
Int synchr_nesting;
#if defined(VGO_solaris)
Int bind_guard_flag; /* Bind flag from the runtime linker. */
#endif /* VGO_solaris */
}
Thread;
/* Get hg's admin_threads value, so libhb can visit all of them. */
Thread* get_admin_threads ( void );
/* Stores information about a lock's current state. These are
allocated and later freed (when the containing memory becomes
NoAccess). This gives a problem for the XError type, which
contains Lock*s. Solution is to copy any Lock which is to be
incorporated into an XErrors, so as to make it independent from the
'normal' collection of Locks, which can come and go. When the lock
is copied, its .magic is changed from LockN_Magic to
LockP_Magic. */
/* Lock kinds. */
typedef
enum {
LK_mbRec=1001, /* normal mutex, possibly recursive */
LK_nonRec, /* normal mutex, definitely non recursive */
LK_rdwr /* reader-writer lock */
}
LockKind;
typedef
struct _Lock {
/* ADMIN */
struct _Lock* admin_next; /* fields for a double linked */
struct _Lock* admin_prev; /* list of these locks */
ULong unique; /* used for persistence-hashing */
UInt magic; /* LockN_MAGIC or LockP_MAGIC */
/* EXPOSITION */
/* Place where lock first came to the attention of Helgrind. */
ExeContext* appeared_at;
/* If the lock is held, place where the lock most recently made
an unlocked->locked transition. Must be sync'd with .heldBy:
either both NULL or both non-NULL. */
ExeContext* acquired_at;
/* USEFUL-STATIC */
SO* hbso; /* associated SO */
Addr guestaddr; /* Guest address of lock */
LockKind kind; /* what kind of lock this is */
/* USEFUL-DYNAMIC */
Bool heldW;
WordBag* heldBy; /* bag of threads that hold this lock */
/* .heldBy is NULL: lock is unheld, and .heldW is meaningless
but arbitrarily set to False
.heldBy is non-NULL:
.heldW is True: lock is w-held by threads in heldBy
.heldW is False: lock is r-held by threads in heldBy
Either way, heldBy may not validly be an empty Bag.
for LK_nonRec, r-holdings are not allowed, and w-holdings may
only have sizeTotal(heldBy) == 1
for LK_mbRec, r-holdings are not allowed, and w-holdings may
only have sizeUnique(heldBy) == 1
for LK_rdwr, w-holdings may only have sizeTotal(heldBy) == 1 */
}
Lock;
#define Lock_INVALID ((Lock*)1UL)
/*----------------------------------------------------------------*/
/*--- Sanity checking ---*/
/*----------------------------------------------------------------*/
Bool HG_(is_sane_Thread) ( Thread* thr );
Bool HG_(is_sane_LockP) ( Lock* lock );
Bool HG_(is_sane_LockN) ( Lock* lock );
Bool HG_(is_sane_LockNorP) ( Lock* lock );
#endif /* ! __HG_LOCK_N_THREAD_H */
/*--------------------------------------------------------------------*/
/*--- end hg_lock_n_thread.h ---*/
/*--------------------------------------------------------------------*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,945 @@
/*--------------------------------------------------------------------*/
/*--- Sets of words, with unique set identifiers. ---*/
/*--- hg_wordset.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks LLP
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcprint.h"
#include "pub_tool_threadstate.h"
#include "pub_tool_wordfm.h"
#include "hg_basics.h"
#include "hg_wordset.h" /* self */
// define to 1 to have (a lot of) debugging of add/re-use/die WSU entries.
#define HG_DEBUG 0
//------------------------------------------------------------------//
//--- Word Cache ---//
//------------------------------------------------------------------//
typedef
struct { UWord arg1; UWord arg2; UWord res; }
WCacheEnt;
/* Each cache is a fixed sized array of N_WCACHE_STAT_MAX entries.
However only the first .dynMax are used. This is because at some
point, expanding the cache further overall gives a slowdown because
searching more entries more than negates any performance advantage
from caching those entries in the first place. Hence use .dynMax
to allow the size of the cache(s) to be set differently for each
different WordSetU. */
#define N_WCACHE_STAT_MAX 32
typedef
struct {
WCacheEnt ent[N_WCACHE_STAT_MAX];
UWord dynMax; /* 1 .. N_WCACHE_STAT_MAX inclusive */
UWord inUse; /* 0 .. dynMax inclusive */
}
WCache;
#define WCache_INIT(_zzcache,_zzdynmax) \
do { \
tl_assert((_zzdynmax) >= 1); \
tl_assert((_zzdynmax) <= N_WCACHE_STAT_MAX); \
(_zzcache).dynMax = (_zzdynmax); \
(_zzcache).inUse = 0; \
} while (0)
#define WCache_LOOKUP_AND_RETURN(_retty,_zzcache,_zzarg1,_zzarg2) \
do { \
UWord _i; \
UWord _arg1 = (UWord)(_zzarg1); \
UWord _arg2 = (UWord)(_zzarg2); \
WCache* _cache = &(_zzcache); \
tl_assert(_cache->dynMax >= 1); \
tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX); \
tl_assert(_cache->inUse >= 0); \
tl_assert(_cache->inUse <= _cache->dynMax); \
if (_cache->inUse > 0) { \
if (_cache->ent[0].arg1 == _arg1 \
&& _cache->ent[0].arg2 == _arg2) \
return (_retty)_cache->ent[0].res; \
for (_i = 1; _i < _cache->inUse; _i++) { \
if (_cache->ent[_i].arg1 == _arg1 \
&& _cache->ent[_i].arg2 == _arg2) { \
WCacheEnt tmp = _cache->ent[_i-1]; \
_cache->ent[_i-1] = _cache->ent[_i]; \
_cache->ent[_i] = tmp; \
return (_retty)_cache->ent[_i-1].res; \
} \
} \
} \
} while (0)
#define WCache_UPDATE(_zzcache,_zzarg1,_zzarg2,_zzresult) \
do { \
Word _i; \
UWord _arg1 = (UWord)(_zzarg1); \
UWord _arg2 = (UWord)(_zzarg2); \
UWord _res = (UWord)(_zzresult); \
WCache* _cache = &(_zzcache); \
tl_assert(_cache->dynMax >= 1); \
tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX); \
tl_assert(_cache->inUse >= 0); \
tl_assert(_cache->inUse <= _cache->dynMax); \
if (_cache->inUse < _cache->dynMax) \
_cache->inUse++; \
for (_i = _cache->inUse-1; _i >= 1; _i--) \
_cache->ent[_i] = _cache->ent[_i-1]; \
_cache->ent[0].arg1 = _arg1; \
_cache->ent[0].arg2 = _arg2; \
_cache->ent[0].res = _res; \
} while (0)
//------------------------------------------------------------------//
//--- WordSet ---//
//--- Implementation ---//
//------------------------------------------------------------------//
typedef
struct {
WordSetU* owner; /* for sanity checking */
UWord* words;
UWord size; /* Really this should be SizeT */
}
WordVec;
/* ix2vec[0 .. ix2vec_used-1] are pointers to the lock sets (WordVecs)
really. vec2ix is the inverse mapping, mapping WordVec* to the
corresponding ix2vec entry number. The two mappings are mutually
redundant.
If a WordVec WV is marked as dead by HG(dieWS), WV is removed from
vec2ix. The entry of the dead WVs in ix2vec are used to maintain a
linked list of free (to be re-used) ix2vec entries. */
struct _WordSetU {
void* (*alloc)(const HChar*,SizeT);
const HChar* cc;
void (*dealloc)(void*);
WordFM* vec2ix; /* WordVec-to-WordSet mapping tree */
WordVec** ix2vec; /* WordSet-to-WordVec mapping array */
UWord ix2vec_size;
UWord ix2vec_used;
WordVec** ix2vec_free;
WordSet empty; /* cached, for speed */
/* Caches for some operations */
WCache cache_addTo;
WCache cache_delFrom;
WCache cache_intersect;
WCache cache_minus;
/* Stats */
UWord n_add;
UWord n_add_uncached;
UWord n_del;
UWord n_del_uncached;
UWord n_die;
UWord n_union;
UWord n_intersect;
UWord n_intersect_uncached;
UWord n_minus;
UWord n_minus_uncached;
UWord n_elem;
UWord n_doubleton;
UWord n_isEmpty;
UWord n_isSingleton;
UWord n_anyElementOf;
UWord n_isSubsetOf;
};
/* Create a new WordVec of the given size. */
static WordVec* new_WV_of_size ( WordSetU* wsu, UWord sz )
{
WordVec* wv;
tl_assert(sz >= 0);
wv = wsu->alloc( wsu->cc, sizeof(WordVec) );
wv->owner = wsu;
wv->words = NULL;
wv->size = sz;
if (sz > 0) {
wv->words = wsu->alloc( wsu->cc, (SizeT)sz * sizeof(UWord) );
}
return wv;
}
static void delete_WV ( WordVec* wv )
{
void (*dealloc)(void*) = wv->owner->dealloc;
if (wv->words) {
dealloc(wv->words);
}
dealloc(wv);
}
static void delete_WV_for_FM ( UWord wv ) {
delete_WV( (WordVec*)wv );
}
static Word cmp_WordVecs_for_FM ( UWord wv1W, UWord wv2W )
{
UWord i;
WordVec* wv1 = (WordVec*)wv1W;
WordVec* wv2 = (WordVec*)wv2W;
// WordVecs with smaller size are smaller.
if (wv1->size < wv2->size) {
return -1;
}
if (wv1->size > wv2->size) {
return 1;
}
// Sizes are equal => order based on content.
for (i = 0; i < wv1->size; i++) {
if (wv1->words[i] == wv2->words[i])
continue;
if (wv1->words[i] < wv2->words[i])
return -1;
if (wv1->words[i] > wv2->words[i])
return 1;
tl_assert(0);
}
return 0; /* identical */
}
static void ensure_ix2vec_space ( WordSetU* wsu )
{
UInt i, new_sz;
WordVec** new_vec;
tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
if (wsu->ix2vec_used < wsu->ix2vec_size)
return;
new_sz = 2 * wsu->ix2vec_size;
if (new_sz == 0) new_sz = 1;
new_vec = wsu->alloc( wsu->cc, new_sz * sizeof(WordVec*) );
tl_assert(new_vec);
for (i = 0; i < wsu->ix2vec_size; i++)
new_vec[i] = wsu->ix2vec[i];
if (wsu->ix2vec)
wsu->dealloc(wsu->ix2vec);
wsu->ix2vec = new_vec;
wsu->ix2vec_size = new_sz;
}
/* True if wv is a dead entry (i.e. is in the linked list of free to be re-used
entries in ix2vec). */
static inline Bool is_dead ( WordSetU* wsu, WordVec* wv )
{
if (wv == NULL) /* last element in free linked list in ix2vec */
return True;
else
return (WordVec**)wv >= &(wsu->ix2vec[1])
&& (WordVec**)wv < &(wsu->ix2vec[wsu->ix2vec_size]);
}
/* Index into a WordSetU, doing the obvious range check. Failure of
the assertions marked XXX and YYY is an indication of passing the
wrong WordSetU* in the public API of this module.
Accessing a dead ws will assert. */
static WordVec* do_ix2vec ( WordSetU* wsu, WordSet ws )
{
WordVec* wv;
tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
if (wsu->ix2vec_used > 0)
tl_assert(wsu->ix2vec);
/* If this assertion fails, it may mean you supplied a 'ws'
that does not come from the 'wsu' universe. */
tl_assert(ws < wsu->ix2vec_used); /* XXX */
wv = wsu->ix2vec[ws];
/* Make absolutely sure that 'ws' is a non dead member of 'wsu'. */
tl_assert(wv);
tl_assert(!is_dead(wsu,wv));
tl_assert(wv->owner == wsu); /* YYY */
return wv;
}
/* Same as do_ix2vec but returns NULL for a dead ws. */
static WordVec* do_ix2vec_with_dead ( WordSetU* wsu, WordSet ws )
{
WordVec* wv;
tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
if (wsu->ix2vec_used > 0)
tl_assert(wsu->ix2vec);
/* If this assertion fails, it may mean you supplied a 'ws'
that does not come from the 'wsu' universe. */
tl_assert(ws < wsu->ix2vec_used); /* XXX */
wv = wsu->ix2vec[ws];
/* Make absolutely sure that 'ws' is either dead or a member of 'wsu'. */
if (is_dead(wsu,wv))
wv = NULL;
else
tl_assert(wv->owner == wsu); /* YYY */
return wv;
}
/* See if wv is contained within wsu. If so, deallocate wv and return
the index of the already-present copy. If not, add wv to both the
vec2ix and ix2vec mappings and return its index.
*/
static WordSet add_or_dealloc_WordVec( WordSetU* wsu, WordVec* wv_new )
{
Bool have;
WordVec* wv_old;
UWord/*Set*/ ix_old = -1;
/* Really WordSet, but need something that can safely be casted to
a Word* in the lookupFM. Making it WordSet (which is 32 bits)
causes failures on a 64-bit platform. */
tl_assert(wv_new->owner == wsu);
have = VG_(lookupFM)( wsu->vec2ix,
(UWord*)&wv_old, (UWord*)&ix_old,
(UWord)wv_new );
if (have) {
tl_assert(wv_old != wv_new);
tl_assert(wv_old);
tl_assert(wv_old->owner == wsu);
tl_assert(ix_old < wsu->ix2vec_used);
tl_assert(wsu->ix2vec[ix_old] == wv_old);
delete_WV( wv_new );
return (WordSet)ix_old;
} else if (wsu->ix2vec_free) {
WordSet ws;
tl_assert(is_dead(wsu,(WordVec*)wsu->ix2vec_free));
ws = wsu->ix2vec_free - &(wsu->ix2vec[0]);
tl_assert(wsu->ix2vec[ws] == NULL || is_dead(wsu,wsu->ix2vec[ws]));
wsu->ix2vec_free = (WordVec **) wsu->ix2vec[ws];
wsu->ix2vec[ws] = wv_new;
VG_(addToFM)( wsu->vec2ix, (UWord)wv_new, ws );
if (HG_DEBUG) VG_(printf)("aodW %s re-use free %d %p\n", wsu->cc, (Int)ws, wv_new );
return ws;
} else {
ensure_ix2vec_space( wsu );
tl_assert(wsu->ix2vec);
tl_assert(wsu->ix2vec_used < wsu->ix2vec_size);
wsu->ix2vec[wsu->ix2vec_used] = wv_new;
VG_(addToFM)( wsu->vec2ix, (Word)wv_new, (Word)wsu->ix2vec_used );
if (HG_DEBUG) VG_(printf)("aodW %s %d %p\n", wsu->cc, (Int)wsu->ix2vec_used, wv_new );
wsu->ix2vec_used++;
tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
return (WordSet)(wsu->ix2vec_used - 1);
}
}
WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( const HChar*, SizeT ),
const HChar* cc,
void (*dealloc)(void*),
Word cacheSize )
{
WordSetU* wsu;
WordVec* empty;
wsu = alloc_nofail( cc, sizeof(WordSetU) );
VG_(memset)( wsu, 0, sizeof(WordSetU) );
wsu->alloc = alloc_nofail;
wsu->cc = cc;
wsu->dealloc = dealloc;
wsu->vec2ix = VG_(newFM)( alloc_nofail, cc,
dealloc, cmp_WordVecs_for_FM );
wsu->ix2vec_used = 0;
wsu->ix2vec_size = 0;
wsu->ix2vec = NULL;
wsu->ix2vec_free = NULL;
WCache_INIT(wsu->cache_addTo, cacheSize);
WCache_INIT(wsu->cache_delFrom, cacheSize);
WCache_INIT(wsu->cache_intersect, cacheSize);
WCache_INIT(wsu->cache_minus, cacheSize);
empty = new_WV_of_size( wsu, 0 );
wsu->empty = add_or_dealloc_WordVec( wsu, empty );
return wsu;
}
void HG_(deleteWordSetU) ( WordSetU* wsu )
{
void (*dealloc)(void*) = wsu->dealloc;
tl_assert(wsu->vec2ix);
VG_(deleteFM)( wsu->vec2ix, delete_WV_for_FM, NULL/*val-finalizer*/ );
if (wsu->ix2vec)
dealloc(wsu->ix2vec);
dealloc(wsu);
}
WordSet HG_(emptyWS) ( WordSetU* wsu )
{
return wsu->empty;
}
Bool HG_(isEmptyWS) ( WordSetU* wsu, WordSet ws )
{
WordVec* wv = do_ix2vec( wsu, ws );
wsu->n_isEmpty++;
if (wv->size == 0) {
tl_assert(ws == wsu->empty);
return True;
} else {
tl_assert(ws != wsu->empty);
return False;
}
}
Bool HG_(isSingletonWS) ( WordSetU* wsu, WordSet ws, UWord w )
{
WordVec* wv;
tl_assert(wsu);
wsu->n_isSingleton++;
wv = do_ix2vec( wsu, ws );
return (Bool)(wv->size == 1 && wv->words[0] == w);
}
UWord HG_(cardinalityWS) ( WordSetU* wsu, WordSet ws )
{
WordVec* wv;
tl_assert(wsu);
wv = do_ix2vec( wsu, ws );
tl_assert(wv->size >= 0);
return wv->size;
}
UWord HG_(anyElementOfWS) ( WordSetU* wsu, WordSet ws )
{
WordVec* wv;
tl_assert(wsu);
wsu->n_anyElementOf++;
wv = do_ix2vec( wsu, ws );
tl_assert(wv->size >= 1);
return wv->words[0];
}
UWord HG_(cardinalityWSU) ( WordSetU* wsu )
{
tl_assert(wsu);
return wsu->ix2vec_used;
}
void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords,
WordSetU* wsu, WordSet ws )
{
WordVec* wv;
if (HG_DEBUG) VG_(printf)("getPayloadWS %s %d\n", wsu->cc, (Int)ws);
tl_assert(wsu);
wv = do_ix2vec( wsu, ws );
tl_assert(wv->size >= 0);
*nWords = wv->size;
*words = wv->words;
}
void HG_(dieWS) ( WordSetU* wsu, WordSet ws )
{
WordVec* wv = do_ix2vec_with_dead( wsu, ws );
WordVec* wv_in_vec2ix;
UWord/*Set*/ wv_ix = -1;
if (HG_DEBUG) VG_(printf)("dieWS %s %d %p\n", wsu->cc, (Int)ws, wv);
if (ws == 0)
return; // we never die the empty set.
if (!wv)
return; // already dead. (or a bug ?).
wsu->n_die++;
wsu->ix2vec[ws] = (WordVec*) wsu->ix2vec_free;
wsu->ix2vec_free = &wsu->ix2vec[ws];
VG_(delFromFM) ( wsu->vec2ix,
(UWord*)&wv_in_vec2ix, (UWord*)&wv_ix,
(UWord)wv );
if (HG_DEBUG) VG_(printf)("dieWS wv_ix %d\n", (Int)wv_ix);
tl_assert (wv_ix);
tl_assert (wv_ix == ws);
delete_WV( wv );
wsu->cache_addTo.inUse = 0;
wsu->cache_delFrom.inUse = 0;
wsu->cache_intersect.inUse = 0;
wsu->cache_minus.inUse = 0;
}
Bool HG_(plausibleWS) ( WordSetU* wsu, WordSet ws )
{
if (wsu == NULL) return False;
if (ws < 0 || ws >= wsu->ix2vec_used)
return False;
return True;
}
Bool HG_(saneWS_SLOW) ( WordSetU* wsu, WordSet ws )
{
WordVec* wv;
UWord i;
if (wsu == NULL) return False;
if (ws < 0 || ws >= wsu->ix2vec_used)
return False;
wv = do_ix2vec( wsu, ws );
/* can never happen .. do_ix2vec will assert instead. Oh well. */
if (wv->owner != wsu) return False;
if (wv->size < 0) return False;
if (wv->size > 0) {
for (i = 0; i < wv->size-1; i++) {
if (wv->words[i] >= wv->words[i+1])
return False;
}
}
return True;
}
Bool HG_(elemWS) ( WordSetU* wsu, WordSet ws, UWord w )
{
UWord i;
WordVec* wv = do_ix2vec( wsu, ws );
wsu->n_elem++;
for (i = 0; i < wv->size; i++) {
if (wv->words[i] == w)
return True;
}
return False;
}
WordSet HG_(doubletonWS) ( WordSetU* wsu, UWord w1, UWord w2 )
{
WordVec* wv;
wsu->n_doubleton++;
if (w1 == w2) {
wv = new_WV_of_size(wsu, 1);
wv->words[0] = w1;
}
else if (w1 < w2) {
wv = new_WV_of_size(wsu, 2);
wv->words[0] = w1;
wv->words[1] = w2;
}
else {
tl_assert(w1 > w2);
wv = new_WV_of_size(wsu, 2);
wv->words[0] = w2;
wv->words[1] = w1;
}
return add_or_dealloc_WordVec( wsu, wv );
}
WordSet HG_(singletonWS) ( WordSetU* wsu, UWord w )
{
return HG_(doubletonWS)( wsu, w, w );
}
WordSet HG_(isSubsetOf) ( WordSetU* wsu, WordSet small, WordSet big )
{
wsu->n_isSubsetOf++;
return small == HG_(intersectWS)( wsu, small, big );
}
void HG_(ppWS) ( WordSetU* wsu, WordSet ws )
{
UWord i;
WordVec* wv;
tl_assert(wsu);
wv = do_ix2vec( wsu, ws );
VG_(printf)("{");
for (i = 0; i < wv->size; i++) {
VG_(printf)("%p", (void*)wv->words[i]);
if (i < wv->size-1)
VG_(printf)(",");
}
VG_(printf)("}");
}
void HG_(ppWSUstats) ( WordSetU* wsu, const HChar* name )
{
VG_(printf)(" WordSet \"%s\":\n", name);
VG_(printf)(" addTo %10lu (%lu uncached)\n",
wsu->n_add, wsu->n_add_uncached);
VG_(printf)(" delFrom %10lu (%lu uncached)\n",
wsu->n_del, wsu->n_del_uncached);
VG_(printf)(" union %10lu\n", wsu->n_union);
VG_(printf)(" intersect %10lu (%lu uncached) "
"[nb. incl isSubsetOf]\n",
wsu->n_intersect, wsu->n_intersect_uncached);
VG_(printf)(" minus %10lu (%lu uncached)\n",
wsu->n_minus, wsu->n_minus_uncached);
VG_(printf)(" elem %10lu\n", wsu->n_elem);
VG_(printf)(" doubleton %10lu\n", wsu->n_doubleton);
VG_(printf)(" isEmpty %10lu\n", wsu->n_isEmpty);
VG_(printf)(" isSingleton %10lu\n", wsu->n_isSingleton);
VG_(printf)(" anyElementOf %10lu\n", wsu->n_anyElementOf);
VG_(printf)(" isSubsetOf %10lu\n", wsu->n_isSubsetOf);
VG_(printf)(" dieWS %10lu\n", wsu->n_die);
}
WordSet HG_(addToWS) ( WordSetU* wsu, WordSet ws, UWord w )
{
UWord k, j;
WordVec* wv_new;
WordVec* wv;
WordSet result = (WordSet)(-1); /* bogus */
wsu->n_add++;
WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_addTo, ws, w);
wsu->n_add_uncached++;
/* If already present, this is a no-op. */
wv = do_ix2vec( wsu, ws );
for (k = 0; k < wv->size; k++) {
if (wv->words[k] == w) {
result = ws;
goto out;
}
}
/* Ok, not present. Build a new one ... */
wv_new = new_WV_of_size( wsu, wv->size + 1 );
k = j = 0;
for (; k < wv->size && wv->words[k] < w; k++) {
wv_new->words[j++] = wv->words[k];
}
wv_new->words[j++] = w;
for (; k < wv->size; k++) {
tl_assert(wv->words[k] > w);
wv_new->words[j++] = wv->words[k];
}
tl_assert(j == wv_new->size);
/* Find any existing copy, or add the new one. */
result = add_or_dealloc_WordVec( wsu, wv_new );
tl_assert(result != (WordSet)(-1));
out:
WCache_UPDATE(wsu->cache_addTo, ws, w, result);
return result;
}
WordSet HG_(delFromWS) ( WordSetU* wsu, WordSet ws, UWord w )
{
UWord i, j, k;
WordVec* wv_new;
WordSet result = (WordSet)(-1); /* bogus */
WordVec* wv = do_ix2vec( wsu, ws );
wsu->n_del++;
/* special case empty set */
if (wv->size == 0) {
tl_assert(ws == wsu->empty);
return ws;
}
WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_delFrom, ws, w);
wsu->n_del_uncached++;
/* If not already present, this is a no-op. */
for (i = 0; i < wv->size; i++) {
if (wv->words[i] == w)
break;
}
if (i == wv->size) {
result = ws;
goto out;
}
/* So w is present in ws, and the new set will be one element
smaller. */
tl_assert(i >= 0 && i < wv->size);
tl_assert(wv->size > 0);
wv_new = new_WV_of_size( wsu, wv->size - 1 );
j = k = 0;
for (; j < wv->size; j++) {
if (j == i)
continue;
wv_new->words[k++] = wv->words[j];
}
tl_assert(k == wv_new->size);
result = add_or_dealloc_WordVec( wsu, wv_new );
if (wv->size == 1) {
tl_assert(result == wsu->empty);
}
out:
WCache_UPDATE(wsu->cache_delFrom, ws, w, result);
return result;
}
WordSet HG_(unionWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
{
UWord i1, i2, k, sz;
WordVec* wv_new;
WordVec* wv1 = do_ix2vec( wsu, ws1 );
WordVec* wv2 = do_ix2vec( wsu, ws2 );
wsu->n_union++;
sz = 0;
i1 = i2 = 0;
while (1) {
if (i1 >= wv1->size || i2 >= wv2->size)
break;
sz++;
if (wv1->words[i1] < wv2->words[i2]) {
i1++;
} else
if (wv1->words[i1] > wv2->words[i2]) {
i2++;
} else {
i1++;
i2++;
}
}
tl_assert(i1 <= wv1->size);
tl_assert(i2 <= wv2->size);
tl_assert(i1 == wv1->size || i2 == wv2->size);
if (i1 == wv1->size && i2 < wv2->size) {
sz += (wv2->size - i2);
}
if (i2 == wv2->size && i1 < wv1->size) {
sz += (wv1->size - i1);
}
wv_new = new_WV_of_size( wsu, sz );
k = 0;
i1 = i2 = 0;
while (1) {
if (i1 >= wv1->size || i2 >= wv2->size)
break;
if (wv1->words[i1] < wv2->words[i2]) {
wv_new->words[k++] = wv1->words[i1];
i1++;
} else
if (wv1->words[i1] > wv2->words[i2]) {
wv_new->words[k++] = wv2->words[i2];
i2++;
} else {
wv_new->words[k++] = wv1->words[i1];
i1++;
i2++;
}
}
tl_assert(i1 <= wv1->size);
tl_assert(i2 <= wv2->size);
tl_assert(i1 == wv1->size || i2 == wv2->size);
if (i1 == wv1->size && i2 < wv2->size) {
while (i2 < wv2->size)
wv_new->words[k++] = wv2->words[i2++];
}
if (i2 == wv2->size && i1 < wv1->size) {
while (i1 < wv1->size)
wv_new->words[k++] = wv1->words[i1++];
}
tl_assert(k == sz);
return add_or_dealloc_WordVec( wsu, wv_new );
}
WordSet HG_(intersectWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
{
UWord i1, i2, k, sz;
WordSet ws_new = (WordSet)(-1); /* bogus */
WordVec* wv_new;
WordVec* wv1;
WordVec* wv2;
wsu->n_intersect++;
/* Deal with an obvious case fast. */
if (ws1 == ws2)
return ws1;
/* Since intersect(x,y) == intersect(y,x), convert both variants to
the same query. This reduces the number of variants the cache
has to deal with. */
if (ws1 > ws2) {
WordSet wst = ws1; ws1 = ws2; ws2 = wst;
}
WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_intersect, ws1, ws2);
wsu->n_intersect_uncached++;
wv1 = do_ix2vec( wsu, ws1 );
wv2 = do_ix2vec( wsu, ws2 );
sz = 0;
i1 = i2 = 0;
while (1) {
if (i1 >= wv1->size || i2 >= wv2->size)
break;
if (wv1->words[i1] < wv2->words[i2]) {
i1++;
} else
if (wv1->words[i1] > wv2->words[i2]) {
i2++;
} else {
sz++;
i1++;
i2++;
}
}
tl_assert(i1 <= wv1->size);
tl_assert(i2 <= wv2->size);
tl_assert(i1 == wv1->size || i2 == wv2->size);
wv_new = new_WV_of_size( wsu, sz );
k = 0;
i1 = i2 = 0;
while (1) {
if (i1 >= wv1->size || i2 >= wv2->size)
break;
if (wv1->words[i1] < wv2->words[i2]) {
i1++;
} else
if (wv1->words[i1] > wv2->words[i2]) {
i2++;
} else {
wv_new->words[k++] = wv1->words[i1];
i1++;
i2++;
}
}
tl_assert(i1 <= wv1->size);
tl_assert(i2 <= wv2->size);
tl_assert(i1 == wv1->size || i2 == wv2->size);
tl_assert(k == sz);
ws_new = add_or_dealloc_WordVec( wsu, wv_new );
if (sz == 0) {
tl_assert(ws_new == wsu->empty);
}
tl_assert(ws_new != (WordSet)(-1));
WCache_UPDATE(wsu->cache_intersect, ws1, ws2, ws_new);
return ws_new;
}
WordSet HG_(minusWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
{
UWord i1, i2, k, sz;
WordSet ws_new = (WordSet)(-1); /* bogus */
WordVec* wv_new;
WordVec* wv1;
WordVec* wv2;
wsu->n_minus++;
WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_minus, ws1, ws2);
wsu->n_minus_uncached++;
wv1 = do_ix2vec( wsu, ws1 );
wv2 = do_ix2vec( wsu, ws2 );
sz = 0;
i1 = i2 = 0;
while (1) {
if (i1 >= wv1->size || i2 >= wv2->size)
break;
if (wv1->words[i1] < wv2->words[i2]) {
sz++;
i1++;
} else
if (wv1->words[i1] > wv2->words[i2]) {
i2++;
} else {
i1++;
i2++;
}
}
tl_assert(i1 <= wv1->size);
tl_assert(i2 <= wv2->size);
tl_assert(i1 == wv1->size || i2 == wv2->size);
if (i2 == wv2->size && i1 < wv1->size) {
sz += (wv1->size - i1);
}
wv_new = new_WV_of_size( wsu, sz );
k = 0;
i1 = i2 = 0;
while (1) {
if (i1 >= wv1->size || i2 >= wv2->size)
break;
if (wv1->words[i1] < wv2->words[i2]) {
wv_new->words[k++] = wv1->words[i1];
i1++;
} else
if (wv1->words[i1] > wv2->words[i2]) {
i2++;
} else {
i1++;
i2++;
}
}
tl_assert(i1 <= wv1->size);
tl_assert(i2 <= wv2->size);
tl_assert(i1 == wv1->size || i2 == wv2->size);
if (i2 == wv2->size && i1 < wv1->size) {
while (i1 < wv1->size)
wv_new->words[k++] = wv1->words[i1++];
}
tl_assert(k == sz);
ws_new = add_or_dealloc_WordVec( wsu, wv_new );
if (sz == 0) {
tl_assert(ws_new == wsu->empty);
}
tl_assert(ws_new != (WordSet)(-1));
WCache_UPDATE(wsu->cache_minus, ws1, ws2, ws_new);
return ws_new;
}
static __attribute__((unused))
void show_WS ( WordSetU* wsu, WordSet ws )
{
UWord i;
WordVec* wv = do_ix2vec( wsu, ws );
VG_(printf)("#%u{", ws);
for (i = 0; i < wv->size; i++) {
VG_(printf)("%lu", wv->words[i]);
if (i < wv->size-1)
VG_(printf)(",");
}
VG_(printf)("}\n");
}
//------------------------------------------------------------------//
//--- end WordSet ---//
//--- Implementation ---//
//------------------------------------------------------------------//
/*--------------------------------------------------------------------*/
/*--- end hg_wordset.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,126 @@
/*--------------------------------------------------------------------*/
/*--- Sets of words, with unique set identifiers. ---*/
/*--- hg_wordset.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Helgrind, a Valgrind tool for detecting errors
in threaded programs.
Copyright (C) 2007-2015 OpenWorks LLP
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#ifndef __HG_WORDSET_H
#define __HG_WORDSET_H
//------------------------------------------------------------------//
//--- WordSet ---//
//--- Public Interface ---//
//------------------------------------------------------------------//
typedef struct _WordSetU WordSetU; /* opaque */
typedef UInt WordSet; /* opaque, small int index */
/* Allocate and initialise a WordSetU */
WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( const HChar*, SizeT ),
const HChar* cc,
void (*dealloc)(void*),
Word cacheSize );
/* Free up the WordSetU. */
void HG_(deleteWordSetU) ( WordSetU* );
/* Get the number of elements in this WordSetU. Note that the dead
WordSet are included in the WordSetU number of elements. */
UWord HG_(cardinalityWSU) ( WordSetU* );
/* Show performance stats for this WordSetU. */
void HG_(ppWSUstats) ( WordSetU* wsu, const HChar* name );
/* Element-level operations on WordSets. Note that the WordSet
numbers given out are 0, 1, 2, 3, etc, and as it happens 0 always
represents the empty set. */
WordSet HG_(emptyWS) ( WordSetU* );
WordSet HG_(addToWS) ( WordSetU*, WordSet, UWord );
WordSet HG_(delFromWS) ( WordSetU*, WordSet, UWord );
WordSet HG_(unionWS) ( WordSetU*, WordSet, WordSet );
WordSet HG_(intersectWS) ( WordSetU*, WordSet, WordSet );
WordSet HG_(minusWS) ( WordSetU*, WordSet, WordSet );
Bool HG_(isEmptyWS) ( WordSetU*, WordSet );
Bool HG_(isSingletonWS) ( WordSetU*, WordSet, UWord );
UWord HG_(anyElementOfWS) ( WordSetU*, WordSet );
UWord HG_(cardinalityWS) ( WordSetU*, WordSet );
Bool HG_(elemWS) ( WordSetU*, WordSet, UWord );
WordSet HG_(doubletonWS) ( WordSetU*, UWord, UWord );
WordSet HG_(singletonWS) ( WordSetU*, UWord );
WordSet HG_(isSubsetOf) ( WordSetU*, WordSet, WordSet );
Bool HG_(plausibleWS) ( WordSetU*, WordSet );
Bool HG_(saneWS_SLOW) ( WordSetU*, WordSet );
void HG_(ppWS) ( WordSetU*, WordSet );
void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords,
WordSetU*, WordSet );
/* HG_(dieWS) indicates WordSet is not used/not referenced anymore,
and its memory can be reclaimed.
If ever a WordSet with the same content would be needed again,
a new WordSet will be reallocated.
BUG ALERT: !!! Using HG_(dieWS) on a WSU introduces a risk of
dangling references. Dangling references can be created by keeping
a ws after having marked it dead. This ws (just an index in
reality) will be re-cycled : a newly created wv can get the same
index. This implies that the wrong wv will be used if the
"old" ws has been kept.
Re-using a "dead" ws will be detected if the index has not been
re-cycled yet.
Another possibility of bug is to ask for the payload of a ws, and
then have this ws marked dead while the payload is still being
examined. This is a real dangling reference in free or re-allocated
memory. */
void HG_(dieWS) ( WordSetU*, WordSet );
//------------------------------------------------------------------//
//--- end WordSet ---//
//--- Public Interface ---//
//------------------------------------------------------------------//
#endif /* ! __HG_WORDSET_H */
/*--------------------------------------------------------------------*/
/*--- end hg_wordset.h ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,186 @@
/*--------------------------------------------------------------------*/
/*--- LibHB: a library for implementing and checking ---*/
/*--- the happens-before relationship in concurrent programs. ---*/
/*--- libhb_main.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of LibHB, a library for implementing and checking
the happens-before relationship in concurrent programs.
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __LIBHB_H
#define __LIBHB_H
/* Abstract to user: thread identifiers */
/* typedef struct _Thr Thr; */ /* now in hg_lock_n_thread.h */
/* Abstract to user: synchronisation objects */
/* typedef struct _SO SO; */ /* now in hg_lock_n_thread.h */
/* Initialise library; returns Thr* for root thread. 'shadow_alloc'
should never return NULL, instead it should simply not return if
they encounter an out-of-memory condition. */
Thr* libhb_init (
void (*get_stacktrace)( Thr*, Addr*, UWord ),
ExeContext* (*get_EC)( Thr* )
);
/* Shut down the library, and print stats (in fact that's _all_
this is for.) */
void libhb_shutdown ( Bool show_stats );
/* Thread creation: returns Thr* for new thread */
Thr* libhb_create ( Thr* parent );
/* Thread async exit */
void libhb_async_exit ( Thr* exitter );
void libhb_joinedwith_done ( Thr* exitter );
/* Synchronisation objects (abstract to caller) */
/* Allocate a new one (alloc'd by library) */
SO* libhb_so_alloc ( void );
/* Dealloc one */
void libhb_so_dealloc ( SO* so );
/* Send a message via a sync object. If strong_send is true, the
resulting inter-thread dependency seen by a future receiver of this
message will be a dependency on this thread only. That is, in a
strong send, the VC inside the SO is replaced by the clock of the
sending thread. For a weak send, the sender's VC is joined into
that already in the SO, if any. This subtlety is needed to model
rwlocks: a strong send corresponds to releasing a rwlock that had
been w-held (or releasing a standard mutex). A weak send
corresponds to releasing a rwlock that has been r-held.
(rationale): Since in general many threads may hold a rwlock in
r-mode, a weak send facility is necessary in order that the final
SO reflects the join of the VCs of all the threads releasing the
rwlock, rather than merely holding the VC of the most recent thread
to release it. */
void libhb_so_send ( Thr* thr, SO* so, Bool strong_send );
/* Recv a message from a sync object. If strong_recv is True, the
resulting inter-thread dependency is considered adequate to induce
a h-b ordering on both reads and writes. If it is False, the
implied h-b ordering exists only for reads, not writes. This is
subtlety is required in order to support reader-writer locks: a
thread doing a write-acquire of a rwlock (or acquiring a normal
mutex) models this by doing a strong receive. A thread doing a
read-acquire of a rwlock models this by doing a !strong_recv. */
void libhb_so_recv ( Thr* thr, SO* so, Bool strong_recv );
/* Has this SO ever been sent on? */
Bool libhb_so_everSent ( SO* so );
/* Memory accesses (1/2/4/8 byte size). They report a race if one is
found. */
#define LIBHB_CWRITE_1(_thr,_a) zsm_sapply08_f__msmcwrite((_thr),(_a))
#define LIBHB_CWRITE_2(_thr,_a) zsm_sapply16_f__msmcwrite((_thr),(_a))
#define LIBHB_CWRITE_4(_thr,_a) zsm_sapply32_f__msmcwrite((_thr),(_a))
#define LIBHB_CWRITE_8(_thr,_a) zsm_sapply64_f__msmcwrite((_thr),(_a))
#define LIBHB_CWRITE_N(_thr,_a,_n) zsm_sapplyNN_f__msmcwrite((_thr),(_a),(_n))
#define LIBHB_CREAD_1(_thr,_a) zsm_sapply08_f__msmcread((_thr),(_a))
#define LIBHB_CREAD_2(_thr,_a) zsm_sapply16_f__msmcread((_thr),(_a))
#define LIBHB_CREAD_4(_thr,_a) zsm_sapply32_f__msmcread((_thr),(_a))
#define LIBHB_CREAD_8(_thr,_a) zsm_sapply64_f__msmcread((_thr),(_a))
#define LIBHB_CREAD_N(_thr,_a,_n) zsm_sapplyNN_f__msmcread((_thr),(_a),(_n))
void zsm_sapply08_f__msmcwrite ( Thr* thr, Addr a );
void zsm_sapply16_f__msmcwrite ( Thr* thr, Addr a );
void zsm_sapply32_f__msmcwrite ( Thr* thr, Addr a );
void zsm_sapply64_f__msmcwrite ( Thr* thr, Addr a );
void zsm_sapplyNN_f__msmcwrite ( Thr* thr, Addr a, SizeT len );
void zsm_sapply08_f__msmcread ( Thr* thr, Addr a );
void zsm_sapply16_f__msmcread ( Thr* thr, Addr a );
void zsm_sapply32_f__msmcread ( Thr* thr, Addr a );
void zsm_sapply64_f__msmcread ( Thr* thr, Addr a );
void zsm_sapplyNN_f__msmcread ( Thr* thr, Addr a, SizeT len );
void libhb_Thr_resumes ( Thr* thr );
/* Set memory address ranges to new (freshly allocated), or noaccess
(no longer accessible). NB: "AHAE" == "Actually Has An Effect" :-) */
void libhb_srange_new ( Thr*, Addr, SizeT );
void libhb_srange_untrack ( Thr*, Addr, SizeT );
void libhb_srange_noaccess_NoFX ( Thr*, Addr, SizeT ); /* IS IGNORED */
void libhb_srange_noaccess_AHAE ( Thr*, Addr, SizeT ); /* IS NOT IGNORED */
/* Counts the nr of bytes addressable in the range [a, a+len[
(so a+len excluded) and returns the nr of addressable bytes found.
If abits /= NULL, abits must point to a block of memory of length len.
In this array, each addressable byte will be indicated with 0xff.
Non-addressable bytes are indicated with 0x00. */
UWord libhb_srange_get_abits (Addr a, /*OUT*/UChar *abits, SizeT len);
/* Get and set the hgthread (pointer to corresponding Thread
structure). */
Thread* libhb_get_Thr_hgthread ( Thr* );
void libhb_set_Thr_hgthread ( Thr*, Thread* );
/* Low level copy of shadow state from [src,src+len) to [dst,dst+len).
Overlapping moves are checked for and asserted against. */
void libhb_copy_shadow_state ( Thr* thr, Addr src, Addr dst, SizeT len );
/* Call this periodically to give libhb the opportunity to
garbage-collect its internal data structures. */
void libhb_maybe_GC ( void );
/* Extract info from the conflicting-access machinery. */
Bool libhb_event_map_lookup ( /*OUT*/ExeContext** resEC,
/*OUT*/Thr** resThr,
/*OUT*/SizeT* resSzB,
/*OUT*/Bool* resIsW,
/*OUT*/WordSetID* locksHeldW,
Thr* thr, Addr a, SizeT szB, Bool isW );
typedef void (*Access_t) (StackTrace ips, UInt n_ips,
Thr* Thr_a,
Addr ga,
SizeT SzB,
Bool isW,
WordSetID locksHeldW );
/* Call fn for each recorded access history that overlaps with range [a, a+szB[.
fn is first called for oldest access.*/
void libhb_event_map_access_history ( Addr a, SizeT szB, Access_t fn );
/* ------ Exported from hg_main.c ------ */
/* Yes, this is a horrible tangle. Sigh. */
/* Get the univ_lset (universe for locksets) from hg_main.c. Sigh. */
WordSetU* HG_(get_univ_lsets) ( void );
/* Get the the header pointer for the double linked list of locks
(admin_locks). */
Lock* HG_(get_admin_locks) ( void );
#endif /* __LIBHB_H */
/*--------------------------------------------------------------------*/
/*--- end libhb.h ---*/
/*--------------------------------------------------------------------*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,221 @@
include $(top_srcdir)/Makefile.tool-tests.am
dist_noinst_SCRIPTS = filter_stderr \
filter_stderr_solaris \
filter_helgrind \
filter_xml
EXTRA_DIST = \
annotate_hbefore.vgtest annotate_hbefore.stdout.exp \
annotate_hbefore.stderr.exp \
annotate_rwlock.vgtest annotate_rwlock.stdout.exp \
annotate_rwlock.stderr.exp \
annotate_smart_pointer.vgtest annotate_smart_pointer.stdout.exp \
annotate_smart_pointer.stderr.exp \
cond_init_destroy.vgtest cond_init_destroy.stderr.exp \
cond_timedwait_invalid.vgtest cond_timedwait_invalid.stdout.exp \
cond_timedwait_invalid.stderr.exp \
cond_timedwait_test.vgtest cond_timedwait_test.stdout.exp \
cond_timedwait_test.stderr.exp \
bar_bad.vgtest bar_bad.stdout.exp bar_bad.stderr.exp \
bar_trivial.vgtest bar_trivial.stdout.exp bar_trivial.stderr.exp \
free_is_write.vgtest free_is_write.stdout.exp \
free_is_write.stderr.exp \
hg01_all_ok.vgtest hg01_all_ok.stdout.exp hg01_all_ok.stderr.exp \
hg02_deadlock.vgtest hg02_deadlock.stdout.exp hg02_deadlock.stderr.exp \
hg03_inherit.vgtest hg03_inherit.stdout.exp hg03_inherit.stderr.exp \
hg04_race.vgtest hg04_race.stdout.exp hg04_race.stderr.exp \
hg05_race2.vgtest hg05_race2.stdout.exp hg05_race2.stderr.exp \
hg06_readshared.vgtest hg06_readshared.stdout.exp \
hg06_readshared.stderr.exp \
locked_vs_unlocked1_fwd.vgtest \
locked_vs_unlocked1_fwd.stderr.exp \
locked_vs_unlocked1_fwd.stdout.exp \
locked_vs_unlocked1_rev.vgtest \
locked_vs_unlocked1_rev.stderr.exp \
locked_vs_unlocked1_rev.stdout.exp \
locked_vs_unlocked2.vgtest \
locked_vs_unlocked2.stderr.exp \
locked_vs_unlocked2.stdout.exp \
locked_vs_unlocked3.vgtest \
locked_vs_unlocked3.stderr.exp \
locked_vs_unlocked3.stdout.exp \
pth_barrier1.vgtest pth_barrier1.stdout.exp pth_barrier1.stderr.exp \
pth_barrier2.vgtest pth_barrier2.stdout.exp pth_barrier2.stderr.exp \
pth_barrier3.vgtest pth_barrier3.stdout.exp pth_barrier3.stderr.exp \
pth_destroy_cond.vgtest \
pth_destroy_cond.stdout.exp pth_destroy_cond.stderr.exp \
pth_cond_destroy_busy.vgtest pth_cond_destroy_busy.stderr.exp \
pth_cond_destroy_busy.stderr.exp-ppc64 \
pth_cond_destroy_busy.stderr.exp-solaris \
pth_spinlock.vgtest pth_spinlock.stdout.exp pth_spinlock.stderr.exp \
rwlock_race.vgtest rwlock_race.stdout.exp rwlock_race.stderr.exp \
rwlock_test.vgtest rwlock_test.stdout.exp rwlock_test.stderr.exp \
shmem_abits.vgtest shmem_abits.stdout.exp shmem_abits.stderr.exp \
stackteardown.vgtest stackteardown.stdout.exp stackteardown.stderr.exp \
t2t_laog.vgtest t2t_laog.stdout.exp t2t_laog.stderr.exp \
tc01_simple_race.vgtest tc01_simple_race.stdout.exp \
tc01_simple_race.stderr.exp \
tc02_simple_tls.vgtest tc02_simple_tls.stdout.exp \
tc02_simple_tls.stderr.exp \
tc03_re_excl.vgtest tc03_re_excl.stdout.exp \
tc03_re_excl.stderr.exp \
tc04_free_lock.vgtest tc04_free_lock.stdout.exp \
tc04_free_lock.stderr.exp \
tc05_simple_race.vgtest tc05_simple_race.stdout.exp \
tc05_simple_race.stderr.exp \
tc06_two_races.vgtest tc06_two_races.stdout.exp \
tc06_two_races.stderr.exp \
tc06_two_races_xml.vgtest tc06_two_races_xml.stdout.exp \
tc06_two_races_xml.stderr.exp \
tc07_hbl1.vgtest tc07_hbl1.stdout.exp tc07_hbl1.stderr.exp \
tc08_hbl2.vgtest tc08_hbl2.stdout.exp tc08_hbl2.stderr.exp \
tc09_bad_unlock.vgtest tc09_bad_unlock.stdout.exp \
tc09_bad_unlock.stderr.exp tc09_bad_unlock.stderr.exp-solaris \
tc10_rec_lock.vgtest tc10_rec_lock.stdout.exp tc10_rec_lock.stderr.exp \
tc11_XCHG.vgtest tc11_XCHG.stdout.exp tc11_XCHG.stderr.exp \
tc12_rwl_trivial.vgtest tc12_rwl_trivial.stdout.exp \
tc12_rwl_trivial.stderr.exp \
tc12_rwl_trivial.stderr.exp-darwin970 \
tc12_rwl_trivial.stderr.exp-solaris \
tc13_laog1.vgtest tc13_laog1.stdout.exp tc13_laog1.stderr.exp \
tc14_laog_dinphils.vgtest tc14_laog_dinphils.stdout.exp \
tc14_laog_dinphils.stderr.exp \
tc15_laog_lockdel.vgtest tc15_laog_lockdel.stdout.exp \
tc15_laog_lockdel.stderr.exp \
tc16_byterace.vgtest tc16_byterace.stdout.exp \
tc16_byterace.stderr.exp \
tc17_sembar.vgtest tc17_sembar.stdout.exp \
tc17_sembar.stderr.exp \
tc18_semabuse.vgtest tc18_semabuse.stdout.exp \
tc18_semabuse.stderr.exp \
tc18_semabuse.stderr.exp-linux-mips32 \
tc18_semabuse.stderr.exp-linux-mips32-b \
tc18_semabuse.stderr.exp-solaris \
tc19_shadowmem.vgtest tc19_shadowmem.stdout.exp \
tc19_shadowmem.stderr.exp tc19_shadowmem.stderr.exp-mips32 \
tc20_verifywrap.vgtest tc20_verifywrap.stdout.exp \
tc20_verifywrap.stderr.exp \
tc20_verifywrap.stderr.exp-glibc-2.18 \
tc20_verifywrap.stderr.exp-glibc-2.21 \
tc20_verifywrap.stderr.exp-mips32 \
tc20_verifywrap.stderr.exp-mips32-b \
tc20_verifywrap.stderr.exp-s390x \
tc20_verifywrap.stderr.exp-solaris \
tc21_pthonce.vgtest tc21_pthonce.stdout.exp tc21_pthonce.stderr.exp \
tc22_exit_w_lock.vgtest tc22_exit_w_lock.stdout.exp \
tc22_exit_w_lock.stderr.exp \
tc22_exit_w_lock.stderr.exp-kfail-x86 \
tc22_exit_w_lock.stderr.exp-solaris \
tc23_bogus_condwait.vgtest tc23_bogus_condwait.stdout.exp \
tc23_bogus_condwait.stderr.exp \
tc23_bogus_condwait.stderr.exp-mips32 \
tc24_nonzero_sem.vgtest tc24_nonzero_sem.stdout.exp \
tc24_nonzero_sem.stderr.exp \
tls_threads.vgtest tls_threads.stdout.exp \
tls_threads.stderr.exp
# Wrapper headers used by some check programs.
noinst_HEADERS = safe-pthread.h safe-semaphore.h
# XXX: tc18_semabuse uses operations that are unsupported on Darwin. It
# should be conditionally compiled like tc20_verifywrap is.
check_PROGRAMS = \
annotate_hbefore \
cond_init_destroy \
cond_timedwait_invalid \
cond_timedwait_test \
free_is_write \
hg01_all_ok \
hg02_deadlock \
hg03_inherit \
hg04_race \
hg05_race2 \
hg06_readshared \
locked_vs_unlocked1 \
locked_vs_unlocked2 \
locked_vs_unlocked3 \
pth_destroy_cond \
shmem_abits \
stackteardown \
t2t \
tc01_simple_race \
tc02_simple_tls \
tc03_re_excl \
tc04_free_lock \
tc05_simple_race \
tc06_two_races \
tc07_hbl1 \
tc08_hbl2 \
tc09_bad_unlock \
tc10_rec_lock \
tc11_XCHG \
tc12_rwl_trivial \
tc13_laog1 \
tc14_laog_dinphils \
tc15_laog_lockdel \
tc16_byterace \
tc17_sembar \
tc18_semabuse \
tc19_shadowmem \
tc21_pthonce \
tc23_bogus_condwait \
tc24_nonzero_sem \
tls_threads
# DDD: it seg faults, and then the Valgrind exit path hangs
# JRS 29 July 09: it craps out in the stack unwinder, in
#==13480== at 0xF00B81FF: ??? f00b8180 VG_(get_StackTrace_wrk)
#==13480== by 0xF00B83F8: ??? f00b8340 VG_(get_StackTrace)
#==13480== by 0xF009FE19: ??? f009fd70 record_ExeContext_wrk
#==13480== by 0xF009D92E: ??? f009d8c0 construct_error
#==13480== by 0xF009F001: ??? f009eef0 VG_(maybe_record_error)
#==13480== by 0xF0081F80: ??? f0081f00 HG_(record_error_misc)
#==13480== by 0xF0089C00: ??? f0089b80 evh__pre_thread_ll_exit
#==13480== by 0xF01111D1: ??? f0111070 run_a_thread_NORETURN
#==13480== by 0xF0111512: ??? f0111500 start_thread_NORETURN
# when the thread being unwound is at __bsdthread_terminate+0
#
# Like Tom says, the stack unwinder protection is bollocks.
# We should junk all previous schemes and simply get the
# stack unwinder to consult aspacem at each frame (cache-accelerated,
# of course) to check each page it visits is accessible.
#
if ! VGCONF_PLATFORMS_INCLUDE_X86_DARWIN
check_PROGRAMS += \
tc22_exit_w_lock
endif
if VGCONF_PLATFORMS_INCLUDE_ARM_LINUX
annotate_hbefore_CFLAGS = $(AM_CFLAGS) -mcpu=cortex-a8
tc07_hbl1_CFLAGS = $(AM_CFLAGS) -mcpu=cortex-a8
tc08_hbl2_CFLAGS = $(AM_CFLAGS) -mcpu=cortex-a8
else
annotate_hbefore_CFLAGS = $(AM_CFLAGS)
tc07_hbl1_CFLAGS = $(AM_CFLAGS)
tc08_hbl2_CFLAGS = $(AM_CFLAGS)
endif
if HAVE_PTHREAD_BARRIER
check_PROGRAMS += bar_bad bar_trivial
endif
if HAVE_PTHREAD_MUTEX_TIMEDLOCK
check_PROGRAMS += tc20_verifywrap
endif
if HAVE_BUILTIN_ATOMIC
check_PROGRAMS += annotate_rwlock
endif
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
LDADD = -lpthread
if VGCONF_OS_IS_DARWIN
annotate_hbefore_CFLAGS = $(AM_CFLAGS) -mdynamic-no-pic
else
annotate_hbefore_CFLAGS = $(AM_CFLAGS)
endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,416 @@
/* Program which uses a happens-before edge to coordinate an access to
variable 'shared_var' between two threads. The h-b edge is created
by a custom (kludgesome!) mechanism and hence we need to use
ANNOTATES_HAPPEN_{BEFORE,AFTER} to explain to Helgrind what's going
on (else it reports a race). */
#include <pthread.h>
#include <stdio.h>
#include <assert.h>
#include "../../helgrind/helgrind.h"
/* Todo: move all this do_acasW guff into a support library. It's
useful for multiple tests, not just this one.
XXX: all the do_acasW routines assume the supplied address
is UWord (naturally) aligned. */
typedef unsigned long int UWord;
#if defined(VGA_ppc64be) || defined(VGA_ppc64le)
// ppc64
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord old, success;
/* Fetch the old value, and set the reservation */
__asm__ __volatile__ (
"ldarx %0, 0,%1" "\n" // rD,rA,rB
: /*out*/ "=b"(old)
: /*in*/ "b"(addr)
: /*trash*/ "memory","cc"
);
/* If the old value isn't as expected, we've had it */
if (old != expected) return 0;
/* otherwise try to stuff the new value in */
__asm__ __volatile__(
"stdcx. %2, 0,%1" "\n" // rS,rA,rB
"mfcr %0" "\n\t"
"srdi %0,%0,29" "\n\t"
"andi. %0,%0,1" "\n"
: /*out*/ "=b"(success)
: /*in*/ "b"(addr), "b"(nyu)
);
assert(success == 0 || success == 1);
return success;
}
#elif defined(VGA_ppc32)
// ppc32
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord old, success;
/* Fetch the old value, and set the reservation */
__asm__ __volatile__ (
"lwarx %0, 0,%1" "\n" // rD,rA,rB
: /*out*/ "=b"(old)
: /*in*/ "b"(addr)
: /*trash*/ "memory","cc"
);
/* If the old value isn't as expected, we've had it */
if (old != expected) return 0;
/* otherwise try to stuff the new value in */
__asm__ __volatile__(
"stwcx. %2, 0,%1" "\n" // rS,rA,rB
"mfcr %0" "\n\t"
"srwi %0,%0,29" "\n\t"
"andi. %0,%0,1" "\n"
: /*out*/ "=b"(success)
: /*in*/ "b"(addr), "b"(nyu)
);
assert(success == 0 || success == 1);
return success;
}
#elif defined(VGA_amd64)
// amd64
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord block[4] = { (UWord)addr, expected, nyu, 2 };
__asm__ __volatile__(
"movq 0(%%rsi), %%rdi" "\n\t" // addr
"movq 8(%%rsi), %%rax" "\n\t" // expected
"movq 16(%%rsi), %%rbx" "\n\t" // nyu
"xorq %%rcx,%%rcx" "\n\t"
"lock; cmpxchgq %%rbx,(%%rdi)" "\n\t"
"setz %%cl" "\n\t"
"movq %%rcx, 24(%%rsi)" "\n"
: /*out*/
: /*in*/ "S"(&block[0])
: /*trash*/"memory","cc","rdi","rax","rbx","rcx"
);
assert(block[3] == 0 || block[3] == 1);
return block[3] & 1;
}
#elif defined(VGA_x86)
// x86
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord block[4] = { (UWord)addr, expected, nyu, 2 };
__asm__ __volatile__(
"pushl %%ebx" "\n\t"
"movl 0(%%esi), %%edi" "\n\t" // addr
"movl 4(%%esi), %%eax" "\n\t" // expected
"movl 8(%%esi), %%ebx" "\n\t" // nyu
"xorl %%ecx,%%ecx" "\n\t"
"lock; cmpxchgl %%ebx,(%%edi)" "\n\t"
"setz %%cl" "\n\t"
"movl %%ecx, 12(%%esi)" "\n\t"
"popl %%ebx" "\n"
: /*out*/
: /*in*/ "S"(&block[0])
: /*trash*/"memory","cc","edi","eax","ecx"
);
assert(block[3] == 0 || block[3] == 1);
return block[3] & 1;
}
#elif defined(VGA_arm)
// arm
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord old, success;
UWord block[2] = { (UWord)addr, nyu };
/* Fetch the old value, and set the reservation */
__asm__ __volatile__ (
"ldrex %0, [%1]" "\n"
: /*out*/ "=r"(old)
: /*in*/ "r"(addr)
);
/* If the old value isn't as expected, we've had it */
if (old != expected) return 0;
/* otherwise try to stuff the new value in */
__asm__ __volatile__(
"ldr r4, [%1, #0]" "\n\t"
"ldr r5, [%1, #4]" "\n\t"
"strex r6, r5, [r4, #0]" "\n\t"
"eor %0, r6, #1" "\n\t"
: /*out*/ "=r"(success)
: /*in*/ "r"(&block[0])
: /*trash*/ "r4","r5","r6","memory"
);
assert(success == 0 || success == 1);
return success;
}
#elif defined(VGA_arm64)
// arm64
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord old, success;
UWord block[2] = { (UWord)addr, nyu };
/* Fetch the old value, and set the reservation */
__asm__ __volatile__ (
"ldxr %0, [%1]" "\n"
: /*out*/ "=r"(old)
: /*in*/ "r"(addr)
);
/* If the old value isn't as expected, we've had it */
if (old != expected) return 0;
/* otherwise try to stuff the new value in */
__asm__ __volatile__(
"ldr x4, [%1, #0]" "\n\t"
"ldr x5, [%1, #8]" "\n\t"
"stxr w6, x5, [x4, #0]" "\n\t"
"eor %0, x6, #1" "\n\t"
: /*out*/ "=r"(success)
: /*in*/ "r"(&block[0])
: /*trash*/ "x4","x5","x6","memory"
);
assert(success == 0 || success == 1);
return success;
}
#elif defined(VGA_s390x)
// s390x
/* return 1 if success, 0 if failure */
UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
{
int cc;
__asm__ __volatile__ (
"csg %2,%3,%1\n\t"
"ipm %0\n\t"
"srl %0,28\n\t"
: /* out */ "=r" (cc)
: /* in */ "Q" (*addr), "d" (expected), "d" (nyu)
: "memory", "cc"
);
return cc == 0;
}
#elif defined(VGA_mips32)
// mips32
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord success;
UWord block[3] = { (UWord)addr, nyu, expected};
__asm__ __volatile__(
".set noreorder" "\n\t"
"lw $t0, 0(%1)" "\n\t"
"lw $t2, 8(%1)" "\n\t"
"lw $t3, 4(%1)" "\n\t"
"ll $t1, 0($t0)" "\n\t"
"bne $t1, $t2, exit_0" "\n\t"
"nop" "\n\t"
"sc $t3, 0($t0)" "\n\t"
"move %0, $t3" "\n\t"
"b exit" "\n\t"
"nop" "\n\t"
"exit_0:" "\n\t"
"move %0, $zero" "\n\t"
"exit:" "\n\t"
: /*out*/ "=r"(success)
: /*in*/ "r"(&block[0])
: /*trash*/ "t0", "t1", "t2", "t3", "memory"
);
assert(success == 0 || success == 1);
return success;
}
#elif defined(VGA_mips64)
// mips64
/* return 1 if success, 0 if failure */
UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
{
UWord success;
UWord block[3] = { (UWord)addr, nyu, expected};
__asm__ __volatile__(
".set noreorder" "\n\t"
"ld $t0, 0(%1)" "\n\t"
"ld $t2, 16(%1)" "\n\t"
"ld $t3, 8(%1)" "\n\t"
"ll $t1, 0($t0)" "\n\t"
"bne $t1, $t2, exit_0" "\n\t"
"nop" "\n\t"
"sc $t3, 0($t0)" "\n\t"
"move %0, $t3" "\n\t"
"b exit" "\n\t"
"nop" "\n\t"
"exit_0:" "\n\t"
"move %0, $zero" "\n\t"
"exit:" "\n\t"
: /*out*/ "=r"(success)
: /*in*/ "r"(&block[0])
: /*trash*/ "t0", "t1", "t2", "t3", "memory"
);
assert(success == 0 || success == 1);
return success;
}
#elif defined(VGA_tilegx)
/* return 1 if success, 0 if failure */
UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
{
/* Load the compare value into special register 0x2780 */
__insn_mtspr(0x2780, expected);
return __insn_cmpexch(addr, nyu);
}
#endif
void atomic_incW ( UWord* w )
{
while (1) {
UWord old = *w;
UWord nyu = old + 1;
UWord ok = do_acasW( w, old, nyu );
if (ok) break;
};
}
#if 0
#define NNN 1000000
void* thread_fn ( void* arg )
{
UWord* w = (UWord*)arg;
int i;
for (i = 0; i < NNN; i++)
atomic_incW( w );
return NULL;
}
int main ( void )
{
int r;
//ANNOTATE_HAPPENS_BEFORE(0);
//return 0;
UWord w = 0;
pthread_t t1, t2;
r= pthread_create( &t1, NULL, &thread_fn, (void*)&w ); assert(!r);
r= pthread_create( &t2, NULL, &thread_fn, (void*)&w ); assert(!r);
r= pthread_join( t1, NULL ); assert(!r);
r= pthread_join( t2, NULL ); assert(!r);
printf("result = %lu\n", w );
return 0;
}
#endif
int shared_var = 0; // is not raced upon
void delayXms ( int i )
{
struct timespec ts = { 0, 1 * 1000 * 1000 };
// We do the sleep in small pieces to have scheduling
// events ensuring a fair switch between threads, even
// without --fair-sched=yes. This is a.o. needed for
// running this test under an outer helgrind or an outer
// sgcheck.
while (i > 0) {
nanosleep(&ts, NULL);
i--;
}
}
void do_wait ( UWord* w )
{
UWord w0 = *w;
UWord volatile * wV = w;
while (*wV == w0)
delayXms(1); // small sleeps, ensuring context switches
ANNOTATE_HAPPENS_AFTER(w);
}
void do_signal ( UWord* w )
{
ANNOTATE_HAPPENS_BEFORE(w);
atomic_incW(w);
}
void* thread_fn1 ( void* arg )
{
UWord* w = (UWord*)arg;
delayXms(500); // ensure t2 gets to its wait first
shared_var = 1; // first access
do_signal(w); // cause h-b edge to second thread
delayXms(500);
return NULL;
}
void* thread_fn2 ( void* arg )
{
UWord* w = (UWord*)arg;
do_wait(w); // wait for h-b edge from first thread
shared_var = 2; // second access
delayXms(500);
return NULL;
}
int main ( void )
{
int r;
UWord w = 0;
pthread_t t1, t2;
r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w ); assert(!r);
r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w ); assert(!r);
r= pthread_join( t1, NULL ); assert(!r);
r= pthread_join( t2, NULL ); assert(!r);
return 0;
}
@@ -0,0 +1,2 @@
vgopts: -q --fair-sched=try
prog: annotate_hbefore
@@ -0,0 +1,176 @@
/* This program is a marginally modified copy of
drd/tests/annotate_rwlock.c,
which was originally written by Bart van Assche.
Unfortunately due to the need to #include helgrind.h instead of
drd.h, it can't be an exact copy.
*/
/**
* @file annotate_rwlock.c
*
* @brief Multithreaded test program that triggers various access patterns
* without triggering any race conditions using a reader-writer lock
* implemented via busy-waiting. Annotations are used to tell DRD
* which higher-level rwlock operations are being performed.
*/
#define _GNU_SOURCE 1
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h> /* usleep() */
#include "../../config.h"
#include "../../helgrind/helgrind.h"
#ifndef HAVE_BUILTIN_ATOMIC
#error Sorry, but this test program can only be compiled by a compiler that\
has built-in functions for atomic memory access.
#endif
typedef struct {
volatile int locked;
int writer_count;
int reader_count;
} rwlock_t;
static rwlock_t s_rwlock;
static int s_counter;
static void rwlock_init(rwlock_t* p)
{
// DRD_IGNORE_VAR(*p);
p->locked = 0;
p->writer_count = 0;
p->reader_count = 0;
ANNOTATE_RWLOCK_CREATE(p);
}
static void rwlock_destroy(rwlock_t* p)
{
ANNOTATE_RWLOCK_DESTROY(p);
assert(p->locked == 0);
assert(p->writer_count == 0);
assert(p->reader_count == 0);
}
static void rwlock_rdlock(rwlock_t* p)
{
while (1)
{
while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
;
if (p->writer_count == 0)
break;
#ifdef __APPLE__
/* Darwin doesn't have an implementation of pthread_yield(). */
usleep(100 * 1000);
#else
sched_yield();
#endif
(void) __sync_fetch_and_sub(&p->locked, 1);
}
p->reader_count++;
assert(p->reader_count >= 0);
assert(p->writer_count >= 0);
assert(p->reader_count == 0 || p->writer_count == 0);
(void) __sync_fetch_and_sub(&p->locked, 1);
//ANNOTATE_READERLOCK_ACQUIRED(p);
ANNOTATE_RWLOCK_ACQUIRED(p, 0);
}
static void rwlock_wrlock(rwlock_t* p)
{
while (1)
{
while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
;
if (p->reader_count == 0)
break;
#ifdef __APPLE__
/* Darwin doesn't have an implementation of pthread_yield(). */
usleep(100 * 1000);
#else
sched_yield();
#endif
(void) __sync_fetch_and_sub(&p->locked, 1);
}
p->writer_count++;
assert(p->reader_count >= 0);
assert(p->writer_count >= 0);
assert(p->reader_count == 0 || p->writer_count == 0);
(void) __sync_fetch_and_sub(&p->locked, 1);
// ANNOTATE_WRITERLOCK_ACQUIRED(p);
ANNOTATE_RWLOCK_ACQUIRED(p, 1);
}
static void rwlock_unlock(rwlock_t* p)
{
while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
;
if (p->reader_count > 0)
{
p->reader_count--;
//ANNOTATE_READERLOCK_RELEASED(p);
ANNOTATE_RWLOCK_RELEASED(p, 0);
}
else
{
p->writer_count--;
//ANNOTATE_WRITERLOCK_RELEASED(p);
ANNOTATE_RWLOCK_RELEASED(p, 1);
}
assert(p->reader_count >= 0);
assert(p->writer_count >= 0);
assert(p->reader_count == 0 || p->writer_count == 0);
(void) __sync_fetch_and_sub(&p->locked, 1);
}
static void* thread_func(void* arg)
{
int i;
int sum = 0;
for (i = 0; i < 1000; i++)
{
rwlock_rdlock(&s_rwlock);
sum += s_counter;
rwlock_unlock(&s_rwlock);
rwlock_wrlock(&s_rwlock);
s_counter++;
rwlock_unlock(&s_rwlock);
}
return 0;
}
int main(int argc, char** argv)
{
const int thread_count = 10;
pthread_t tid[thread_count];
int i;
rwlock_init(&s_rwlock);
for (i = 0; i < thread_count; i++)
{
pthread_create(&tid[i], 0, thread_func, 0);
}
for (i = 0; i < thread_count; i++)
{
pthread_join(tid[i], 0);
}
rwlock_destroy(&s_rwlock);
fprintf(stderr, "Finished.\n");
return 0;
}
@@ -0,0 +1,122 @@
---Thread-Announcement------------------------------------------
Thread #x was created
...
by 0x........: pthread_create@* (hg_intercepts.c:...)
by 0x........: main (annotate_rwlock.c:164)
---Thread-Announcement------------------------------------------
Thread #x was created
...
by 0x........: pthread_create@* (hg_intercepts.c:...)
by 0x........: main (annotate_rwlock.c:164)
----------------------------------------------------------------
Possible data race during read of size 4 at 0x........ by thread #x
Locks held: none
at 0x........: rwlock_rdlock (annotate_rwlock.c:71)
by 0x........: thread_func (annotate_rwlock.c:144)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
This conflicts with a previous write of size 4 by thread #x
Locks held: none
at 0x........: rwlock_wrlock (annotate_rwlock.c:106)
by 0x........: thread_func (annotate_rwlock.c:147)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
Address 0x........ is 4 bytes inside data symbol "s_rwlock"
----------------------------------------------------------------
Possible data race during read of size 4 at 0x........ by thread #x
Locks held: none
at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
by 0x........: thread_func (annotate_rwlock.c:144)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
This conflicts with a previous write of size 4 by thread #x
Locks held: none
at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
by 0x........: thread_func (annotate_rwlock.c:144)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
Address 0x........ is 8 bytes inside data symbol "s_rwlock"
----------------------------------------------------------------
Possible data race during write of size 4 at 0x........ by thread #x
Locks held: none
at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
by 0x........: thread_func (annotate_rwlock.c:144)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
This conflicts with a previous read of size 4 by thread #x
Locks held: none
at 0x........: rwlock_unlock (annotate_rwlock.c:131)
by 0x........: thread_func (annotate_rwlock.c:149)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
Address 0x........ is 8 bytes inside data symbol "s_rwlock"
----------------------------------------------------------------
Possible data race during write of size 4 at 0x........ by thread #x
Locks held: none
at 0x........: rwlock_unlock (annotate_rwlock.c:121)
by 0x........: thread_func (annotate_rwlock.c:146)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
This conflicts with a previous read of size 4 by thread #x
Locks held: none
at 0x........: rwlock_unlock (annotate_rwlock.c:131)
by 0x........: thread_func (annotate_rwlock.c:149)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
Address 0x........ is 8 bytes inside data symbol "s_rwlock"
----------------------------------------------------------------
Possible data race during write of size 4 at 0x........ by thread #x
Locks held: none
at 0x........: rwlock_wrlock (annotate_rwlock.c:106)
by 0x........: thread_func (annotate_rwlock.c:147)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
This conflicts with a previous read of size 4 by thread #x
Locks held: none
at 0x........: rwlock_unlock (annotate_rwlock.c:132)
by 0x........: thread_func (annotate_rwlock.c:149)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
Address 0x........ is 4 bytes inside data symbol "s_rwlock"
----------------------------------------------------------------
Lock at 0x........ was first observed
at 0x........: rwlock_init (annotate_rwlock.c:54)
by 0x........: main (annotate_rwlock.c:161)
Address 0x........ is 0 bytes inside data symbol "s_rwlock"
Possible data race during write of size 4 at 0x........ by thread #x
Locks held: 1, at address 0x........
at 0x........: rwlock_unlock (annotate_rwlock.c:127)
by 0x........: thread_func (annotate_rwlock.c:149)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
This conflicts with a previous read of size 4 by thread #x
Locks held: none
at 0x........: rwlock_unlock (annotate_rwlock.c:132)
by 0x........: thread_func (annotate_rwlock.c:149)
by 0x........: mythread_wrapper (hg_intercepts.c:...)
...
Address 0x........ is 4 bytes inside data symbol "s_rwlock"
Finished.

Some files were not shown because too many files have changed in this diff Show More