2013-05-16 03:16:45

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 0/9] liblockdep: userspace lockdep

liblockdep is a tiny wrapper built around kernel/lockdep.c. The aim is to
provide the same functionality the kernel gets from lockdep to userspace.

The bulk of the code here is the LD_PRELOAD support which provides users
an easy way to test their code without having to integrate liblockdep into
said code. Simply doing:

lockdep my_app

Would provide lockdep support to my_app.

There is also a small test suite to test both mutexes and rwlocks, it's
based on the tests in lib/locking-selftest.c.

This entire patch series was reviewed by lockdep maintainers and accepted to
the tip tree previously. It was pulled out so that the potential merge of
liblockdep won't delay the rest of the commits in the tip locking tree.

For some more background about this entire thing, the folks at LWN did
an awesome overview: http://lwn.net/Articles/536363/


Changes from v3:
- More fixes/suggestions/code from Peter.

Changes from v2:
- Fix possible build breakage due to recent changes to kernel/lockdep.c

Changes from v1:
- Addressed Peter's comments.

Sasha Levin (9):
lockdep: Be nice about building from userspace
liblockdep: Wrap kernel/lockdep.c to allow usage from userspace
liblockdep: Add public headers for pthread_mutex_t implementation
liblockdep: Add pthread_mutex_t test suite
liblockdep: Add public headers for pthread_rwlock_t implementation
liblockdep: Add pthread_rwlock_t test suite
liblockdep: Support using LD_PRELOAD
liblockdep: Add the 'lockdep' user-space utility
liblockdep: Add a MAINTAINERS entry

MAINTAINERS | 5 +
kernel/lockdep.c | 4 +
tools/lib/lockdep/Makefile | 251 ++++++++++++
tools/lib/lockdep/common.c | 33 ++
tools/lib/lockdep/include/liblockdep/common.h | 42 ++
tools/lib/lockdep/include/liblockdep/mutex.h | 70 ++++
tools/lib/lockdep/include/liblockdep/rwlock.h | 86 ++++
tools/lib/lockdep/lockdep | 3 +
tools/lib/lockdep/lockdep.c | 2 +
tools/lib/lockdep/lockdep_internals.h | 1 +
tools/lib/lockdep/lockdep_states.h | 1 +
tools/lib/lockdep/preload.c | 447 +++++++++++++++++++++
tools/lib/lockdep/rbtree.c | 1 +
tools/lib/lockdep/run_tests.sh | 27 ++
tools/lib/lockdep/tests/AA.c | 13 +
tools/lib/lockdep/tests/ABBA.c | 13 +
tools/lib/lockdep/tests/ABBCCA.c | 15 +
tools/lib/lockdep/tests/ABBCCDDA.c | 17 +
tools/lib/lockdep/tests/ABCABC.c | 15 +
tools/lib/lockdep/tests/ABCDBCDA.c | 17 +
tools/lib/lockdep/tests/ABCDBDDA.c | 17 +
tools/lib/lockdep/tests/WW.c | 13 +
tools/lib/lockdep/tests/common.h | 12 +
tools/lib/lockdep/tests/unlock_balance.c | 12 +
tools/lib/lockdep/uinclude/asm/hweight.h | 0
tools/lib/lockdep/uinclude/asm/sections.h | 0
tools/lib/lockdep/uinclude/linux/bitops.h | 0
tools/lib/lockdep/uinclude/linux/compiler.h | 7 +
tools/lib/lockdep/uinclude/linux/debug_locks.h | 12 +
tools/lib/lockdep/uinclude/linux/delay.h | 0
tools/lib/lockdep/uinclude/linux/export.h | 7 +
tools/lib/lockdep/uinclude/linux/ftrace.h | 0
tools/lib/lockdep/uinclude/linux/gfp.h | 0
tools/lib/lockdep/uinclude/linux/hardirq.h | 11 +
tools/lib/lockdep/uinclude/linux/hash.h | 1 +
tools/lib/lockdep/uinclude/linux/interrupt.h | 0
tools/lib/lockdep/uinclude/linux/irqflags.h | 38 ++
tools/lib/lockdep/uinclude/linux/kallsyms.h | 32 ++
tools/lib/lockdep/uinclude/linux/kern_levels.h | 25 ++
tools/lib/lockdep/uinclude/linux/kernel.h | 37 ++
tools/lib/lockdep/uinclude/linux/kmemcheck.h | 8 +
tools/lib/lockdep/uinclude/linux/linkage.h | 0
tools/lib/lockdep/uinclude/linux/list.h | 1 +
tools/lib/lockdep/uinclude/linux/lockdep.h | 55 +++
tools/lib/lockdep/uinclude/linux/mm_types.h | 0
tools/lib/lockdep/uinclude/linux/module.h | 6 +
tools/lib/lockdep/uinclude/linux/mutex.h | 0
tools/lib/lockdep/uinclude/linux/poison.h | 1 +
tools/lib/lockdep/uinclude/linux/prefetch.h | 6 +
tools/lib/lockdep/uinclude/linux/proc_fs.h | 0
tools/lib/lockdep/uinclude/linux/rbtree.h | 1 +
.../lib/lockdep/uinclude/linux/rbtree_augmented.h | 2 +
tools/lib/lockdep/uinclude/linux/rcu.h | 16 +
tools/lib/lockdep/uinclude/linux/seq_file.h | 0
tools/lib/lockdep/uinclude/linux/spinlock.h | 25 ++
tools/lib/lockdep/uinclude/linux/stacktrace.h | 32 ++
tools/lib/lockdep/uinclude/linux/stringify.h | 7 +
tools/lib/lockdep/uinclude/linux/system.h | 0
tools/lib/lockdep/uinclude/linux/types.h | 58 +++
tools/lib/lockdep/uinclude/linux/util.h | 0
tools/lib/lockdep/uinclude/trace/events/lock.h | 0
61 files changed, 1505 insertions(+)
create mode 100644 tools/lib/lockdep/Makefile
create mode 100644 tools/lib/lockdep/common.c
create mode 100644 tools/lib/lockdep/include/liblockdep/common.h
create mode 100644 tools/lib/lockdep/include/liblockdep/mutex.h
create mode 100644 tools/lib/lockdep/include/liblockdep/rwlock.h
create mode 100755 tools/lib/lockdep/lockdep
create mode 100644 tools/lib/lockdep/lockdep.c
create mode 100644 tools/lib/lockdep/lockdep_internals.h
create mode 100644 tools/lib/lockdep/lockdep_states.h
create mode 100644 tools/lib/lockdep/preload.c
create mode 100644 tools/lib/lockdep/rbtree.c
create mode 100755 tools/lib/lockdep/run_tests.sh
create mode 100644 tools/lib/lockdep/tests/AA.c
create mode 100644 tools/lib/lockdep/tests/ABBA.c
create mode 100644 tools/lib/lockdep/tests/ABBCCA.c
create mode 100644 tools/lib/lockdep/tests/ABBCCDDA.c
create mode 100644 tools/lib/lockdep/tests/ABCABC.c
create mode 100644 tools/lib/lockdep/tests/ABCDBCDA.c
create mode 100644 tools/lib/lockdep/tests/ABCDBDDA.c
create mode 100644 tools/lib/lockdep/tests/WW.c
create mode 100644 tools/lib/lockdep/tests/common.h
create mode 100644 tools/lib/lockdep/tests/unlock_balance.c
create mode 100644 tools/lib/lockdep/uinclude/asm/hweight.h
create mode 100644 tools/lib/lockdep/uinclude/asm/sections.h
create mode 100644 tools/lib/lockdep/uinclude/linux/bitops.h
create mode 100644 tools/lib/lockdep/uinclude/linux/compiler.h
create mode 100644 tools/lib/lockdep/uinclude/linux/debug_locks.h
create mode 100644 tools/lib/lockdep/uinclude/linux/delay.h
create mode 100644 tools/lib/lockdep/uinclude/linux/export.h
create mode 100644 tools/lib/lockdep/uinclude/linux/ftrace.h
create mode 100644 tools/lib/lockdep/uinclude/linux/gfp.h
create mode 100644 tools/lib/lockdep/uinclude/linux/hardirq.h
create mode 100644 tools/lib/lockdep/uinclude/linux/hash.h
create mode 100644 tools/lib/lockdep/uinclude/linux/interrupt.h
create mode 100644 tools/lib/lockdep/uinclude/linux/irqflags.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kallsyms.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kern_levels.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kernel.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kmemcheck.h
create mode 100644 tools/lib/lockdep/uinclude/linux/linkage.h
create mode 100644 tools/lib/lockdep/uinclude/linux/list.h
create mode 100644 tools/lib/lockdep/uinclude/linux/lockdep.h
create mode 100644 tools/lib/lockdep/uinclude/linux/mm_types.h
create mode 100644 tools/lib/lockdep/uinclude/linux/module.h
create mode 100644 tools/lib/lockdep/uinclude/linux/mutex.h
create mode 100644 tools/lib/lockdep/uinclude/linux/poison.h
create mode 100644 tools/lib/lockdep/uinclude/linux/prefetch.h
create mode 100644 tools/lib/lockdep/uinclude/linux/proc_fs.h
create mode 100644 tools/lib/lockdep/uinclude/linux/rbtree.h
create mode 100644 tools/lib/lockdep/uinclude/linux/rbtree_augmented.h
create mode 100644 tools/lib/lockdep/uinclude/linux/rcu.h
create mode 100644 tools/lib/lockdep/uinclude/linux/seq_file.h
create mode 100644 tools/lib/lockdep/uinclude/linux/spinlock.h
create mode 100644 tools/lib/lockdep/uinclude/linux/stacktrace.h
create mode 100644 tools/lib/lockdep/uinclude/linux/stringify.h
create mode 100644 tools/lib/lockdep/uinclude/linux/system.h
create mode 100644 tools/lib/lockdep/uinclude/linux/types.h
create mode 100644 tools/lib/lockdep/uinclude/linux/util.h
create mode 100644 tools/lib/lockdep/uinclude/trace/events/lock.h

--
1.8.2.1


2013-05-16 03:16:34

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 1/9] lockdep: Be nice about building from userspace

Lockdep is an awesome piece of code which detects locking issues
which are relevant both to userspace and kernelspace. We can
easily make lockdep work in userspace since there is really no
kernel spacific magic going on in the code.

All we need is to wrap two functions which are used by lockdep
and are very kernel specific.

Doing that will allow tools located in tools/ to easily utilize
lockdep's code for their own use.

Signed-off-by: Sasha Levin <[email protected]>
Cc: [email protected]
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Ingo Molnar <[email protected]>
---
kernel/lockdep.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index 1f3186b..edb7f7e 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -590,6 +590,7 @@ static int very_verbose(struct lock_class *class)
/*
* Is this the address of a static object:
*/
+#ifdef __KERNEL__
static int static_obj(void *obj)
{
unsigned long start = (unsigned long) &_stext,
@@ -616,6 +617,7 @@ static int static_obj(void *obj)
*/
return is_module_address(addr) || is_module_percpu_address(addr);
}
+#endif

