add initial cocci instrumentation demo

This doesn't yet fix anything, but adds enough basics
to show what is intended.

Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org>
diff --git a/Makefile b/Makefile
index 122bbfb..4eb4364 100644
--- a/Makefile
+++ b/Makefile
@@ -5,3 +5,7 @@
 
 clean:
 	rm -f main
+
+coccicheck:
+
+	pycocci fix-lock.cocci ./
diff --git a/README b/README
new file mode 100644
index 0000000..9dc0bbf
--- /dev/null
+++ b/README
@@ -0,0 +1,61 @@
+ cocci-tact: Demo for instrumenting with Coccinelle
+====================================================
+
+This is a basic demo to show how one can do instrumentation with Coccinelle.
+
+ The problem
+=============
+
+Some Linux kernel debugging features often require adding a lot of debug data
+structures to a source code repository, often this is addressed with
+CONFIG_DEBUG_* features but sometimes maintaining this upstream on Linux is not
+really welcomed due to how intrusive your changes may be or the difficulty
+in maintaining it properly. Sometimes such code can simply increase the
+complexity of what developers see, and can often riddle developers how to
+address its use / extending it / or replacing your instrumentation code.
+
+ Instrumenting with Coccinelle
+===============================
+
+Coccinelle enables a possible alternative: modify upstream code only when
+you need it, for a throw away debugging kernel. Your instrumentation then
+can be compartamentalized as much as possible upstream, and changes to
+existing code kept separate, outside of what developers see.
+
+This demo illustrates adding some form of instrumentation basic templates
+to code in userspace by trying to take advantage of a simple line which
+would be assumed to be upstream: on code pthread_mutex_protects_3().
+
+In this case pthread_mutex_protects_3() provides Coccinelle with hints
+over what data structures a mutex protects. It is the goal of the
+instrumentation mechanisms being developed here to either fix or
+debug this code without affecting readability for users or average
+developers of the code.
+
+This demo is written in userspace to help facilitate testing of ideas.
+Instrumentation ideas are not yet complete but its the hope this provides
+enough examples to show how this sort of work might look like. Locking
+would just be one area that could use this. There are obviously other
+domains that could benefit from this.
+
+ Requirements
+==============
+
+  * Coccinelle >= 1.0.2
+  * gcc
+  * make
+
+ Usage
+=======
+
+To see the problem run:
+
+	make
+	./main
+
+To see what the instrumentation does:
+
+	make coccicheck
+	git diff
+
+This doesn't do anything quite useful just yet.
diff --git a/fix-lock.cocci b/fix-lock.cocci
new file mode 100644
index 0000000..7ea7f7c
--- /dev/null
+++ b/fix-lock.cocci
@@ -0,0 +1,159 @@
+@ find_drv_name @
+identifier drv;
+identifier mutex;
+@@
+
+pthread_mutex_protects_3(&drv->mutex, ...);
+
+@ find_hint @
+type T;
+T *drv;
+identifier mutex, item_1, item_2, item_3;
+@@
+
+pthread_mutex_protects_3(&drv->mutex, drv->item_1, drv->item_2, drv->item_3);
+
+@ add_instrumentation_vars @
+type find_hint.T;
+identifier find_hint.mutex, find_hint.item_1, find_hint.item_2, find_hint.item_3;
+type T1, T2;
+fresh identifier instr_item_1 = "__instr_" ## item_1;
+fresh identifier instr_item_2 = "__instr_" ## item_2;
+fresh identifier instr_item_3 = "__instr_" ## item_3;
+@@
+
+T {
+	...
++	int instr_item_1;
+	T1 item_1;
+	...
++	int instr_item_2;
+	T2 item_2;
+	...
++	int instr_item_3;
+	T2 item_3;
+	...
+};
+
+@ add_counter depends on add_instrumentation_vars @
+type find_hint.T;
+identifier find_hint.mutex, find_hint.item_1, find_hint.item_2, find_hint.item_3;
+identifier find_drv_name.drv;
+fresh identifier fn_instr = "__instr_" ## mutex;
+@@
+
+#include <string.h>
++
++T;
++static void fn_instr(T *drv)
++{
++}
++
+
+@ find_pthread_fn depends on find_hint @
+identifier fn, ret;
+expression thread, attr, val;
+@@
+
+ret = pthread_create(thread, attr, fn, val);
+
+@ check_fn_access @
+identifier find_pthread_fn.fn;
+type find_hint.T;
+T *drv;
+//identifier find_drv_name.drv;
+identifier add_counter.fn_instr;
+identifier find_hint.mutex;
+identifier find_hint.item_1;
+identifier find_hint.item_2;
+identifier find_hint.item_3;
+@@
+
+fn (...)
+{
+	<+...
+(
+	drv->item_1
+|
+	drv->item_2
+|
+	drv->item_3
+)
+        ...+>
++
++	/* top level routine accesses drv */
++	fn_instr(drv);
++
+}
+
+@ check_helpers depends on find_pthread_fn @
+identifier helper;
+identifier find_pthread_fn.fn;
+identifier find_hint.mutex;
+type find_hint.T;
+T *drv;
+@@
+
+fn (...)
+{
+	<+... when != pthread_mutex_lock(&drv->mutex);
++	/* going to check this routine */
+	helper(...);
+	...+>
+}
+
+@ helper_accessing_with_lock exists @
+identifier check_helpers.helper;
+type find_hint.T;
+T *drv;
+identifier find_hint.mutex;
+identifier find_hint.item_1;
+identifier find_hint.item_2;
+identifier find_hint.item_3;
+position p1, p2;
+identifier add_counter.fn_instr;
+@@
+
+helper(...)
+{
+	...
+	pthread_mutex_lock@p1(&drv->mutex);
+	<+...
+(
+	drv->item_1
+|
+	drv->item_2
+|
+	drv->item_3
+)
+        ...+>
+	pthread_mutex_unlock@p2(&drv->mutex);
++	/* routine had a lock */
++	fn_instr(drv);
+}
+
+@ helper_accessing_without_lock exists @
+identifier check_helpers.helper;
+type find_hint.T;
+T *drv;
+identifier find_hint.mutex;
+identifier find_hint.item_1;
+identifier find_hint.item_2;
+identifier find_hint.item_3;
+identifier add_counter.fn_instr;
+@@
+
+helper(...)
+{
+	<+... when != pthread_mutex_lock(&drv->mutex);
+(
+	drv->item_1
+|
+	drv->item_2
+|
+	drv->item_3
+)
+        ...+>
++	/* was missing lock */
++	fn_instr(drv);
+}