implement pthread mutex deadlock detection

this works by building a directed graph of acquired
pthread mutexes and making sure there are no loops in
that graph.

this feature is enabled with:

    setprop debug.libc.pthread 1

when a potential deadlock is detected, a large warning is
output to the log with appropriate back traces.

currently disabled at compile-time. set PTHREAD_DEBUG_ENABLED=1
to enable.

Change-Id: I916eed2319599e8aaf8f229d3f18a8ddbec3aa8a
diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c
index 3a74375..16d4998 100644
--- a/libc/bionic/pthread.c
+++ b/libc/bionic/pthread.c
@@ -51,6 +51,9 @@
 #include <stdio.h>
 #include <bionic_pthread.h>
 
+extern void pthread_debug_mutex_lock_check(pthread_mutex_t *mutex);
+extern void pthread_debug_mutex_unlock_check(pthread_mutex_t *mutex);
+
 extern int  __pthread_clone(int (*fn)(void*), void *child_stack, int flags, void *arg);
 extern void _exit_with_stack_teardown(void * stackBase, int stackSize, int retCode);
 extern void _exit_thread(int  retCode);
@@ -897,20 +900,6 @@
     return 0;
 }
 
-int pthread_mutex_destroy(pthread_mutex_t *mutex)
-{
-    int ret;
-
-    /* use trylock to ensure that the mutex value is
-     * valid and is not already locked. */
-    ret = pthread_mutex_trylock(mutex);
-    if (ret != 0)
-        return ret;
-
-    mutex->value = 0xdead10cc;
-    return 0;
-}
-
 
 /*
  * Lock a non-recursive mutex.
@@ -1073,7 +1062,8 @@
     _normal_unlock(&__recursive_lock, 0);
 }
 
-int pthread_mutex_lock(pthread_mutex_t *mutex)
+__LIBC_HIDDEN__
+int pthread_mutex_lock_impl(pthread_mutex_t *mutex)
 {
     int mvalue, mtype, tid, new_lock_type, shared;
 
@@ -1134,8 +1124,21 @@
     return 0;
 }
 
+int pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+    int err = pthread_mutex_lock_impl(mutex);
+#ifdef PTHREAD_DEBUG
+    if (PTHREAD_DEBUG_ENABLED) {
+        if (!err) {
+            pthread_debug_mutex_lock_check(mutex);
+        }
+    }
+#endif
+    return err;
+}
 
-int pthread_mutex_unlock(pthread_mutex_t *mutex)
+__LIBC_HIDDEN__
+int pthread_mutex_unlock_impl(pthread_mutex_t *mutex)
 {
     int mvalue, mtype, tid, oldv, shared;
 
@@ -1175,8 +1178,18 @@
     return 0;
 }
 
+int pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+#ifdef PTHREAD_DEBUG
+    if (PTHREAD_DEBUG_ENABLED) {
+        pthread_debug_mutex_unlock_check(mutex);
+    }
+#endif
+    return pthread_mutex_unlock_impl(mutex);
+}
 
-int pthread_mutex_trylock(pthread_mutex_t *mutex)
+__LIBC_HIDDEN__
+int pthread_mutex_trylock_impl(pthread_mutex_t *mutex)
 {
     int mvalue, mtype, tid, oldv, shared;
 
@@ -1219,6 +1232,18 @@
     return 0;
 }
 
+int pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+    int err = pthread_mutex_trylock_impl(mutex);
+#ifdef PTHREAD_DEBUG
+    if (PTHREAD_DEBUG_ENABLED) {
+        if (!err) {
+            pthread_debug_mutex_lock_check(mutex);
+        }
+    }
+#endif
+    return err;
+}
 
 /* initialize 'ts' with the difference between 'abstime' and the current time
  * according to 'clock'. Returns -1 if abstime already expired, or 0 otherwise.
@@ -1254,7 +1279,8 @@
     }
 }
 
-int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs)
+__LIBC_HIDDEN__
+int pthread_mutex_lock_timeout_np_impl(pthread_mutex_t *mutex, unsigned msecs)
 {
     clockid_t        clock = CLOCK_MONOTONIC;
     struct timespec  abstime;
@@ -1339,6 +1365,35 @@
     return 0;
 }
 
+int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs)
+{
+    int err = pthread_mutex_lock_timeout_np_impl(mutex, msecs);
+#ifdef PTHREAD_DEBUG
+    if (PTHREAD_DEBUG_ENABLED) {
+        if (!err) {
+            pthread_debug_mutex_lock_check(mutex);
+        }
+    }
+#endif
+    return err;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+    int ret;
+
+    /* use trylock to ensure that the mutex value is
+     * valid and is not already locked. */
+    ret = pthread_mutex_trylock_impl(mutex);
+    if (ret != 0)
+        return ret;
+
+    mutex->value = 0xdead10cc;
+    return 0;
+}
+
+
+
 int pthread_condattr_init(pthread_condattr_t *attr)
 {
     if (attr == NULL)