/*
* To make lock name printouts unique, we calculate a unique
@@ -4116,6 +4118,7 @@ void debug_check_no_locks_held(struct task_struct *task)
print_held_locks_bug(task);
}

+#ifdef __KERNEL__
void debug_show_all_locks(void)
{
struct task_struct *g, *p;
@@ -4173,6 +4176,7 @@ retry:
read_unlock(&tasklist_lock);
}
EXPORT_SYMBOL_GPL(debug_show_all_locks);
+#endif

/*
* Careful: only use this function if you are sure that
--
1.8.2.1

2013-05-16 03:16:40

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 6/9] liblockdep: Add pthread_rwlock_t test suite

A simple test to make sure we handle rwlocks correctly.

Signed-off-by: Sasha Levin <[email protected]>
---
tools/lib/lockdep/tests/WW.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 tools/lib/lockdep/tests/WW.c

diff --git a/tools/lib/lockdep/tests/WW.c b/tools/lib/lockdep/tests/WW.c
new file mode 100644
index 0000000..d44f77d
--- /dev/null
+++ b/tools/lib/lockdep/tests/WW.c
@@ -0,0 +1,13 @@
+#include <liblockdep/rwlock.h>
+
+void main(void)
+{
+ pthread_rwlock_t a, b;
+
+ pthread_rwlock_init(&a, NULL);
+ pthread_rwlock_init(&b, NULL);
+
+ pthread_rwlock_wrlock(&a);
+ pthread_rwlock_rdlock(&b);
+ pthread_rwlock_wrlock(&a);
+}
--
1.8.2.1

2013-05-16 03:16:53

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 2/9] liblockdep: Wrap kernel/lockdep.c to allow usage from userspace

kernel/lockdep.c deals with validating locking scenarios for
various architectures supported by the kernel. There isn't
anything kernel specific going on in lockdep, and when we
compare userspace to other architectures that don't have to deal
with irqs such as s390, they become all too similar.

We wrap kernel/lockdep.c and include/linux/lockdep.h with
several headers which allow us to build and use lockdep from
userspace. We don't touch the kernel code itself which means
that any work done on lockdep in the kernel will automatically
benefit userspace lockdep as well!

Signed-off-by: Sasha Levin <[email protected]>
---
tools/lib/lockdep/Makefile | 251 +++++++++++++++++++++
tools/lib/lockdep/common.c | 33 +++
tools/lib/lockdep/lockdep.c | 2 +
tools/lib/lockdep/lockdep_internals.h | 1 +
tools/lib/lockdep/lockdep_states.h | 1 +
tools/lib/lockdep/rbtree.c | 1 +
tools/lib/lockdep/uinclude/asm/hweight.h | 0
tools/lib/lockdep/uinclude/asm/sections.h | 0
tools/lib/lockdep/uinclude/linux/bitops.h | 0
tools/lib/lockdep/uinclude/linux/compiler.h | 7 +
tools/lib/lockdep/uinclude/linux/debug_locks.h | 12 +
tools/lib/lockdep/uinclude/linux/delay.h | 0
tools/lib/lockdep/uinclude/linux/export.h | 7 +
tools/lib/lockdep/uinclude/linux/ftrace.h | 0
tools/lib/lockdep/uinclude/linux/gfp.h | 0
tools/lib/lockdep/uinclude/linux/hardirq.h | 11 +
tools/lib/lockdep/uinclude/linux/hash.h | 1 +
tools/lib/lockdep/uinclude/linux/interrupt.h | 0
tools/lib/lockdep/uinclude/linux/irqflags.h | 38 ++++
tools/lib/lockdep/uinclude/linux/kallsyms.h | 32 +++
tools/lib/lockdep/uinclude/linux/kern_levels.h | 25 ++
tools/lib/lockdep/uinclude/linux/kernel.h | 37 +++
tools/lib/lockdep/uinclude/linux/kmemcheck.h | 8 +
tools/lib/lockdep/uinclude/linux/linkage.h | 0
tools/lib/lockdep/uinclude/linux/list.h | 1 +
tools/lib/lockdep/uinclude/linux/lockdep.h | 55 +++++
tools/lib/lockdep/uinclude/linux/mm_types.h | 0
tools/lib/lockdep/uinclude/linux/module.h | 6 +
tools/lib/lockdep/uinclude/linux/mutex.h | 0
tools/lib/lockdep/uinclude/linux/poison.h | 1 +
tools/lib/lockdep/uinclude/linux/prefetch.h | 6 +
tools/lib/lockdep/uinclude/linux/proc_fs.h | 0
tools/lib/lockdep/uinclude/linux/rbtree.h | 1 +
.../lib/lockdep/uinclude/linux/rbtree_augmented.h | 2 +
tools/lib/lockdep/uinclude/linux/rcu.h | 16 ++
tools/lib/lockdep/uinclude/linux/seq_file.h | 0
tools/lib/lockdep/uinclude/linux/spinlock.h | 25 ++
tools/lib/lockdep/uinclude/linux/stacktrace.h | 32 +++
tools/lib/lockdep/uinclude/linux/stringify.h | 7 +
tools/lib/lockdep/uinclude/linux/system.h | 0
tools/lib/lockdep/uinclude/linux/types.h | 58 +++++
tools/lib/lockdep/uinclude/linux/util.h | 0
tools/lib/lockdep/uinclude/trace/events/lock.h | 0
43 files changed, 677 insertions(+)
create mode 100644 tools/lib/lockdep/Makefile
create mode 100644 tools/lib/lockdep/common.c
create mode 100644 tools/lib/lockdep/lockdep.c
create mode 100644 tools/lib/lockdep/lockdep_internals.h
create mode 100644 tools/lib/lockdep/lockdep_states.h
create mode 100644 tools/lib/lockdep/rbtree.c
create mode 100644 tools/lib/lockdep/uinclude/asm/hweight.h
create mode 100644 tools/lib/lockdep/uinclude/asm/sections.h
create mode 100644 tools/lib/lockdep/uinclude/linux/bitops.h
create mode 100644 tools/lib/lockdep/uinclude/linux/compiler.h
create mode 100644 tools/lib/lockdep/uinclude/linux/debug_locks.h
create mode 100644 tools/lib/lockdep/uinclude/linux/delay.h
create mode 100644 tools/lib/lockdep/uinclude/linux/export.h
create mode 100644 tools/lib/lockdep/uinclude/linux/ftrace.h
create mode 100644 tools/lib/lockdep/uinclude/linux/gfp.h
create mode 100644 tools/lib/lockdep/uinclude/linux/hardirq.h
create mode 100644 tools/lib/lockdep/uinclude/linux/hash.h
create mode 100644 tools/lib/lockdep/uinclude/linux/interrupt.h
create mode 100644 tools/lib/lockdep/uinclude/linux/irqflags.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kallsyms.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kern_levels.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kernel.h
create mode 100644 tools/lib/lockdep/uinclude/linux/kmemcheck.h
create mode 100644 tools/lib/lockdep/uinclude/linux/linkage.h
create mode 100644 tools/lib/lockdep/uinclude/linux/list.h
create mode 100644 tools/lib/lockdep/uinclude/linux/lockdep.h
create mode 100644 tools/lib/lockdep/uinclude/linux/mm_types.h
create mode 100644 tools/lib/lockdep/uinclude/linux/module.h
create mode 100644 tools/lib/lockdep/uinclude/linux/mutex.h
create mode 100644 tools/lib/lockdep/uinclude/linux/poison.h
create mode 100644 tools/lib/lockdep/uinclude/linux/prefetch.h
create mode 100644 tools/lib/lockdep/uinclude/linux/proc_fs.h
create mode 100644 tools/lib/lockdep/uinclude/linux/rbtree.h
create mode 100644 tools/lib/lockdep/uinclude/linux/rbtree_augmented.h
create mode 100644 tools/lib/lockdep/uinclude/linux/rcu.h
create mode 100644 tools/lib/lockdep/uinclude/linux/seq_file.h
create mode 100644 tools/lib/lockdep/uinclude/linux/spinlock.h
create mode 100644 tools/lib/lockdep/uinclude/linux/stacktrace.h
create mode 100644 tools/lib/lockdep/uinclude/linux/stringify.h
create mode 100644 tools/lib/lockdep/uinclude/linux/system.h
create mode 100644 tools/lib/lockdep/uinclude/linux/types.h
create mode 100644 tools/lib/lockdep/uinclude/linux/util.h
create mode 100644 tools/lib/lockdep/uinclude/trace/events/lock.h

diff --git a/tools/lib/lockdep/Makefile b/tools/lib/lockdep/Makefile
new file mode 100644
index 0000000..66c14f2
--- /dev/null
+++ b/tools/lib/lockdep/Makefile
@@ -0,0 +1,251 @@
+# liblockdep version
+LL_VERSION = 0
+LL_PATCHLEVEL = 0
+LL_EXTRAVERSION = 1
+
+# file format version
+FILE_VERSION = 1
+
+MAKEFLAGS += --no-print-directory
+
+
+# Makefiles suck: This macro sets a default value of $(2) for the
+# variable named by $(1), unless the variable has been set by
+# environment or command line. This is necessary for CC and AR
+# because make sets default values, so the simpler ?= approach
+# won't work as expected.
+define allow-override
+ $(if $(or $(findstring environment,$(origin $(1))),\
+ $(findstring command line,$(origin $(1)))),,\
+ $(eval $(1) = $(2)))
+endef
+
+# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,AR,$(CROSS_COMPILE)ar)
+
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+prefix ?= /usr/local
+libdir_relative = lib
+libdir = $(prefix)/$(libdir_relative)
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+
+export DESTDIR DESTDIR_SQ INSTALL
+
+# copy a bit from Linux kbuild
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+ifeq ("$(origin O)", "command line")
+ BUILD_OUTPUT := $(O)
+endif
+
+ifeq ($(BUILD_SRC),)
+ifneq ($(BUILD_OUTPUT),)
+
+define build_output
+ $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \
+ BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1
+endef
+
+saved-output := $(BUILD_OUTPUT)
+BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd)
+$(if $(BUILD_OUTPUT),, \
+ $(error output directory "$(saved-output)" does not exist))
+
+all: sub-make
+
+gui: force
+ $(call build_output, all_cmd)
+
+$(filter-out gui,$(MAKECMDGOALS)): sub-make
+
+sub-make: force
+ $(call build_output, $(MAKECMDGOALS))
+
+
+# Leave processing to above invocation of make
+skip-makefile := 1
+
+endif # BUILD_OUTPUT
+endif # BUILD_SRC
+
+# We process the rest of the Makefile if this is the final invocation of make
+ifeq ($(skip-makefile),)
+
+srctree := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))
+objtree := $(CURDIR)
+src := $(srctree)
+obj := $(objtree)
+
+export prefix libdir bindir src obj
+
+# Shell quotes
+libdir_SQ = $(subst ','\'',$(libdir))
+bindir_SQ = $(subst ','\'',$(bindir))
+
+LIB_FILE = liblockdep.a liblockdep.so
+BIN_FILE = lockdep
+
+CONFIG_INCLUDES =
+CONFIG_LIBS =
+CONFIG_FLAGS =
+
+OBJ = $@
+N =
+
+export Q VERBOSE
+
+LIBLOCKDEP_VERSION = $(LL_VERSION).$(LL_PATCHLEVEL).$(LL_EXTRAVERSION)
+
+INCLUDES = -I. -I/usr/local/include -I./uinclude $(CONFIG_INCLUDES)
+
+# Set compile option CFLAGS if not set elsewhere
+CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g
+
+override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
+
+ifeq ($(VERBOSE),1)
+ Q =
+ print_compile =
+ print_app_build =
+ print_fpic_compile =
+ print_shared_lib_compile =
+ print_install =
+else
+ Q = @
+ print_compile = echo ' CC '$(OBJ);
+ print_app_build = echo ' BUILD '$(OBJ);
+ print_fpic_compile = echo ' CC FPIC '$(OBJ);
+ print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ);
+ print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ);
+ print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2';
+endif
+
+do_fpic_compile = \
+ ($(print_fpic_compile) \
+ $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@)
+
+do_app_build = \
+ ($(print_app_build) \
+ $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS))
+
+do_compile_shared_library = \
+ ($(print_shared_lib_compile) \
+ $(CC) --shared $^ -o $@ -lpthread -ldl)
+
+do_build_static_lib = \
+ ($(print_static_lib_build) \
+ $(RM) $@; $(AR) rcs $@ $^)
+
+
+define do_compile
+ $(print_compile) \
+ $(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
+endef
+
+$(obj)/%.o: $(src)/%.c
+ $(Q)$(call do_compile)
+
+%.o: $(src)/%.c
+ $(Q)$(call do_compile)
+
+PEVENT_LIB_OBJS = common.o lockdep.o preload.o rbtree.o
+
+ALL_OBJS = $(PEVENT_LIB_OBJS)
+
+CMD_TARGETS = $(LIB_FILE)
+
+TARGETS = $(CMD_TARGETS)
+
+
+all: all_cmd
+
+all_cmd: $(CMD_TARGETS)
+
+liblockdep.so: $(PEVENT_LIB_OBJS)
+ $(Q)$(do_compile_shared_library)
+
+liblockdep.a: $(PEVENT_LIB_OBJS)
+ $(Q)$(do_build_static_lib)
+
+$(PEVENT_LIB_OBJS): %.o: $(src)/%.c
+ $(Q)$(do_fpic_compile)
+
+## make deps
+
+all_objs := $(sort $(ALL_OBJS))
+all_deps := $(all_objs:%.o=.%.d)
+
+# let .d file also depends on the source and header files
+define check_deps
+ @set -e; $(RM) $@; \
+ $(CC) -MM $(CFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ $(RM) $@.$$$$
+endef
+
+$(all_deps): .%.d: $(src)/%.c
+ $(Q)$(call check_deps)
+
+$(all_objs) : %.o : .%.d
+
+dep_includes := $(wildcard $(all_deps))
+
+ifneq ($(dep_includes),)
+ include $(dep_includes)
+endif
+
+### Detect environment changes
+TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
+
+tags: force
+ $(RM) tags
+ find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
+ --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
+
+TAGS: force
+ $(RM) TAGS
+ find . -name '*.[ch]' | xargs etags \
+ --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
+
+define do_install
+ $(print_install) \
+ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+ fi; \
+ $(INSTALL) $1 '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: all_cmd
+ $(Q)$(call do_install,$(LIB_FILE),$(libdir_SQ))
+ $(Q)$(call do_install,$(BIN_FILE),$(bindir_SQ))
+
+install: install_lib
+
+clean:
+ $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d
+ $(RM) tags TAGS
+
+endif # skip-makefile
+
+PHONY += force
+force:
+
+# Declare the contents of the .PHONY variable as phony. We keep that
+# information in a variable so we can use it in if_changed and friends.
+.PHONY: $(PHONY)
diff --git a/tools/lib/lockdep/common.c b/tools/lib/lockdep/common.c
new file mode 100644
index 0000000..8ef602f
--- /dev/null
+++ b/tools/lib/lockdep/common.c
@@ -0,0 +1,33 @@
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/compiler.h>
+#include <linux/lockdep.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+static __thread struct task_struct current_obj;
+
+/* lockdep wants these */
+bool debug_locks = true;
+bool debug_locks_silent;
+
+__attribute__((constructor)) static void liblockdep_init(void)
+{
+ lockdep_init();
+}
+
+__attribute__((destructor)) static void liblockdep_exit(void)
+{
+ debug_check_no_locks_held(&current_obj);
+}
+
+struct task_struct *__curr(void)
+{
+ if (current_obj.pid == 0) {
+ /* Makes lockdep output pretty */
+ prctl(PR_GET_NAME, current_obj.comm);
+ current_obj.pid = syscall(__NR_gettid);
+ }
+
+ return &current_obj;
+}
diff --git a/tools/lib/lockdep/lockdep.c b/tools/lib/lockdep/lockdep.c
new file mode 100644
index 0000000..8ddd0ff
--- /dev/null
+++ b/tools/lib/lockdep/lockdep.c
@@ -0,0 +1,2 @@
+#include <linux/lockdep.h>
+#include "../../../kernel/lockdep.c"
diff --git a/tools/lib/lockdep/lockdep_internals.h b/tools/lib/lockdep/lockdep_internals.h
new file mode 100644
index 0000000..109e96f
--- /dev/null
+++ b/tools/lib/lockdep/lockdep_internals.h
@@ -0,0 +1 @@
+#include "../../../kernel/lockdep_internals.h"
diff --git a/tools/lib/lockdep/lockdep_states.h b/tools/lib/lockdep/lockdep_states.h
new file mode 100644
index 0000000..6b75423
--- /dev/null
+++ b/tools/lib/lockdep/lockdep_states.h
@@ -0,0 +1 @@
+#include "../../../kernel/lockdep_states.h"
diff --git a/tools/lib/lockdep/rbtree.c b/tools/lib/lockdep/rbtree.c
new file mode 100644
index 0000000..f7f4303
--- /dev/null
+++ b/tools/lib/lockdep/rbtree.c
@@ -0,0 +1 @@
+#include "../../../lib/rbtree.c"
diff --git a/tools/lib/lockdep/uinclude/asm/hweight.h b/tools/lib/lockdep/uinclude/asm/hweight.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/asm/sections.h b/tools/lib/lockdep/uinclude/asm/sections.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/bitops.h b/tools/lib/lockdep/uinclude/linux/bitops.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
new file mode 100644
index 0000000..7ac838a
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/compiler.h
@@ -0,0 +1,7 @@
+#ifndef _LIBLOCKDEP_LINUX_COMPILER_H_
+#define _LIBLOCKDEP_LINUX_COMPILER_H_
+
+#define __used __attribute__((__unused__))
+#define unlikely
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/debug_locks.h b/tools/lib/lockdep/uinclude/linux/debug_locks.h
new file mode 100644
index 0000000..f38eb64
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/debug_locks.h
@@ -0,0 +1,12 @@
+#ifndef _LIBLOCKDEP_DEBUG_LOCKS_H_
+#define _LIBLOCKDEP_DEBUG_LOCKS_H_
+
+#include <stddef.h>
+#include <linux/compiler.h>
+
+#define DEBUG_LOCKS_WARN_ON(x) (x)
+
+extern bool debug_locks;
+extern bool debug_locks_silent;
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/delay.h b/tools/lib/lockdep/uinclude/linux/delay.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/export.h b/tools/lib/lockdep/uinclude/linux/export.h
new file mode 100644
index 0000000..6bdf349
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/export.h
@@ -0,0 +1,7 @@
+#ifndef _LIBLOCKDEP_LINUX_EXPORT_H_
+#define _LIBLOCKDEP_LINUX_EXPORT_H_
+
+#define EXPORT_SYMBOL(sym)
+#define EXPORT_SYMBOL_GPL(sym)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/ftrace.h b/tools/lib/lockdep/uinclude/linux/ftrace.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/gfp.h b/tools/lib/lockdep/uinclude/linux/gfp.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/hardirq.h b/tools/lib/lockdep/uinclude/linux/hardirq.h
new file mode 100644
index 0000000..c8f3f8f
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/hardirq.h
@@ -0,0 +1,11 @@
+#ifndef _LIBLOCKDEP_LINUX_HARDIRQ_H_
+#define _LIBLOCKDEP_LINUX_HARDIRQ_H_
+
+#define SOFTIRQ_BITS 0UL
+#define HARDIRQ_BITS 0UL
+#define SOFTIRQ_SHIFT 0UL
+#define HARDIRQ_SHIFT 0UL
+#define hardirq_count() 0UL
+#define softirq_count() 0UL
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/hash.h b/tools/lib/lockdep/uinclude/linux/hash.h
new file mode 100644
index 0000000..0f84798
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/hash.h
@@ -0,0 +1 @@
+#include "../../../include/linux/hash.h"
diff --git a/tools/lib/lockdep/uinclude/linux/interrupt.h b/tools/lib/lockdep/uinclude/linux/interrupt.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/irqflags.h b/tools/lib/lockdep/uinclude/linux/irqflags.h
new file mode 100644
index 0000000..6cc296f
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/irqflags.h
@@ -0,0 +1,38 @@
+#ifndef _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_
+#define _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_
+
+# define trace_hardirq_context(p) 0
+# define trace_softirq_context(p) 0
+# define trace_hardirqs_enabled(p) 0
+# define trace_softirqs_enabled(p) 0
+# define trace_hardirq_enter() do { } while (0)
+# define trace_hardirq_exit() do { } while (0)
+# define lockdep_softirq_enter() do { } while (0)
+# define lockdep_softirq_exit() do { } while (0)
+# define INIT_TRACE_IRQFLAGS
+
+# define stop_critical_timings() do { } while (0)
+# define start_critical_timings() do { } while (0)
+
+#define raw_local_irq_disable() do { } while (0)
+#define raw_local_irq_enable() do { } while (0)
+#define raw_local_irq_save(flags) ((flags) = 0)
+#define raw_local_irq_restore(flags) do { } while (0)
+#define raw_local_save_flags(flags) ((flags) = 0)
+#define raw_irqs_disabled_flags(flags) do { } while (0)
+#define raw_irqs_disabled() 0
+#define raw_safe_halt()
+
+#define local_irq_enable() do { } while (0)
+#define local_irq_disable() do { } while (0)
+#define local_irq_save(flags) ((flags) = 0)
+#define local_irq_restore(flags) do { } while (0)
+#define local_save_flags(flags) ((flags) = 0)
+#define irqs_disabled() (1)
+#define irqs_disabled_flags(flags) (0)
+#define safe_halt() do { } while (0)
+
+#define trace_lock_release(x, y)
+#define trace_lock_acquire(a, b, c, d, e, f, g)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kallsyms.h b/tools/lib/lockdep/uinclude/linux/kallsyms.h
new file mode 100644
index 0000000..b0f2dbd
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/kallsyms.h
@@ -0,0 +1,32 @@
+#ifndef _LIBLOCKDEP_LINUX_KALLSYMS_H_
+#define _LIBLOCKDEP_LINUX_KALLSYMS_H_
+
+#include <linux/kernel.h>
+#include <stdio.h>
+
+#define KSYM_NAME_LEN 128
+
+struct module;
+
+static inline const char *kallsyms_lookup(unsigned long addr,
+ unsigned long *symbolsize,
+ unsigned long *offset,
+ char **modname, char *namebuf)
+{
+ return NULL;
+}
+
+#include <execinfo.h>
+#include <stdlib.h>
+static inline void print_ip_sym(unsigned long ip)
+{
+ char **name;
+
+ name = backtrace_symbols((void **)&ip, 1);
+
+ printf("%s\n", *name);
+
+ free(name);
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kern_levels.h b/tools/lib/lockdep/uinclude/linux/kern_levels.h
new file mode 100644
index 0000000..3b9bade
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/kern_levels.h
@@ -0,0 +1,25 @@
+#ifndef __KERN_LEVELS_H__
+#define __KERN_LEVELS_H__
+
+#define KERN_SOH "" /* ASCII Start Of Header */
+#define KERN_SOH_ASCII ''
+
+#define KERN_EMERG KERN_SOH "" /* system is unusable */
+#define KERN_ALERT KERN_SOH "" /* action must be taken immediately */
+#define KERN_CRIT KERN_SOH "" /* critical conditions */
+#define KERN_ERR KERN_SOH "" /* error conditions */
+#define KERN_WARNING KERN_SOH "" /* warning conditions */
+#define KERN_NOTICE KERN_SOH "" /* normal but significant condition */
+#define KERN_INFO KERN_SOH "" /* informational */
+#define KERN_DEBUG KERN_SOH "" /* debug-level messages */
+
+#define KERN_DEFAULT KERN_SOH "" /* the default kernel loglevel */
+
+/*
+ * Annotation for a "continued" line of log printout (only done after a
+ * line that had no enclosing \n). Only to be used by core/arch code
+ * during early bootup (a continued line is not SMP-safe otherwise).
+ */
+#define KERN_CONT ""
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kernel.h b/tools/lib/lockdep/uinclude/linux/kernel.h
new file mode 100644
index 0000000..afa8323
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/kernel.h
@@ -0,0 +1,37 @@
+#ifndef _LIBLOCKDEP_LINUX_KERNEL_H_
+#define _LIBLOCKDEP_LINUX_KERNEL_H_
+
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/rcu.h>
+#include <linux/hardirq.h>
+#include <linux/kern_levels.h>
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) * __mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+#define WARN_ON(x) (x)
+#define WARN_ON_ONCE(x) (x)
+#define likely(x) (x)
+#define WARN(x, y, z) (x)
+#define uninitialized_var(x) x
+#define __init
+#define noinline
+#define list_add_tail_rcu list_add_tail
+
+#ifndef CALLER_ADDR0
+#define _THIS_IP_ CALLER_ADDR0
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+#endif
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kmemcheck.h b/tools/lib/lockdep/uinclude/linux/kmemcheck.h
new file mode 100644
index 0000000..94d598b
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/kmemcheck.h
@@ -0,0 +1,8 @@
+#ifndef _LIBLOCKDEP_LINUX_KMEMCHECK_H_
+#define _LIBLOCKDEP_LINUX_KMEMCHECK_H_
+
+static inline void kmemcheck_mark_initialized(void *address, unsigned int n)
+{
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/linkage.h b/tools/lib/lockdep/uinclude/linux/linkage.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/list.h b/tools/lib/lockdep/uinclude/linux/list.h
new file mode 100644
index 0000000..6e9ef31
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/list.h
@@ -0,0 +1 @@
+#include "../../../include/linux/list.h"
diff --git a/tools/lib/lockdep/uinclude/linux/lockdep.h b/tools/lib/lockdep/uinclude/linux/lockdep.h
new file mode 100644
index 0000000..d0f5d6e
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/lockdep.h
@@ -0,0 +1,55 @@
+#ifndef _LIBLOCKDEP_LOCKDEP_H_
+#define _LIBLOCKDEP_LOCKDEP_H_
+
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <string.h>
+#include <limits.h>
+#include <linux/utsname.h>
+
+
+#define MAX_LOCK_DEPTH 2000UL
+
+#include "../../../include/linux/lockdep.h"
+
+struct task_struct {
+ u64 curr_chain_key;
+ int lockdep_depth;
+ unsigned int lockdep_recursion;
+ struct held_lock held_locks[MAX_LOCK_DEPTH];
+ gfp_t lockdep_reclaim_gfp;
+ int pid;
+ char comm[17];
+};
+
+extern struct task_struct *__curr(void);
+
+#define current (__curr())
+
+#define debug_locks_off() 1
+#define task_pid_nr(tsk) ((tsk)->pid)
+
+#define KSYM_NAME_LEN 128
+#define printk printf
+
+#define list_del_rcu list_del
+
+#define atomic_t unsigned long
+#define atomic_inc(x) ((*(x))++)
+
+static struct new_utsname *init_utsname(void)
+{
+ static struct new_utsname n = (struct new_utsname) {
+ .release = "liblockdep",
+ .version = LIBLOCKDEP_VERSION,
+ };
+
+ return &n;
+}
+
+#define print_tainted() ""
+#define static_obj(x) 1
+
+#define debug_show_all_locks()
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/mm_types.h b/tools/lib/lockdep/uinclude/linux/mm_types.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/module.h b/tools/lib/lockdep/uinclude/linux/module.h
new file mode 100644
index 0000000..09c7a7b
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/module.h
@@ -0,0 +1,6 @@
+#ifndef _LIBLOCKDEP_LINUX_MODULE_H_
+#define _LIBLOCKDEP_LINUX_MODULE_H_
+
+#define module_param(name, type, perm)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/mutex.h b/tools/lib/lockdep/uinclude/linux/mutex.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/poison.h b/tools/lib/lockdep/uinclude/linux/poison.h
new file mode 100644
index 0000000..0c27bdf
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/poison.h
@@ -0,0 +1 @@
+#include "../../../include/linux/poison.h"
diff --git a/tools/lib/lockdep/uinclude/linux/prefetch.h b/tools/lib/lockdep/uinclude/linux/prefetch.h
new file mode 100644
index 0000000..d73fe6f
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/prefetch.h
@@ -0,0 +1,6 @@
+#ifndef _LIBLOCKDEP_LINUX_PREFETCH_H_
+#define _LIBLOCKDEP_LINUX_PREFETCH_H
+
+static inline void prefetch(void *a __attribute__((unused))) { }
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/proc_fs.h b/tools/lib/lockdep/uinclude/linux/proc_fs.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/rbtree.h b/tools/lib/lockdep/uinclude/linux/rbtree.h
new file mode 100644
index 0000000..965901d
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/rbtree.h
@@ -0,0 +1 @@
+#include "../../../include/linux/rbtree.h"
diff --git a/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h b/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h
new file mode 100644
index 0000000..c375947
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h
@@ -0,0 +1,2 @@
+#define __always_inline
+#include "../../../include/linux/rbtree_augmented.h"
diff --git a/tools/lib/lockdep/uinclude/linux/rcu.h b/tools/lib/lockdep/uinclude/linux/rcu.h
new file mode 100644
index 0000000..4c99fcb
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/rcu.h
@@ -0,0 +1,16 @@
+#ifndef _LIBLOCKDEP_RCU_H_
+#define _LIBLOCKDEP_RCU_H_
+
+int rcu_scheduler_active;
+
+static inline int rcu_lockdep_current_cpu_online(void)
+{
+ return 1;
+}
+
+static inline int rcu_is_cpu_idle(void)
+{
+ return 1;
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/seq_file.h b/tools/lib/lockdep/uinclude/linux/seq_file.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/spinlock.h b/tools/lib/lockdep/uinclude/linux/spinlock.h
new file mode 100644
index 0000000..68c1aa2
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/spinlock.h
@@ -0,0 +1,25 @@
+#ifndef _LIBLOCKDEP_SPINLOCK_H_
+#define _LIBLOCKDEP_SPINLOCK_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#define arch_spinlock_t pthread_mutex_t
+#define __ARCH_SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER
+
+static inline void arch_spin_lock(arch_spinlock_t *mutex)
+{
+ pthread_mutex_lock(mutex);
+}
+
+static inline void arch_spin_unlock(arch_spinlock_t *mutex)
+{
+ pthread_mutex_unlock(mutex);
+}
+
+static inline bool arch_spin_is_locked(arch_spinlock_t *mutex)
+{
+ return true;
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/stacktrace.h b/tools/lib/lockdep/uinclude/linux/stacktrace.h
new file mode 100644
index 0000000..39aecc6
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/stacktrace.h
@@ -0,0 +1,32 @@
+#ifndef _LIBLOCKDEP_LINUX_STACKTRACE_H_
+#define _LIBLOCKDEP_LINUX_STACKTRACE_H_
+
+#include <execinfo.h>
+
+struct stack_trace {
+ unsigned int nr_entries, max_entries;
+ unsigned long *entries;
+ int skip;
+};
+
+static inline void print_stack_trace(struct stack_trace *trace, int spaces)
+{
+ backtrace_symbols_fd((void **)trace->entries, trace->nr_entries, 1);
+}
+
+#define save_stack_trace(trace) \
+ ((trace)->nr_entries = \
+ backtrace((void **)(trace)->entries, (trace)->max_entries))
+
+static inline int dump_stack(void)
+{
+ void *array[64];
+ size_t size;
+
+ size = backtrace(array, 64);
+ backtrace_symbols_fd(array, size, 1);
+
+ return 0;
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/stringify.h b/tools/lib/lockdep/uinclude/linux/stringify.h
new file mode 100644
index 0000000..05dfcd1
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/stringify.h
@@ -0,0 +1,7 @@
+#ifndef _LIBLOCKDEP_LINUX_STRINGIFY_H_
+#define _LIBLOCKDEP_LINUX_STRINGIFY_H_
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/system.h b/tools/lib/lockdep/uinclude/linux/system.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/linux/types.h b/tools/lib/lockdep/uinclude/linux/types.h
new file mode 100644
index 0000000..929938f
--- /dev/null
+++ b/tools/lib/lockdep/uinclude/linux/types.h
@@ -0,0 +1,58 @@
+#ifndef _LIBLOCKDEP_LINUX_TYPES_H_
+#define _LIBLOCKDEP_LINUX_TYPES_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
+#include <asm/types.h>
+
+struct page;
+struct kmem_cache;
+
+typedef unsigned gfp_t;
+
+typedef __u64 u64;
+typedef __s64 s64;
+
+typedef __u32 u32;
+typedef __s32 s32;
+
+typedef __u16 u16;
+typedef __s16 s16;
+
+typedef __u8 u8;
+typedef __s8 s8;
+
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/util.h b/tools/lib/lockdep/uinclude/linux/util.h
new file mode 100644
index 0000000..e69de29
diff --git a/tools/lib/lockdep/uinclude/trace/events/lock.h b/tools/lib/lockdep/uinclude/trace/events/lock.h
new file mode 100644
index 0000000..e69de29
--
1.8.2.1

2013-05-16 03:16:56

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 5/9] liblockdep: Add public headers for pthread_rwlock_t implementation

Both pthreads and lockdep support dealing with rwlocks, so
here's the liblockdep implementation for those.

Signed-off-by: Sasha Levin <[email protected]>
---
tools/lib/lockdep/include/liblockdep/rwlock.h | 86 +++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
create mode 100644 tools/lib/lockdep/include/liblockdep/rwlock.h

diff --git a/tools/lib/lockdep/include/liblockdep/rwlock.h b/tools/lib/lockdep/include/liblockdep/rwlock.h
new file mode 100644
index 0000000..a680ab8
--- /dev/null
+++ b/tools/lib/lockdep/include/liblockdep/rwlock.h
@@ -0,0 +1,86 @@
+#ifndef _LIBLOCKDEP_RWLOCK_H
+#define _LIBLOCKDEP_RWLOCK_H
+
+#include <pthread.h>
+#include "common.h"
+
+struct liblockdep_pthread_rwlock {
+ pthread_rwlock_t rwlock;
+ struct lockdep_map dep_map;
+};
+
+typedef struct liblockdep_pthread_rwlock liblockdep_pthread_rwlock_t;
+
+#define LIBLOCKDEP_PTHREAD_RWLOCK_INITIALIZER(rwl) \
+ (struct liblockdep_pthread_rwlock) { \
+ .rwlock = PTHREAD_RWLOCK_INITIALIZER, \
+ .dep_map = STATIC_LOCKDEP_MAP_INIT(#rwl, &((&(rwl))->dep_map)), \
+}
+
+static inline int __rwlock_init(liblockdep_pthread_rwlock_t *lock,
+ const char *name,
+ struct lock_class_key *key,
+ const pthread_rwlockattr_t *attr)
+{
+ lockdep_init_map(&lock->dep_map, name, key, 0);
+
+ return pthread_rwlock_init(&lock->rwlock, attr);
+}
+
+#define liblockdep_pthread_rwlock_init(lock, attr) \
+({ \
+ static struct lock_class_key __key; \
+ \
+ __rwlock_init((lock), #lock, &__key, (attr)); \
+})
+
+static inline int liblockdep_pthread_rwlock_rdlock(liblockdep_pthread_rwlock_t *lock)
+{
+ lock_acquire(&lock->dep_map, 0, 0, 2, 2, NULL, (unsigned long)_RET_IP_);
+ return pthread_rwlock_rdlock(&lock->rwlock);
+
+}
+
+static inline int liblockdep_pthread_rwlock_unlock(liblockdep_pthread_rwlock_t *lock)
+{
+ lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
+ return pthread_rwlock_unlock(&lock->rwlock);
+}
+
+static inline int liblockdep_pthread_rwlock_wrlock(liblockdep_pthread_rwlock_t *lock)
+{
+ lock_acquire(&lock->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+ return pthread_rwlock_wrlock(&lock->rwlock);
+}
+
+static inline int liblockdep_pthread_rwlock_tryrdlock(liblockdep_pthread_rwlock_t *lock)
+{
+ lock_acquire(&lock->dep_map, 0, 1, 2, 2, NULL, (unsigned long)_RET_IP_);
+ return pthread_rwlock_tryrdlock(&lock->rwlock) == 0 ? 1 : 0;
+}
+
+static inline int liblockdep_pthread_rwlock_trywlock(liblockdep_pthread_rwlock_t *lock)
+{
+ lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+ return pthread_rwlock_trywlock(&lock->rwlock) == 0 ? 1 : 0;
+}
+
+static inline int liblockdep_rwlock_destroy(liblockdep_pthread_rwlock_t *lock)
+{
+ return pthread_rwlock_destroy(&lock->rwlock);
+}
+
+#ifdef __USE_LIBLOCKDEP
+
+#define pthread_rwlock_t liblockdep_pthread_rwlock_t
+#define pthread_rwlock_init liblockdep_pthread_rwlock_init
+#define pthread_rwlock_rdlock liblockdep_pthread_rwlock_rdlock
+#define pthread_rwlock_unlock liblockdep_pthread_rwlock_unlock
+#define pthread_rwlock_wrlock liblockdep_pthread_rwlock_wrlock
+#define pthread_rwlock_tryrdlock liblockdep_pthread_rwlock_tryrdlock
+#define pthread_rwlock_trywlock liblockdep_pthread_rwlock_trywlock
+#define pthread_rwlock_destroy liblockdep_rwlock_destroy
+
+#endif
+
+#endif
--
1.8.2.1

2013-05-16 03:17:15

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 8/9] liblockdep: Add the 'lockdep' user-space utility

This is a simple wrapper to make using liblockdep on existing
applications much easier.

After running 'make && make install', it becomes quite simple to
test things with liblockdep. For example, to try it on perf:

lockdep perf

No other integration required.

Signed-off-by: Sasha Levin <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: Linus Torvalds <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/lib/lockdep/lockdep | 3 +++
1 file changed, 3 insertions(+)
create mode 100755 tools/lib/lockdep/lockdep

diff --git a/tools/lib/lockdep/lockdep b/tools/lib/lockdep/lockdep
new file mode 100755
index 0000000..a805c81
--- /dev/null
+++ b/tools/lib/lockdep/lockdep
@@ -0,0 +1,3 @@
+#! /bin/bash
+
+LD_PRELOAD="liblockdep.so $LD_PRELOAD" "$@"
--
1.8.2.1

2013-05-16 03:16:49

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

This allows lockdep to be used without being compiled in the
original program.

Usage is quite simple:

LD_PRELOAD=/path/to/liblockdep.so /path/to/my/program

And magically, you'll have lockdep checking in your program!

Signed-off-by: Sasha Levin <[email protected]>
---
tools/lib/lockdep/preload.c | 447 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 447 insertions(+)
create mode 100644 tools/lib/lockdep/preload.c

diff --git a/tools/lib/lockdep/preload.c b/tools/lib/lockdep/preload.c
new file mode 100644
index 0000000..83f5b22
--- /dev/null
+++ b/tools/lib/lockdep/preload.c
@@ -0,0 +1,447 @@
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include "include/liblockdep/mutex.h"
+#include "../../../include/linux/rbtree.h"
+
+/**
+ * struct lock_lookup - liblockdep's view of a single unique lock
+ * @orig: pointer to the original pthread lock, used for lookups
+ * @dep_map: lockdep's dep_map structure
+ * @key: lockdep's key structure
+ * @node: rb-tree node used to store the lock in a global tree
+ * @name: a unique name for the lock
+ */
+struct lock_lookup {
+ void *orig; /* Original pthread lock, used for lookups */
+ struct lockdep_map dep_map; /* Since all locks are dynamic, we need
+ * a dep_map and a key for each lock */
+ /*
+ * Wait, there's no support for key classes? Yup :(
+ * Most big projects wrap the pthread api with their own calls to
+ * be compatible with different locking methods. This means that
+ * "classes" will be brokes since the function that creates all
+ * locks will point to a generic locking function instead of the
+ * actual code that wants to do the locking.
+ */
+ struct lock_class_key key;
+ struct rb_node node;
+#define LIBLOCKDEP_MAX_LOCK_NAME 22
+ char name[LIBLOCKDEP_MAX_LOCK_NAME];
+};
+
+/* This is where we store our locks */
+static struct rb_root locks = RB_ROOT;
+static pthread_rwlock_t locks_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* pthread mutex API */
+
+#ifdef __GLIBC__
+extern int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
+extern int __pthread_mutex_lock(pthread_mutex_t *mutex);
+extern int __pthread_mutex_trylock(pthread_mutex_t *mutex);
+extern int __pthread_mutex_unlock(pthread_mutex_t *mutex);
+extern int __pthread_mutex_destroy(pthread_mutex_t *mutex);
+#else
+#define __pthread_mutex_init NULL
+#define __pthread_mutex_lock NULL
+#define __pthread_mutex_trylock NULL
+#define __pthread_mutex_unlock NULL
+#define __pthread_mutex_destroy NULL
+#endif
+static int (*ll_pthread_mutex_init)(pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *attr) = __pthread_mutex_init;
+static int (*ll_pthread_mutex_lock)(pthread_mutex_t *mutex) = __pthread_mutex_lock;
+static int (*ll_pthread_mutex_trylock)(pthread_mutex_t *mutex) = __pthread_mutex_trylock;
+static int (*ll_pthread_mutex_unlock)(pthread_mutex_t *mutex) = __pthread_mutex_unlock;
+static int (*ll_pthread_mutex_destroy)(pthread_mutex_t *mutex) = __pthread_mutex_destroy;
+
+/* pthread rwlock API */
+
+#ifdef __GLIBC__
+extern int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
+extern int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
+#else
+#define __pthread_rwlock_init NULL
+#define __pthread_rwlock_destroy NULL
+#define __pthread_rwlock_wrlock NULL
+#define __pthread_rwlock_trywrlock NULL
+#define __pthread_rwlock_rdlock NULL
+#define __pthread_rwlock_tryrdlock NULL
+#define __pthread_rwlock_unlock NULL
+#endif
+
+static int (*ll_pthread_rwlock_init)(pthread_rwlock_t *rwlock,
+ const pthread_rwlockattr_t *attr) = __pthread_rwlock_init;
+static int (*ll_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock) = __pthread_rwlock_destroy;
+static int (*ll_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_rdlock;
+static int (*ll_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_tryrdlock;
+static int (*ll_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_trywrlock;
+static int (*ll_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_wrlock;
+static int (*ll_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_unlock;
+
+enum { none, prepare, done, } __init_state;
+static void init_preload(void);
+static void try_init_preload(void)
+{
+ if (!__init_state != done)
+ init_preload();
+}
+
+static struct rb_node **__get_lock_node(void *lock, struct rb_node **parent)
+{
+ struct rb_node **node = &locks.rb_node;
+ struct lock_lookup *l;
+
+ *parent = NULL;
+
+ while (*node) {
+ l = rb_entry(*node, struct lock_lookup, node);
+
+ *parent = *node;
+ if (lock < l->orig)
+ node = &l->node.rb_left;
+ else if (lock > l->orig)
+ node = &l->node.rb_right;
+ else
+ return node;
+ }
+
+ return node;
+}
+
+#ifndef LIBLOCKDEP_STATIC_ENTRIES
+#define LIBLOCKDEP_STATIC_ENTRIES 1024
+#endif
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static struct lock_lookup __locks[LIBLOCKDEP_STATIC_ENTRIES];
+static int __locks_nr;
+
+static inline bool is_static_lock(struct lock_lookup *lock)
+{
+ return lock >= __locks && lock < __locks + ARRAY_SIZE(__locks);
+}
+
+static struct lock_lookup *alloc_lock(void)
+{
+ if (__init_state != done) {
+ /*
+ * Some programs attempt to initialize and use locks in their
+ * allocation path. This means that a call to malloc() would
+ * result in locks being initialized and locked.
+ *
+ * Why is it an issue for us? dlsym() below will try allocating
+ * to give us the original function. Since this allocation will
+ * result in a locking operations, we have to let pthread deal
+ * with it, but we can't! we don't have the pointer to the
+ * original API since we're inside dlsym() trying to get it
+ */
+
+ int idx = __locks_nr++;
+ if (idx >= ARRAY_SIZE(__locks)) {
+ fprintf(stderr,
+ "LOCKDEP error: insufficient LIBLOCKDEP_STATIC_ENTRIES\n");
+ exit(EX_UNAVAILABLE);
+ }
+ return __locks + idx;
+ }
+
+ return malloc(sizeof(struct lock_lookup));
+}
+
+static inline void free_lock(struct lock_lookup *lock)
+{
+ if (likely(!is_static_lock(lock)))
+ free(lock);
+}
+
+/**
+ * __get_lock - find or create a lock instance
+ * @lock: pointer to a pthread lock function
+ *
+ * Try to find an existing lock in the rbtree using the provided pointer. If
+ * one wasn't found - create it.
+ */
+static struct lock_lookup *__get_lock(void *lock)
+{
+ struct rb_node **node, *parent;
+ struct lock_lookup *l;
+
+ ll_pthread_rwlock_rdlock(&locks_rwlock);
+ node = __get_lock_node(lock, &parent);
+ ll_pthread_rwlock_unlock(&locks_rwlock);
+ if (*node) {
+ return rb_entry(*node, struct lock_lookup, node);
+ }
+
+ /* We didn't find the lock, let's create it */
+ l = alloc_lock();
+ if (l == NULL)
+ return NULL;
+
+ l->orig = lock;
+ /*
+ * Currently the name of the lock is the ptr value of the pthread lock,
+ * while not optimal, it makes debugging a bit easier.
+ *
+ * TODO: Get the real name of the lock using libdwarf
+ */
+ sprintf(l->name, "%p", lock);
+ lockdep_init_map(&l->dep_map, l->name, &l->key, 0);
+
+ ll_pthread_rwlock_wrlock(&locks_rwlock);
+ /* This might have changed since the last time we fetched it */
+ node = __get_lock_node(lock, &parent);
+ rb_link_node(&l->node, parent, node);
+ rb_insert_color(&l->node, &locks);
+ ll_pthread_rwlock_unlock(&locks_rwlock);
+
+ return l;
+}
+
+static void __del_lock(struct lock_lookup *lock)
+{
+ ll_pthread_rwlock_wrlock(&locks_rwlock);
+ rb_erase(&lock->node, &locks);
+ ll_pthread_rwlock_unlock(&locks_rwlock);
+ free_lock(lock);
+}
+
+int pthread_mutex_init(pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *attr)
+{
+ int r;
+
+ /*
+ * We keep trying to init our preload module because there might be
+ * code in init sections that tries to touch locks before we are
+ * initialized, in that case we'll need to manually call preload
+ * to get us going.
+ *
+ * Funny enough, kernel's lockdep had the same issue, and used
+ * (almost) the same solution. See look_up_lock_class() in
+ * kernel/lockdep.c for details.
+ */
+ try_init_preload();
+
+ r = ll_pthread_mutex_init(mutex, attr);
+ if (r == 0)
+ /*
+ * We do a dummy initialization here so that lockdep could
+ * warn us if something fishy is going on - such as
+ * initializing a held lock.
+ */
+ __get_lock(mutex);
+
+ return r;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+ int r;
+
+ try_init_preload();
+
+ lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 2, NULL,
+ (unsigned long)_RET_IP_);
+ /*
+ * Here's the thing with pthread mutexes: unlike the kernel variant,
+ * they can fail.
+ *
+ * This means that the behaviour here is a bit different from what's
+ * going on in the kernel: there we just tell lockdep that we took the
+ * lock before actually taking it, but here we must deal with the case
+ * that locking failed.
+ *
+ * To do that we'll "release" the lock if locking failed - this way
+ * we'll get lockdep doing the correct checks when we try to take
+ * the lock, and if that fails - we'll be back to the correct
+ * state by releasing it.
+ */
+ r = ll_pthread_mutex_lock(mutex);
+ if (r)
+ lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+ int r;
+
+ try_init_preload();
+
+ lock_acquire(&__get_lock(mutex)->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+ r = ll_pthread_mutex_trylock(mutex);
+ if (r)
+ lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+ int r;
+
+ try_init_preload();
+
+ lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
+ /*
+ * Just like taking a lock, only in reverse!
+ *
+ * If we fail releasing the lock, tell lockdep we're holding it again.
+ */
+ r = ll_pthread_mutex_unlock(mutex);
+ if (r)
+ lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+ try_init_preload();
+
+ /*
+ * Let's see if we're releasing a lock that's held.
+ *
+ * TODO: Hook into free() and add that check there as well.
+ */
+ debug_check_no_locks_freed(mutex, mutex + sizeof(*mutex));
+ __del_lock(__get_lock(mutex));
+ return ll_pthread_mutex_destroy(mutex);
+}
+
+/* This is the rwlock part, very similar to what happened with mutex above */
+int pthread_rwlock_init(pthread_rwlock_t *rwlock,
+ const pthread_rwlockattr_t *attr)
+{
+ int r;
+
+ try_init_preload();
+
+ r = ll_pthread_rwlock_init(rwlock, attr);
+ if (r == 0)
+ __get_lock(rwlock);
+
+ return r;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
+{
+ try_init_preload();
+
+ debug_check_no_locks_freed(rwlock, rwlock + sizeof(*rwlock));
+ __del_lock(__get_lock(rwlock));
+ return ll_pthread_rwlock_destroy(rwlock);
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
+{
+ int r;
+
+ init_preload();
+
+ lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 2, 2, NULL, (unsigned long)_RET_IP_);
+ r = ll_pthread_rwlock_rdlock(rwlock);
+ if (r)
+ lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
+{
+ int r;
+
+ init_preload();
+
+ lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 2, 2, NULL, (unsigned long)_RET_IP_);
+ r = ll_pthread_rwlock_tryrdlock(rwlock);
+ if (r)
+ lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
+{
+ int r;
+
+ init_preload();
+
+ lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+ r = ll_pthread_rwlock_trywrlock(rwlock);
+ if (r)
+ lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
+{
+ int r;
+
+ init_preload();
+
+ lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+ r = ll_pthread_rwlock_wrlock(rwlock);
+ if (r)
+ lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
+{
+ int r;
+
+ init_preload();
+
+ lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+ r = ll_pthread_rwlock_unlock(rwlock);
+ if (r)
+ lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+
+ return r;
+}
+
+__attribute__((constructor)) static void init_preload(void)
+{
+ if (__init_state != done)
+ return;
+
+#ifndef __GLIBC__
+ __init_state = prepare;
+
+ ll_pthread_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init");
+ ll_pthread_mutex_lock = dlsym(RTLD_NEXT, "pthread_mutex_lock");
+ ll_pthread_mutex_trylock = dlsym(RTLD_NEXT, "pthread_mutex_trylock");
+ ll_pthread_mutex_unlock = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
+ ll_pthread_mutex_destroy = dlsym(RTLD_NEXT, "pthread_mutex_destroy");
+
+ ll_pthread_rwlock_init = dlsym(RTLD_NEXT, "pthread_rwlock_init");
+ ll_pthread_rwlock_destroy = dlsym(RTLD_NEXT, "pthread_rwlock_destroy");
+ ll_pthread_rwlock_rdlock = dlsym(RTLD_NEXT, "pthread_rwlock_rdlock");
+ ll_pthread_rwlock_tryrdlock = dlsym(RTLD_NEXT, "pthread_rwlock_tryrdlock");
+ ll_pthread_rwlock_wrlock = dlsym(RTLD_NEXT, "pthread_rwlock_wrlock");
+ ll_pthread_rwlock_trywrlock = dlsym(RTLD_NEXT, "pthread_rwlock_trywrlock");
+ ll_pthread_rwlock_unlock = dlsym(RTLD_NEXT, "pthread_rwlock_unlock");
+#endif
+
+ printf("%p\n", ll_pthread_mutex_trylock);fflush(stdout);
+
+ lockdep_init();
+
+ __init_state = done;
+}
--
1.8.2.1

2013-05-16 03:17:54

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 4/9] liblockdep: Add pthread_mutex_t test suite

This is a rather simple and basic test suite to test common
locking issues.

Beyond tests, it also shows how to use the library.

Signed-off-by: Sasha Levin <[email protected]>
---
tools/lib/lockdep/run_tests.sh | 27 +++++++++++++++++++++++++++
tools/lib/lockdep/tests/AA.c | 13 +++++++++++++
tools/lib/lockdep/tests/ABBA.c | 13 +++++++++++++
tools/lib/lockdep/tests/ABBCCA.c | 15 +++++++++++++++
tools/lib/lockdep/tests/ABBCCDDA.c | 17 +++++++++++++++++
tools/lib/lockdep/tests/ABCABC.c | 15 +++++++++++++++
tools/lib/lockdep/tests/ABCDBCDA.c | 17 +++++++++++++++++
tools/lib/lockdep/tests/ABCDBDDA.c | 17 +++++++++++++++++
tools/lib/lockdep/tests/common.h | 12 ++++++++++++
tools/lib/lockdep/tests/unlock_balance.c | 12 ++++++++++++
10 files changed, 158 insertions(+)
create mode 100755 tools/lib/lockdep/run_tests.sh
create mode 100644 tools/lib/lockdep/tests/AA.c
create mode 100644 tools/lib/lockdep/tests/ABBA.c
create mode 100644 tools/lib/lockdep/tests/ABBCCA.c
create mode 100644 tools/lib/lockdep/tests/ABBCCDDA.c
create mode 100644 tools/lib/lockdep/tests/ABCABC.c
create mode 100644 tools/lib/lockdep/tests/ABCDBCDA.c
create mode 100644 tools/lib/lockdep/tests/ABCDBDDA.c
create mode 100644 tools/lib/lockdep/tests/common.h
create mode 100644 tools/lib/lockdep/tests/unlock_balance.c

diff --git a/tools/lib/lockdep/run_tests.sh b/tools/lib/lockdep/run_tests.sh
new file mode 100755
index 0000000..240a93c
--- /dev/null
+++ b/tools/lib/lockdep/run_tests.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+
+make &> /dev/null
+
+for i in `ls tests/*.c`; do
+ testname=$(basename -s .c "$i")
+ gcc -o tests/$testname -lpthread $i liblockdep.a -Iinclude -D__USE_LIBLOCKDEP &> /dev/null
+ echo -ne "$testname... "
+ if [ $(timeout 1 ./tests/$testname | wc -l) -gt 0 ]; then
+ echo "PASSED!"
+ else
+ echo "FAILED!"
+ fi
+ rm tests/$testname
+done
+
+for i in `ls tests/*.c`; do
+ testname=$(basename -s .c "$i")
+ gcc -o tests/$testname -lpthread -Iinclude $i &> /dev/null
+ echo -ne "(PRELOAD) $testname... "
+ if [ $(timeout 1 ./lockdep ./tests/$testname | wc -l) -gt 0 ]; then
+ echo "PASSED!"
+ else
+ echo "FAILED!"
+ fi
+ rm tests/$testname
+done
diff --git a/tools/lib/lockdep/tests/AA.c b/tools/lib/lockdep/tests/AA.c
new file mode 100644
index 0000000..0f782ff
--- /dev/null
+++ b/tools/lib/lockdep/tests/AA.c
@@ -0,0 +1,13 @@
+#include <liblockdep/mutex.h>
+
+void main(void)
+{
+ pthread_mutex_t a, b;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+
+ pthread_mutex_lock(&a);
+ pthread_mutex_lock(&b);
+ pthread_mutex_lock(&a);
+}
diff --git a/tools/lib/lockdep/tests/ABBA.c b/tools/lib/lockdep/tests/ABBA.c
new file mode 100644
index 0000000..07f0e29
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABBA.c
@@ -0,0 +1,13 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+ pthread_mutex_t a, b;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+
+ LOCK_UNLOCK_2(a, b);
+ LOCK_UNLOCK_2(b, a);
+}
diff --git a/tools/lib/lockdep/tests/ABBCCA.c b/tools/lib/lockdep/tests/ABBCCA.c
new file mode 100644
index 0000000..843db09
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABBCCA.c
@@ -0,0 +1,15 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+ pthread_mutex_t a, b, c;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+ pthread_mutex_init(&c, NULL);
+
+ LOCK_UNLOCK_2(a, b);
+ LOCK_UNLOCK_2(b, c);
+ LOCK_UNLOCK_2(c, a);
+}
diff --git a/tools/lib/lockdep/tests/ABBCCDDA.c b/tools/lib/lockdep/tests/ABBCCDDA.c
new file mode 100644
index 0000000..33620e2
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABBCCDDA.c
@@ -0,0 +1,17 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+ pthread_mutex_t a, b, c, d;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+ pthread_mutex_init(&c, NULL);
+ pthread_mutex_init(&d, NULL);
+
+ LOCK_UNLOCK_2(a, b);
+ LOCK_UNLOCK_2(b, c);
+ LOCK_UNLOCK_2(c, d);
+ LOCK_UNLOCK_2(d, a);
+}
diff --git a/tools/lib/lockdep/tests/ABCABC.c b/tools/lib/lockdep/tests/ABCABC.c
new file mode 100644
index 0000000..3fee51e
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABCABC.c
@@ -0,0 +1,15 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+ pthread_mutex_t a, b, c;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+ pthread_mutex_init(&c, NULL);
+
+ LOCK_UNLOCK_2(a, b);
+ LOCK_UNLOCK_2(c, a);
+ LOCK_UNLOCK_2(b, c);
+}
diff --git a/tools/lib/lockdep/tests/ABCDBCDA.c b/tools/lib/lockdep/tests/ABCDBCDA.c
new file mode 100644
index 0000000..427ba56
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABCDBCDA.c
@@ -0,0 +1,17 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+ pthread_mutex_t a, b, c, d;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+ pthread_mutex_init(&c, NULL);
+ pthread_mutex_init(&d, NULL);
+
+ LOCK_UNLOCK_2(a, b);
+ LOCK_UNLOCK_2(c, d);
+ LOCK_UNLOCK_2(b, c);
+ LOCK_UNLOCK_2(d, a);
+}
diff --git a/tools/lib/lockdep/tests/ABCDBDDA.c b/tools/lib/lockdep/tests/ABCDBDDA.c
new file mode 100644
index 0000000..680c6cf
--- /dev/null
+++ b/tools/lib/lockdep/tests/ABCDBDDA.c
@@ -0,0 +1,17 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+ pthread_mutex_t a, b, c, d;
+
+ pthread_mutex_init(&a, NULL);
+ pthread_mutex_init(&b, NULL);
+ pthread_mutex_init(&c, NULL);
+ pthread_mutex_init(&d, NULL);
+
+ LOCK_UNLOCK_2(a, b);
+ LOCK_UNLOCK_2(c, d);
+ LOCK_UNLOCK_2(b, d);
+ LOCK_UNLOCK_2(d, a);
+}
diff --git a/tools/lib/lockdep/tests/common.h b/tools/lib/lockdep/tests/common.h
new file mode 100644
index 0000000..d89e94d
--- /dev/null
+++ b/tools/lib/lockdep/tests/common.h
@@ -0,0 +1,12 @@
+#ifndef _LIBLOCKDEP_TEST_COMMON_H
+#define _LIBLOCKDEP_TEST_COMMON_H
+
+#define LOCK_UNLOCK_2(a, b) \
+ do { \
+ pthread_mutex_lock(&(a)); \
+ pthread_mutex_lock(&(b)); \
+ pthread_mutex_unlock(&(b)); \
+ pthread_mutex_unlock(&(a)); \
+ } while(0)
+
+#endif
diff --git a/tools/lib/lockdep/tests/unlock_balance.c b/tools/lib/lockdep/tests/unlock_balance.c
new file mode 100644
index 0000000..0bc62de
--- /dev/null
+++ b/tools/lib/lockdep/tests/unlock_balance.c
@@ -0,0 +1,12 @@
+#include <liblockdep/mutex.h>
+
+void main(void)
+{
+ pthread_mutex_t a;
+
+ pthread_mutex_init(&a, NULL);
+
+ pthread_mutex_lock(&a);
+ pthread_mutex_unlock(&a);
+ pthread_mutex_unlock(&a);
+}
--
1.8.2.1

2013-05-16 03:18:19

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 9/9] liblockdep: Add a MAINTAINERS entry

Signed-off-by: Sasha Levin <[email protected]>
---
MAINTAINERS | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5f5c895..0c822ef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4856,6 +4856,11 @@ F: drivers/lguest/
F: include/linux/lguest*.h
F: tools/lguest/

+LIBLOCK
+M: Sasha Levin <[email protected]>
+S: Maintained
+F: tools/lib/lockdep/
+
LINUX FOR IBM pSERIES (RS/6000)
M: Paul Mackerras <[email protected]>
W: http://www.ibm.com/linux/ltc/projects/ppc
--
1.8.2.1

2013-05-16 03:18:17

by Sasha Levin

[permalink] [raw]
Subject: [PATCH v4 3/9] liblockdep: Add public headers for pthread_mutex_t implementation

These headers provide the same API as their pthread mutex
counterparts.

The design here is to allow to easily switch to liblockdep lock
validation just by adding a "liblockdep_" to pthread_mutex_*()
calls, which means that it's easy to integrate liblockdep into
existing codebases.

Signed-off-by: Sasha Levin <[email protected]>
---
tools/lib/lockdep/include/liblockdep/common.h | 42 ++++++++++++++++
tools/lib/lockdep/include/liblockdep/mutex.h | 70 +++++++++++++++++++++++++++
2 files changed, 112 insertions(+)
create mode 100644 tools/lib/lockdep/include/liblockdep/common.h
create mode 100644 tools/lib/lockdep/include/liblockdep/mutex.h

diff --git a/tools/lib/lockdep/include/liblockdep/common.h b/tools/lib/lockdep/include/liblockdep/common.h
new file mode 100644
index 0000000..1113cad
--- /dev/null
+++ b/tools/lib/lockdep/include/liblockdep/common.h
@@ -0,0 +1,42 @@
+#ifndef _LIBLOCKDEP_COMMON_H
+#define _LIBLOCKDEP_COMMON_H
+
+#include <pthread.h>
+
+#ifndef _THIS_IP_
+#define _RET_IP_ (unsigned long)__builtin_return_address(0)
+#endif
+
+#define NR_LOCKDEP_CACHING_CLASSES 2
+#define MAX_LOCKDEP_SUBCLASSES 8UL
+
+struct lockdep_subclass_key {
+ char __one_byte;
+};
+
+struct lock_class_key {
+ struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES];
+};
+
+struct lockdep_map {
+ struct lock_class_key *key;
+ struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES];
+ const char *name;
+#ifdef CONFIG_LOCK_STAT
+ int cpu;
+ unsigned long ip;
+#endif
+};
+
+void lockdep_init_map(struct lockdep_map *lock, const char *name,
+ struct lock_class_key *key, int subclass);
+void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
+ int trylock, int read, int check,
+ struct lockdep_map *nest_lock, unsigned long ip);
+void lock_release(struct lockdep_map *lock, int nested,
+ unsigned long ip);
+
+#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
+ { .name = (_name), .key = (void *)(_key), }
+
+#endif
diff --git a/tools/lib/lockdep/include/liblockdep/mutex.h b/tools/lib/lockdep/include/liblockdep/mutex.h
new file mode 100644
index 0000000..c342f70
--- /dev/null
+++ b/tools/lib/lockdep/include/liblockdep/mutex.h
@@ -0,0 +1,70 @@
+#ifndef _LIBLOCKDEP_MUTEX_H
+#define _LIBLOCKDEP_MUTEX_H
+
+#include <pthread.h>
+#include "common.h"
+
+struct liblockdep_pthread_mutex {
+ pthread_mutex_t mutex;
+ struct lockdep_map dep_map;
+};
+
+typedef struct liblockdep_pthread_mutex liblockdep_pthread_mutex_t;
+
+#define LIBLOCKDEP_PTHREAD_MUTEX_INITIALIZER(mtx) \
+ (const struct liblockdep_pthread_mutex) { \
+ .mutex = PTHREAD_MUTEX_INITIALIZER, \
+ .dep_map = STATIC_LOCKDEP_MAP_INIT(#mtx, &((&(mtx))->dep_map)), \
+}
+
+static inline int __mutex_init(liblockdep_pthread_mutex_t *lock,
+ const char *name,
+ struct lock_class_key *key,
+ const pthread_mutexattr_t *__mutexattr)
+{
+ lockdep_init_map(&lock->dep_map, name, key, 0);
+ return pthread_mutex_init(&lock->mutex, __mutexattr);
+}
+
+#define liblockdep_pthread_mutex_init(mutex, mutexattr) \
+({ \
+ static struct lock_class_key __key; \
+ \
+ __mutex_init((mutex), #mutex, &__key, (mutexattr)); \
+})
+
+static inline int liblockdep_pthread_mutex_lock(liblockdep_pthread_mutex_t *lock)
+{
+ lock_acquire(&lock->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+ return pthread_mutex_lock(&lock->mutex);
+}
+
+static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock)
+{
+ lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
+ return pthread_mutex_unlock(&lock->mutex);
+}
+
+static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock)
+{
+ lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+ return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0;
+}
+
+static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock)
+{
+ return pthread_mutex_destroy(&lock->mutex);
+}
+
+#ifdef __USE_LIBLOCKDEP
+
+#define pthread_mutex_t liblockdep_pthread_mutex_t
+#define pthread_mutex_init liblockdep_pthread_mutex_init
+#define pthread_mutex_lock liblockdep_pthread_mutex_lock
+#define pthread_mutex_unlock liblockdep_pthread_mutex_unlock
+#define pthread_mutex_trylock liblockdep_pthread_mutex_trylock
+#define pthread_mutex_destroy liblockdep_pthread_mutex_destroy
+
+#endif
+
+#endif
--
1.8.2.1

2013-05-16 07:32:59

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH v4 1/9] lockdep: Be nice about building from userspace

On 05/16/2013 06:15 AM, Sasha Levin wrote:
> Lockdep is an awesome piece of code which detects locking issues
> which are relevant both to userspace and kernelspace. We can
> easily make lockdep work in userspace since there is really no
> kernel spacific magic going on in the code.
>
> All we need is to wrap two functions which are used by lockdep
> and are very kernel specific.
>
> Doing that will allow tools located in tools/ to easily utilize
> lockdep's code for their own use.
>
> Signed-off-by: Sasha Levin <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Link: http://lkml.kernel.org/r/[email protected]
> Signed-off-by: Ingo Molnar <[email protected]>

FWIW:

Acked-by: Pekka Enberg <[email protected]>

to the whole series.

Pekka

2013-05-22 09:14:55

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 2/9] liblockdep: Wrap kernel/lockdep.c to allow usage from userspace

On Wed, May 15, 2013 at 11:15:34PM -0400, Sasha Levin wrote:
> kernel/lockdep.c deals with validating locking scenarios for
> various architectures supported by the kernel. There isn't
> anything kernel specific going on in lockdep, and when we
> compare userspace to other architectures that don't have to deal
> with irqs such as s390, they become all too similar.
>
> We wrap kernel/lockdep.c and include/linux/lockdep.h with
> several headers which allow us to build and use lockdep from
> userspace. We don't touch the kernel code itself which means
> that any work done on lockdep in the kernel will automatically
> benefit userspace lockdep as well!
>
> +
> +#ifndef CALLER_ADDR0
> +#define _THIS_IP_ CALLER_ADDR0
> +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
> +#endif

Why are you making _THIS_IP_ equal to _RET_IP_ ?

2013-05-22 09:17:25

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 2/9] liblockdep: Wrap kernel/lockdep.c to allow usage from userspace

On Wed, May 15, 2013 at 11:15:34PM -0400, Sasha Levin wrote:
> --- /dev/null
> +++ b/tools/lib/lockdep/uinclude/linux/lockdep.h
> @@ -0,0 +1,55 @@
> +#ifndef _LIBLOCKDEP_LOCKDEP_H_
> +#define _LIBLOCKDEP_LOCKDEP_H_
> +
> +#include <sys/prctl.h>
> +#include <sys/syscall.h>
> +#include <string.h>
> +#include <limits.h>
> +#include <linux/utsname.h>
> +
> +
> +#define MAX_LOCK_DEPTH 2000UL
> +
> +#include "../../../include/linux/lockdep.h"
> +
> +struct task_struct {
> + u64 curr_chain_key;
> + int lockdep_depth;
> + unsigned int lockdep_recursion;
> + struct held_lock held_locks[MAX_LOCK_DEPTH];
> + gfp_t lockdep_reclaim_gfp;
> + int pid;
> + char comm[17];
> +};

Whee that's a totally awesome MAX_LOCK_DEPTH.. :-)

Should we not also extend the other static allocations, or have you not
yet ran into them? I would suspect that without proper classes we're
bound to run out of class and link storage quite quickly.

2013-05-22 09:22:44

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] liblockdep: Add public headers for pthread_mutex_t implementation

On Wed, May 15, 2013 at 11:15:35PM -0400, Sasha Levin wrote:
> These headers provide the same API as their pthread mutex
> counterparts.
>
> The design here is to allow to easily switch to liblockdep lock
> validation just by adding a "liblockdep_" to pthread_mutex_*()
> calls, which means that it's easy to integrate liblockdep into
> existing codebases.
>
> Signed-off-by: Sasha Levin <[email protected]>
> ---
> tools/lib/lockdep/include/liblockdep/common.h | 42 ++++++++++++++++
> tools/lib/lockdep/include/liblockdep/mutex.h | 70 +++++++++++++++++++++++++++
> 2 files changed, 112 insertions(+)
> create mode 100644 tools/lib/lockdep/include/liblockdep/common.h
> create mode 100644 tools/lib/lockdep/include/liblockdep/mutex.h
>
> diff --git a/tools/lib/lockdep/include/liblockdep/common.h b/tools/lib/lockdep/include/liblockdep/common.h
> new file mode 100644
> index 0000000..1113cad
> --- /dev/null
> +++ b/tools/lib/lockdep/include/liblockdep/common.h
> @@ -0,0 +1,42 @@
> +#ifndef _LIBLOCKDEP_COMMON_H
> +#define _LIBLOCKDEP_COMMON_H
> +
> +#include <pthread.h>
> +
> +#ifndef _THIS_IP_

#ifndef _RET_IP_ ?

> +#define _RET_IP_ (unsigned long)__builtin_return_address(0)
> +#endif
> +
> +#define NR_LOCKDEP_CACHING_CLASSES 2
> +#define MAX_LOCKDEP_SUBCLASSES 8UL
> +
> +struct lockdep_subclass_key {
> + char __one_byte;
> +};
> +
> +struct lock_class_key {
> + struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES];
> +};
> +
> +struct lockdep_map {
> + struct lock_class_key *key;
> + struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES];
> + const char *name;
> +#ifdef CONFIG_LOCK_STAT
> + int cpu;
> + unsigned long ip;
> +#endif
> +};
> +
> +void lockdep_init_map(struct lockdep_map *lock, const char *name,
> + struct lock_class_key *key, int subclass);
> +void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
> + int trylock, int read, int check,
> + struct lockdep_map *nest_lock, unsigned long ip);
> +void lock_release(struct lockdep_map *lock, int nested,
> + unsigned long ip);
> +
> +#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
> + { .name = (_name), .key = (void *)(_key), }
> +
> +#endif
> diff --git a/tools/lib/lockdep/include/liblockdep/mutex.h b/tools/lib/lockdep/include/liblockdep/mutex.h
> new file mode 100644
> index 0000000..c342f70
> --- /dev/null
> +++ b/tools/lib/lockdep/include/liblockdep/mutex.h
> @@ -0,0 +1,70 @@
> +#ifndef _LIBLOCKDEP_MUTEX_H
> +#define _LIBLOCKDEP_MUTEX_H
> +
> +#include <pthread.h>
> +#include "common.h"
> +
> +struct liblockdep_pthread_mutex {
> + pthread_mutex_t mutex;
> + struct lockdep_map dep_map;
> +};
> +
> +typedef struct liblockdep_pthread_mutex liblockdep_pthread_mutex_t;
> +
> +#define LIBLOCKDEP_PTHREAD_MUTEX_INITIALIZER(mtx) \
> + (const struct liblockdep_pthread_mutex) { \
> + .mutex = PTHREAD_MUTEX_INITIALIZER, \
> + .dep_map = STATIC_LOCKDEP_MAP_INIT(#mtx, &((&(mtx))->dep_map)), \
> +}
> +
> +static inline int __mutex_init(liblockdep_pthread_mutex_t *lock,
> + const char *name,
> + struct lock_class_key *key,
> + const pthread_mutexattr_t *__mutexattr)
> +{
> + lockdep_init_map(&lock->dep_map, name, key, 0);
> + return pthread_mutex_init(&lock->mutex, __mutexattr);
> +}
> +
> +#define liblockdep_pthread_mutex_init(mutex, mutexattr) \
> +({ \
> + static struct lock_class_key __key; \
> + \
> + __mutex_init((mutex), #mutex, &__key, (mutexattr)); \
> +})

OK, so people using liblockdep_pthread_mutex_init() do get the 'normal'
class rules.

> +
> +static inline int liblockdep_pthread_mutex_lock(liblockdep_pthread_mutex_t *lock)
> +{
> + lock_acquire(&lock->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
> + return pthread_mutex_lock(&lock->mutex);
> +}

They will however then also want all the 'normal' lockdep annotations to
deal with that like:

liblockdep_pthread_mutex_lock_nested()
liblockdep_pthread_mutex_lock_nest_lock()

*phew* and here I always though pthread_mutex_* was a long prefix...

Also, the above doesn't have the full lockstat contention hooks -- not
sure that's on purpose or not.

> +
> +static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock)
> +{
> + lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
> + return pthread_mutex_unlock(&lock->mutex);
> +}
> +
> +static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock)
> +{
> + lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
> + return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0;
> +}
> +
> +static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock)
> +{
> + return pthread_mutex_destroy(&lock->mutex);
> +}
> +
> +#ifdef __USE_LIBLOCKDEP
> +
> +#define pthread_mutex_t liblockdep_pthread_mutex_t
> +#define pthread_mutex_init liblockdep_pthread_mutex_init
> +#define pthread_mutex_lock liblockdep_pthread_mutex_lock
> +#define pthread_mutex_unlock liblockdep_pthread_mutex_unlock
> +#define pthread_mutex_trylock liblockdep_pthread_mutex_trylock
> +#define pthread_mutex_destroy liblockdep_pthread_mutex_destroy

Given the liblockdep_* things use 'proper' classes do you want this
mapping? If you do, should we use the same alias nonsense glibc uses or
are CPP macros good enough for us?

2013-05-22 09:23:08

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 5/9] liblockdep: Add public headers for pthread_rwlock_t implementation

On Wed, May 15, 2013 at 11:15:37PM -0400, Sasha Levin wrote:
> Both pthreads and lockdep support dealing with rwlocks, so
> here's the liblockdep implementation for those.

I suppose the same comments as for the pthread_mutex_* case...

2013-05-22 09:25:04

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

On Wed, May 15, 2013 at 11:15:39PM -0400, Sasha Levin wrote:
> +static struct lock_lookup __locks[LIBLOCKDEP_STATIC_ENTRIES];
> +static int __locks_nr;
> +
> +static inline bool is_static_lock(struct lock_lookup *lock)
> +{
> + return lock >= __locks && lock < __locks + ARRAY_SIZE(__locks);
> +}
> +
> +static struct lock_lookup *alloc_lock(void)
> +{
> + if (__init_state != done) {
> + /*
> + * Some programs attempt to initialize and use locks in their
> + * allocation path. This means that a call to malloc() would
> + * result in locks being initialized and locked.
> + *
> + * Why is it an issue for us? dlsym() below will try allocating
> + * to give us the original function. Since this allocation will
> + * result in a locking operations, we have to let pthread deal
> + * with it, but we can't! we don't have the pointer to the
> + * original API since we're inside dlsym() trying to get it
> + */
> +
> + int idx = __locks_nr++;
> + if (idx >= ARRAY_SIZE(__locks)) {
> + fprintf(stderr,
> + "LOCKDEP error: insufficient LIBLOCKDEP_STATIC_ENTRIES\n");
> + exit(EX_UNAVAILABLE);
> + }
> + return __locks + idx;
> + }
> +
> + return malloc(sizeof(struct lock_lookup));
> +}

Do we still need this with the glibc __pthread_* static initialization?

2013-05-22 09:27:19

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

On Wed, May 15, 2013 at 11:15:39PM -0400, Sasha Levin wrote:
> +
> +/* pthread mutex API */
> +
> +#ifdef __GLIBC__
> +extern int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
> +extern int __pthread_mutex_lock(pthread_mutex_t *mutex);
> +extern int __pthread_mutex_trylock(pthread_mutex_t *mutex);
> +extern int __pthread_mutex_unlock(pthread_mutex_t *mutex);
> +extern int __pthread_mutex_destroy(pthread_mutex_t *mutex);
> +#else
> +#define __pthread_mutex_init NULL
> +#define __pthread_mutex_lock NULL
> +#define __pthread_mutex_trylock NULL
> +#define __pthread_mutex_unlock NULL
> +#define __pthread_mutex_destroy NULL
> +#endif
> +static int (*ll_pthread_mutex_init)(pthread_mutex_t *mutex,
> + const pthread_mutexattr_t *attr) = __pthread_mutex_init;
> +static int (*ll_pthread_mutex_lock)(pthread_mutex_t *mutex) = __pthread_mutex_lock;
> +static int (*ll_pthread_mutex_trylock)(pthread_mutex_t *mutex) = __pthread_mutex_trylock;
> +static int (*ll_pthread_mutex_unlock)(pthread_mutex_t *mutex) = __pthread_mutex_unlock;
> +static int (*ll_pthread_mutex_destroy)(pthread_mutex_t *mutex) = __pthread_mutex_destroy;
> +
> +/* pthread rwlock API */
> +
> +#ifdef __GLIBC__
> +extern int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
> +extern int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
> +extern int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
> +extern int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
> +extern int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
> +extern int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
> +extern int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
> +#else
> +#define __pthread_rwlock_init NULL
> +#define __pthread_rwlock_destroy NULL
> +#define __pthread_rwlock_wrlock NULL
> +#define __pthread_rwlock_trywrlock NULL
> +#define __pthread_rwlock_rdlock NULL
> +#define __pthread_rwlock_tryrdlock NULL
> +#define __pthread_rwlock_unlock NULL
> +#endif
> +
> +static int (*ll_pthread_rwlock_init)(pthread_rwlock_t *rwlock,
> + const pthread_rwlockattr_t *attr) = __pthread_rwlock_init;
> +static int (*ll_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock) = __pthread_rwlock_destroy;
> +static int (*ll_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_rdlock;
> +static int (*ll_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_tryrdlock;
> +static int (*ll_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_trywrlock;
> +static int (*ll_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_wrlock;
> +static int (*ll_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_unlock;
> +

> +__attribute__((constructor)) static void init_preload(void)
> +{
> + if (__init_state != done)
> + return;
> +
> +#ifndef __GLIBC__
> + __init_state = prepare;
> +
> + ll_pthread_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init");
> + ll_pthread_mutex_lock = dlsym(RTLD_NEXT, "pthread_mutex_lock");
> + ll_pthread_mutex_trylock = dlsym(RTLD_NEXT, "pthread_mutex_trylock");
> + ll_pthread_mutex_unlock = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
> + ll_pthread_mutex_destroy = dlsym(RTLD_NEXT, "pthread_mutex_destroy");
> +
> + ll_pthread_rwlock_init = dlsym(RTLD_NEXT, "pthread_rwlock_init");
> + ll_pthread_rwlock_destroy = dlsym(RTLD_NEXT, "pthread_rwlock_destroy");
> + ll_pthread_rwlock_rdlock = dlsym(RTLD_NEXT, "pthread_rwlock_rdlock");
> + ll_pthread_rwlock_tryrdlock = dlsym(RTLD_NEXT, "pthread_rwlock_tryrdlock");
> + ll_pthread_rwlock_wrlock = dlsym(RTLD_NEXT, "pthread_rwlock_wrlock");
> + ll_pthread_rwlock_trywrlock = dlsym(RTLD_NEXT, "pthread_rwlock_trywrlock");
> + ll_pthread_rwlock_unlock = dlsym(RTLD_NEXT, "pthread_rwlock_unlock");
> +#endif
> +
> + printf("%p\n", ll_pthread_mutex_trylock);fflush(stdout);
> +
> + lockdep_init();
> +
> + __init_state = done;
> +}

I guess that begs the question do we really want to support !glibc?

2013-05-22 09:27:58

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 8/9] liblockdep: Add the 'lockdep' user-space utility

On Wed, May 15, 2013 at 11:15:40PM -0400, Sasha Levin wrote:
> This is a simple wrapper to make using liblockdep on existing
> applications much easier.
>
> After running 'make && make install', it becomes quite simple to
> test things with liblockdep. For example, to try it on perf:
>
> lockdep perf
>
> No other integration required.
>
> Signed-off-by: Sasha Levin <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: Linus Torvalds <[email protected]>
> Cc: Andrew Morton <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Thomas Gleixner <[email protected]>
> Link: http://lkml.kernel.org/r/[email protected]
> Signed-off-by: Ingo Molnar <[email protected]>

I'm listed twice! :-)

2013-05-28 19:31:26

by Sasha Levin

[permalink] [raw]
Subject: Re: [PATCH v4 2/9] liblockdep: Wrap kernel/lockdep.c to allow usage from userspace

On 05/22/2013 05:17 AM, Peter Zijlstra wrote:
> On Wed, May 15, 2013 at 11:15:34PM -0400, Sasha Levin wrote:
>> --- /dev/null
>> +++ b/tools/lib/lockdep/uinclude/linux/lockdep.h
>> @@ -0,0 +1,55 @@
>> +#ifndef _LIBLOCKDEP_LOCKDEP_H_
>> +#define _LIBLOCKDEP_LOCKDEP_H_
>> +
>> +#include <sys/prctl.h>
>> +#include <sys/syscall.h>
>> +#include <string.h>
>> +#include <limits.h>
>> +#include <linux/utsname.h>
>> +
>> +
>> +#define MAX_LOCK_DEPTH 2000UL
>> +
>> +#include "../../../include/linux/lockdep.h"
>> +
>> +struct task_struct {
>> + u64 curr_chain_key;
>> + int lockdep_depth;
>> + unsigned int lockdep_recursion;
>> + struct held_lock held_locks[MAX_LOCK_DEPTH];
>> + gfp_t lockdep_reclaim_gfp;
>> + int pid;
>> + char comm[17];
>> +};
>
> Whee that's a totally awesome MAX_LOCK_DEPTH.. :-)
>
> Should we not also extend the other static allocations, or have you not
> yet ran into them? I would suspect that without proper classes we're
> bound to run out of class and link storage quite quickly.

I've changed MAX_LOCK_DEPTH just because I've actually hit it. I haven't
got around to hitting anything else, but I guess we could preemptively
send them hight.

What values would make sense here?


Thanks,
Sasha

2013-05-28 19:34:43

by Sasha Levin

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] liblockdep: Add public headers for pthread_mutex_t implementation

On 05/22/2013 05:22 AM, Peter Zijlstra wrote:
> They will however then also want all the 'normal' lockdep annotations to
> deal with that like:
>
> liblockdep_pthread_mutex_lock_nested()
> liblockdep_pthread_mutex_lock_nest_lock()
>
> *phew* and here I always though pthread_mutex_* was a long prefix...
>
> Also, the above doesn't have the full lockstat contention hooks -- not
> sure that's on purpose or not.

I was quietly hoping on leaving this out in the initial version of liblockdep
and start adding this and the rest of the toys that come with lockdep once we
figure out whether this code will go into the kernel tree or not.

Should I be adding them now?

>> +
>> +static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock)
>> +{
>> + lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
>> + return pthread_mutex_unlock(&lock->mutex);
>> +}
>> +
>> +static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock)
>> +{
>> + lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
>> + return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0;
>> +}
>> +
>> +static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock)
>> +{
>> + return pthread_mutex_destroy(&lock->mutex);
>> +}
>> +
>> +#ifdef __USE_LIBLOCKDEP
>> +
>> +#define pthread_mutex_t liblockdep_pthread_mutex_t
>> +#define pthread_mutex_init liblockdep_pthread_mutex_init
>> +#define pthread_mutex_lock liblockdep_pthread_mutex_lock
>> +#define pthread_mutex_unlock liblockdep_pthread_mutex_unlock
>> +#define pthread_mutex_trylock liblockdep_pthread_mutex_trylock
>> +#define pthread_mutex_destroy liblockdep_pthread_mutex_destroy
>
> Given the liblockdep_* things use 'proper' classes do you want this
> mapping? If you do, should we use the same alias nonsense glibc uses or
> are CPP macros good enough for us?
>

I think that this will be good enough for our purpose, why wouldn't these
simple macros be enough?


Thanks,
Sasha

2013-05-28 19:36:28

by Sasha Levin

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

On 05/22/2013 05:24 AM, Peter Zijlstra wrote:
> Do we still need this with the glibc __pthread_* static initialization?

Sadly we do.

We need to deal with the two things that cause allocation in our code (because
they loop back into our code via malloc()->malloc_init()->pthread_mutex_init() ).

The first one was the glibc trick to avoid the malloc() caused by dlsym(). The
second one is the malloc() we do when we allocate new lock. Static allocation
deals with that one.


Thanks,
Sasha

2013-05-28 19:39:31

by Sasha Levin

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

On 05/22/2013 05:27 AM, Peter Zijlstra wrote:
> On Wed, May 15, 2013 at 11:15:39PM -0400, Sasha Levin wrote:
>> +
>> +/* pthread mutex API */
>> +
>> +#ifdef __GLIBC__
>> +extern int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
>> +extern int __pthread_mutex_lock(pthread_mutex_t *mutex);
>> +extern int __pthread_mutex_trylock(pthread_mutex_t *mutex);
>> +extern int __pthread_mutex_unlock(pthread_mutex_t *mutex);
>> +extern int __pthread_mutex_destroy(pthread_mutex_t *mutex);
>> +#else
>> +#define __pthread_mutex_init NULL
>> +#define __pthread_mutex_lock NULL
>> +#define __pthread_mutex_trylock NULL
>> +#define __pthread_mutex_unlock NULL
>> +#define __pthread_mutex_destroy NULL
>> +#endif
>> +static int (*ll_pthread_mutex_init)(pthread_mutex_t *mutex,
>> + const pthread_mutexattr_t *attr) = __pthread_mutex_init;
>> +static int (*ll_pthread_mutex_lock)(pthread_mutex_t *mutex) = __pthread_mutex_lock;
>> +static int (*ll_pthread_mutex_trylock)(pthread_mutex_t *mutex) = __pthread_mutex_trylock;
>> +static int (*ll_pthread_mutex_unlock)(pthread_mutex_t *mutex) = __pthread_mutex_unlock;
>> +static int (*ll_pthread_mutex_destroy)(pthread_mutex_t *mutex) = __pthread_mutex_destroy;
>> +
>> +/* pthread rwlock API */
>> +
>> +#ifdef __GLIBC__
>> +extern int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
>> +extern int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
>> +extern int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
>> +extern int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
>> +extern int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
>> +extern int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
>> +extern int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
>> +#else
>> +#define __pthread_rwlock_init NULL
>> +#define __pthread_rwlock_destroy NULL
>> +#define __pthread_rwlock_wrlock NULL
>> +#define __pthread_rwlock_trywrlock NULL
>> +#define __pthread_rwlock_rdlock NULL
>> +#define __pthread_rwlock_tryrdlock NULL
>> +#define __pthread_rwlock_unlock NULL
>> +#endif
>> +
>> +static int (*ll_pthread_rwlock_init)(pthread_rwlock_t *rwlock,
>> + const pthread_rwlockattr_t *attr) = __pthread_rwlock_init;
>> +static int (*ll_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock) = __pthread_rwlock_destroy;
>> +static int (*ll_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_rdlock;
>> +static int (*ll_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_tryrdlock;
>> +static int (*ll_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_trywrlock;
>> +static int (*ll_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_wrlock;
>> +static int (*ll_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_unlock;
>> +
>
>> +__attribute__((constructor)) static void init_preload(void)
>> +{
>> + if (__init_state != done)
>> + return;
>> +
>> +#ifndef __GLIBC__
>> + __init_state = prepare;
>> +
>> + ll_pthread_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init");
>> + ll_pthread_mutex_lock = dlsym(RTLD_NEXT, "pthread_mutex_lock");
>> + ll_pthread_mutex_trylock = dlsym(RTLD_NEXT, "pthread_mutex_trylock");
>> + ll_pthread_mutex_unlock = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
>> + ll_pthread_mutex_destroy = dlsym(RTLD_NEXT, "pthread_mutex_destroy");
>> +
>> + ll_pthread_rwlock_init = dlsym(RTLD_NEXT, "pthread_rwlock_init");
>> + ll_pthread_rwlock_destroy = dlsym(RTLD_NEXT, "pthread_rwlock_destroy");
>> + ll_pthread_rwlock_rdlock = dlsym(RTLD_NEXT, "pthread_rwlock_rdlock");
>> + ll_pthread_rwlock_tryrdlock = dlsym(RTLD_NEXT, "pthread_rwlock_tryrdlock");
>> + ll_pthread_rwlock_wrlock = dlsym(RTLD_NEXT, "pthread_rwlock_wrlock");
>> + ll_pthread_rwlock_trywrlock = dlsym(RTLD_NEXT, "pthread_rwlock_trywrlock");
>> + ll_pthread_rwlock_unlock = dlsym(RTLD_NEXT, "pthread_rwlock_unlock");
>> +#endif
>> +
>> + printf("%p\n", ll_pthread_mutex_trylock);fflush(stdout);
>> +
>> + lockdep_init();
>> +
>> + __init_state = done;
>> +}
>
> I guess that begs the question do we really want to support !glibc?

I assumed we didn't want to give up on that based on your patch. I think that
if we plan to get it into tools/lib/ we shouldn't give up on that straight away.


Thanks,
Sasha

2013-05-29 11:12:03

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 2/9] liblockdep: Wrap kernel/lockdep.c to allow usage from userspace

On Tue, May 28, 2013 at 03:30:35PM -0400, Sasha Levin wrote:
> On 05/22/2013 05:17 AM, Peter Zijlstra wrote:
> > On Wed, May 15, 2013 at 11:15:34PM -0400, Sasha Levin wrote:
> >> --- /dev/null
> >> +++ b/tools/lib/lockdep/uinclude/linux/lockdep.h
> >> @@ -0,0 +1,55 @@
> >> +#ifndef _LIBLOCKDEP_LOCKDEP_H_
> >> +#define _LIBLOCKDEP_LOCKDEP_H_
> >> +
> >> +#include <sys/prctl.h>
> >> +#include <sys/syscall.h>
> >> +#include <string.h>
> >> +#include <limits.h>
> >> +#include <linux/utsname.h>
> >> +
> >> +
> >> +#define MAX_LOCK_DEPTH 2000UL
> >> +
> >> +#include "../../../include/linux/lockdep.h"
> >> +
> >> +struct task_struct {
> >> + u64 curr_chain_key;
> >> + int lockdep_depth;
> >> + unsigned int lockdep_recursion;
> >> + struct held_lock held_locks[MAX_LOCK_DEPTH];
> >> + gfp_t lockdep_reclaim_gfp;
> >> + int pid;
> >> + char comm[17];
> >> +};
> >
> > Whee that's a totally awesome MAX_LOCK_DEPTH.. :-)
> >
> > Should we not also extend the other static allocations, or have you not
> > yet ran into them? I would suspect that without proper classes we're
> > bound to run out of class and link storage quite quickly.
>
> I've changed MAX_LOCK_DEPTH just because I've actually hit it. I haven't
> got around to hitting anything else, but I guess we could preemptively
> send them hight.
>
> What values would make sense here?

Dunno, I suppose we can deal with that when we hit them ;-)

2013-05-29 11:14:57

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] liblockdep: Add public headers for pthread_mutex_t implementation

On Tue, May 28, 2013 at 03:33:51PM -0400, Sasha Levin wrote:
> On 05/22/2013 05:22 AM, Peter Zijlstra wrote:
> > They will however then also want all the 'normal' lockdep annotations to
> > deal with that like:
> >
> > liblockdep_pthread_mutex_lock_nested()
> > liblockdep_pthread_mutex_lock_nest_lock()
> >
> > *phew* and here I always though pthread_mutex_* was a long prefix...
> >
> > Also, the above doesn't have the full lockstat contention hooks -- not
> > sure that's on purpose or not.
>
> I was quietly hoping on leaving this out in the initial version of liblockdep
> and start adding this and the rest of the toys that come with lockdep once we
> figure out whether this code will go into the kernel tree or not.
>
> Should I be adding them now?

I think you'll need them very quicky one you actually start using this
stuff, but sure, add them when you need them.

> >> +
> >> +static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock)
> >> +{
> >> + lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
> >> + return pthread_mutex_unlock(&lock->mutex);
> >> +}
> >> +
> >> +static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock)
> >> +{
> >> + lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
> >> + return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0;
> >> +}
> >> +
> >> +static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock)
> >> +{
> >> + return pthread_mutex_destroy(&lock->mutex);
> >> +}
> >> +
> >> +#ifdef __USE_LIBLOCKDEP
> >> +
> >> +#define pthread_mutex_t liblockdep_pthread_mutex_t
> >> +#define pthread_mutex_init liblockdep_pthread_mutex_init
> >> +#define pthread_mutex_lock liblockdep_pthread_mutex_lock
> >> +#define pthread_mutex_unlock liblockdep_pthread_mutex_unlock
> >> +#define pthread_mutex_trylock liblockdep_pthread_mutex_trylock
> >> +#define pthread_mutex_destroy liblockdep_pthread_mutex_destroy
> >
> > Given the liblockdep_* things use 'proper' classes do you want this
> > mapping? If you do, should we use the same alias nonsense glibc uses or
> > are CPP macros good enough for us?
> >
>
> I think that this will be good enough for our purpose, why wouldn't these
> simple macros be enough?

Suppose you have a funny someone who added a function called:
pthread_mutex_lock_obj() or somesuch, imagine what the CPP thing will
make of that :-)

Then again, people doing that might deserve what they get ;-)

2013-05-29 11:15:22

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

On Tue, May 28, 2013 at 03:35:36PM -0400, Sasha Levin wrote:
> On 05/22/2013 05:24 AM, Peter Zijlstra wrote:
> > Do we still need this with the glibc __pthread_* static initialization?
>
> Sadly we do.
>
> We need to deal with the two things that cause allocation in our code (because
> they loop back into our code via malloc()->malloc_init()->pthread_mutex_init() ).
>
> The first one was the glibc trick to avoid the malloc() caused by dlsym(). The
> second one is the malloc() we do when we allocate new lock. Static allocation
> deals with that one.

OK.. fair enough

2013-05-29 11:16:35

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] liblockdep: Support using LD_PRELOAD

On Tue, May 28, 2013 at 03:38:38PM -0400, Sasha Levin wrote:
> On 05/22/2013 05:27 AM, Peter Zijlstra wrote:
> > I guess that begs the question do we really want to support !glibc?
>
> I assumed we didn't want to give up on that based on your patch. I think that
> if we plan to get it into tools/lib/ we shouldn't give up on that straight away.

Ok, I suppose we'll find out quickly when people are trying this with
other libc implementations and creative malloc/lock setups.