[asterisk-commits] russell: branch russell/locks r306954 - in /team/russell/locks: build_tools/ ...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Feb 8 12:29:21 CST 2011


Author: russell
Date: Tue Feb  8 12:29:16 2011
New Revision: 306954

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=306954
Log:
Save off progress on lock inversion detection.

This is just a random project I started on a plane ride.  I still have
plenty to write.  This is just what I was able to get down before my
battery died.

Modified:
    team/russell/locks/build_tools/cflags.xml
    team/russell/locks/include/asterisk/lock.h
    team/russell/locks/main/lock.c
    team/russell/locks/main/utils.c

Modified: team/russell/locks/build_tools/cflags.xml
URL: http://svnview.digium.com/svn/asterisk/team/russell/locks/build_tools/cflags.xml?view=diff&rev=306954&r1=306953&r2=306954
==============================================================================
--- team/russell/locks/build_tools/cflags.xml (original)
+++ team/russell/locks/build_tools/cflags.xml Tue Feb  8 12:29:16 2011
@@ -2,6 +2,9 @@
 		<member name="DONT_OPTIMIZE" displayname="Disable Optimizations by the Compiler">
 		</member>
 		<member name="DEBUG_THREADS" displayname="Enable Thread Debugging">
+		</member>
+		<member name="LOCK_VALIDATION" displayname="Enable Locking Order Validation">
+			<depend>DEBUG_THREADS</depend>
 		</member>
 		<member name="STATIC_BUILD" displayname="Build static binaries">
 		</member>

Modified: team/russell/locks/include/asterisk/lock.h
URL: http://svnview.digium.com/svn/asterisk/team/russell/locks/include/asterisk/lock.h?view=diff&rev=306954&r1=306953&r2=306954
==============================================================================
--- team/russell/locks/include/asterisk/lock.h (original)
+++ team/russell/locks/include/asterisk/lock.h Tue Feb  8 12:29:16 2011
@@ -91,11 +91,11 @@
 #define AST_LOCK_TRACK_INIT_VALUE { { NULL }, { 0 }, 0, { NULL }, { 0 }, PTHREAD_MUTEX_INIT_VALUE }
 #endif
 
-#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, NULL, 1 }
-#define AST_MUTEX_INIT_VALUE_NOTRACKING { PTHREAD_MUTEX_INIT_VALUE, NULL, 0 }
-
-#define AST_RWLOCK_INIT_VALUE { __AST_RWLOCK_INIT_VALUE, NULL, 1 }
-#define AST_RWLOCK_INIT_VALUE_NOTRACKING { __AST_RWLOCK_INIT_VALUE, NULL, 0 }
+#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, NULL, 1, 1 }
+#define AST_MUTEX_INIT_VALUE_NOTRACKING { PTHREAD_MUTEX_INIT_VALUE, NULL, 0, 1 }
+
+#define AST_RWLOCK_INIT_VALUE { __AST_RWLOCK_INIT_VALUE, NULL, 1, 1 }
+#define AST_RWLOCK_INIT_VALUE_NOTRACKING { __AST_RWLOCK_INIT_VALUE, NULL, 0, 1 }
 
 #define AST_MAX_REENTRANCY 10
 
@@ -111,6 +111,7 @@
 	struct ast_bt backtrace[AST_MAX_REENTRANCY];
 #endif
 	pthread_mutex_t reentr_mutex;
+	uint64_t hash;
 };
 
 /*! \brief Structure for mutex and tracking information.
@@ -123,6 +124,8 @@
 	/*! Track which thread holds this mutex */
 	struct ast_lock_track *track;
 	unsigned int tracking:1;
+	/*! Set to 1 if this lock was defined statically */
+	unsigned int static_lock:1;
 };
 
 /*! \brief Structure for rwlock and tracking information.
@@ -135,6 +138,8 @@
 	/*! Track which thread holds this lock */
 	struct ast_lock_track *track;
 	unsigned int tracking:1;
+	/*! Set to 1 if this lock was defined statically */
+	unsigned int static_lock:1;
 };
 
 typedef struct ast_mutex_info ast_mutex_t;
@@ -475,6 +480,20 @@
 	}
 }
 
+#if defined(LOCK_VALIDATION)
+
+void ast_lock_class_init_mutex(ast_mutex_t *lock, const char *file,
+		const char *func, int line);
+
+void ast_lock_class_init_rwlock(ast_rwlock_t *lock_addr, const char *file,
+		const char *func, int line);
+
+void ast_lock_validation_mutex_lock(ast_mutex_t *mutex);
+
+void ast_lock_validation_rwlock_lock(ast_rwlock_t *rwlock);
+
+#endif /* LOCK_VALIDATION */
+
 #else /* !DEBUG_THREADS */
 
 #define	CHANNEL_DEADLOCK_AVOIDANCE(chan) \

Modified: team/russell/locks/main/lock.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/locks/main/lock.c?view=diff&rev=306954&r1=306953&r2=306954
==============================================================================
--- team/russell/locks/main/lock.c (original)
+++ team/russell/locks/main/lock.c Tue Feb  8 12:29:16 2011
@@ -62,6 +62,10 @@
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
 	ast_reentrancy_init(&t->track);
+
+#ifdef LOCK_VALIDATION
+	ast_lock_class_init_mutex(t, filename, func, lineno);
+#endif /* LOCK_VALIDATION */
 #endif /* DEBUG_THREADS */
 
 	pthread_mutexattr_init(&attr);
@@ -195,6 +199,11 @@
 		ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t);
 #endif
 	}
+
+#if defined(LOCK_VALIDATION)
+	ast_lock_validation_mutex_lock(t);
+#endif /* LOCK_VALIDATION */
+
 #endif /* DEBUG_THREADS */
 
 #if defined(DETECT_DEADLOCKS) && defined(DEBUG_THREADS)
@@ -425,6 +434,7 @@
 		ast_remove_lock_info(t);
 #endif
 	}
+
 #endif /* DEBUG_THREADS */
 
 	res = pthread_mutex_unlock(&t->mutex);
@@ -700,6 +710,10 @@
 	if ((t->tracking = tracking)) {
 		ast_reentrancy_init(&t->track);
 	}
+
+#ifdef LOCK_VALIDATION
+	ast_lock_class_init_rwlock(t, filename, func, lineno);
+#endif /* LOCK_VALIDATION */
 #endif /* DEBUG_THREADS */
 
 	pthread_rwlockattr_init(&attr);
@@ -827,6 +841,7 @@
 		ast_remove_lock_info(t);
 #endif
 	}
+
 #endif /* DEBUG_THREADS */
 
 	res = pthread_rwlock_unlock(&t->lock);
@@ -886,6 +901,11 @@
 		ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t);
 #endif
 	}
+
+#if defined(LOCK_VALIDATION)
+	ast_lock_validation_rwlock_lock(t);
+#endif /* LOCK_VALIDATION */
+
 #endif /* DEBUG_THREADS */
 
 #if defined(DETECT_DEADLOCKS) && defined(DEBUG_THREADS)
@@ -1005,6 +1025,11 @@
 		ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t);
 #endif
 	}
+
+#if defined(LOCK_VALIDATION)
+	ast_lock_validation_rwlock_lock(t);
+#endif /* LOCK_VALIDATION */
+
 #endif /* DEBUG_THREADS */
 
 #if defined(DETECT_DEADLOCKS) && defined(DEBUG_THREADS)

Modified: team/russell/locks/main/utils.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/locks/main/utils.c?view=diff&rev=306954&r1=306953&r2=306954
==============================================================================
--- team/russell/locks/main/utils.c (original)
+++ team/russell/locks/main/utils.c Tue Feb  8 12:29:16 2011
@@ -51,6 +51,7 @@
 #include "asterisk/sha1.h"
 #include "asterisk/cli.h"
 #include "asterisk/linkedlists.h"
+#include "asterisk/astobj2.h"
 
 #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
 #include "asterisk/strings.h"
@@ -492,15 +493,196 @@
 
 #ifdef DEBUG_THREADS
 
-/*! \brief A reasonable maximum number of locks a thread would be holding ... */
-#define AST_MAX_LOCKS 64
-
 /* Allow direct use of pthread_mutex_t and friends */
 #undef pthread_mutex_t
 #undef pthread_mutex_lock
 #undef pthread_mutex_unlock
 #undef pthread_mutex_init
 #undef pthread_mutex_destroy
+
+#if defined(LOCK_VALIDATION)
+
+struct lock_class {
+	uint64_t hash;
+	char file[128];
+	char func[128];
+	int line;
+	unsigned int static_lock;
+	struct ao2_container *locks_before;
+	struct ao2_container *locks_after;
+};
+
+static struct ao2_container *lock_classes;
+
+struct lock_validation {
+	uint64_t hash;
+	/* data? */
+};
+
+static struct ao2_container *lock_validations;
+
+/*!
+ * \brief Set to 1 once the lock validation system is initialized.
+ */
+static unsigned int lock_validation_init;
+
+static const char LV_LOG_LEVEL_NAME[] = "LOCK";
+
+static int lv_log_level;
+
+static int lock_class_hash(const void *obj, const int flags)
+{
+	const struct lock_class *lc = obj;
+
+	return lc->hash;
+}
+
+static int lock_class_cmp(void *obj, void *arg, int flags)
+{
+	const struct lock_class *lc1 = obj;
+	const struct lock_class *lc2 = arg;
+
+	return (lc1->hash == lc2->hash) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int lock_valid_hash(const void *obj, const int flags)
+{
+	const struct lock_validation *lv = obj;
+
+	return lv->hash;
+}
+
+static int lock_valid_cmp(void *obj, void *arg, int flags)
+{
+	const struct lock_validation *lv1 = obj;
+	const struct lock_validation *lv2 = arg;
+
+	return (lv1->hash == lv2->hash) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int lock_class_init(const char *file, const char *func, int line,
+		uint64_t *hash)
+{
+	struct lock_class *lc;
+	struct lock_class tmp_lc;
+
+	tmp_lc.hash = ast_str_hash_add(file, ast_str_hash(func)) + line;
+
+	if ((lc = ao2_find(lock_classes, &tmp_lc, OBJ_POINTER))) {
+		ast_log_dynamic_level(lv_log_level,
+				"Lock initialized twice or hash collision: %s:%d in %s\n",
+				func, line, file);
+		ao2_ref(lc, -1);
+		return -1;
+	}
+
+	if (!(lc = ao2_alloc(sizeof(*lc), NULL))) {
+		return -1;
+	}
+
+	lc->hash = tmp_lc.hash;
+	ast_copy_string(lc->file, file, sizeof(lc->file));
+	ast_copy_string(lc->func, func, sizeof(lc->func));
+	lc->line = line;
+
+	ao2_link(lock_classes, lc);
+
+	ao2_ref(lc, -1);
+
+	return 0;
+}
+
+void ast_lock_class_init_mutex(ast_mutex_t *lock, const char *file,
+		const char *func, int line)
+{
+	if (lock_class_init(file, func, line, &lock->track->hash)) {
+		ast_log_dynamic_level(lv_log_level,
+				"ERROR: Failed to initialize lock class.\n");
+	}
+}
+
+void ast_lock_class_init_rwlock(ast_rwlock_t *lock, const char *file,
+		const char *func, int line)
+{
+	if (lock_class_init(file, func, line, &lock->track->hash)) {
+		ast_log_dynamic_level(lv_log_level,
+				"ERROR: Failed to initialize lock class.\n");
+	}
+}
+
+/*!
+ * \brief detect lock inversion
+ * \internal
+ *
+ * \param hash the hash of the lock being acquired
+ * 
+ * On every lock operation:
+ *
+ * 1) Have we validated this lock set already?
+ *
+ *    lock validations are hashed on:
+ *       + current lock hash
+ *       + hash of all locks currently held
+ *       + hash of all locks previously held after
+ *
+ *    If so, just return.
+ *
+ * 2) set(locks previously held before) = 
+ *        set(locks previously held before) U set(locks currently held)
+ *
+ * 3) Check for an intersection between the set of locks held before
+ *    this one and the set of locks held after this one.  Lock inversion
+ *    has been detected if the result is not a null set.
+ */
+static void lock_validate(struct ast_lock_track *track, unsigned int static_lock)
+{
+	struct lock_class *lc;
+	struct lock_class tmp_lc;
+	//uint64_t hash_sum;
+
+	tmp_lc.hash = track->hash;
+
+	if (!(lc = ao2_find(lock_classes, &tmp_lc, OBJ_POINTER))) {
+		/*
+		 * We didn't find a match on the hash value in this lock.
+		 * If it's a static lock, it means the lock class has not
+		 * yet been initialized.  Otherwise, it's an error.
+		 */
+		if (!static_lock) {
+			ast_log_dynamic_level(lv_log_level,
+					"No lock class for non-static lock\n");
+			return;
+		}
+
+		if (!(lc = ao2_alloc(sizeof(*lc), NULL))) {
+			return;
+		}
+
+		lc->hash = (uint64_t) track;
+
+		ao2_link(lock_classes, lc);
+	}
+
+	/* XXX */
+
+	ao2_ref(lc, -1);
+	lc = NULL;
+}
+
+void ast_lock_validation_mutex_lock(ast_mutex_t *mutex)
+{
+	lock_validate(mutex->track, mutex->static_lock);
+}
+
+void ast_lock_validation_rwlock_lock(ast_rwlock_t *rwlock)
+{
+	lock_validate(rwlock->track, rwlock->static_lock);
+}
+
+#endif /* LOCK_VALIDATION */
+
+/*! \brief A reasonable maximum number of locks a thread would be holding ... */
+#define AST_MAX_LOCKS 64
 
 /*! 
  * \brief Keep track of which locks a thread holds 
@@ -1976,12 +2158,41 @@
 #ifdef HAVE_DEV_URANDOM
 	dev_urandom_fd = open("/dev/urandom", O_RDONLY);
 #endif
+
 	base64_init();
+
 #ifdef DEBUG_THREADS
+
+#if defined(LOCK_VALIDATION)
+	if (!(lock_classes = ao2_container_alloc(517, lock_class_hash,
+					lock_class_cmp))) {
+		return -1;
+	}
+
+	if (!(lock_validations = ao2_container_alloc(517, lock_valid_hash,
+					lock_valid_cmp))) {
+		ao2_ref(lock_classes, -1);
+		lock_classes = NULL;
+		return -1;
+	}
+
+	if ((lv_log_level = ast_logger_register_level(LV_LOG_LEVEL_NAME)) == -1) {
+		ao2_ref(lock_classes, -1);
+		lock_classes = NULL;
+		ao2_ref(lock_validations, -1);
+		lock_validations = NULL;
+		return -1;
+	}
+
+	lock_validation_init = 1;
+#endif /* LOCK_VALIDATION */
+
 #if !defined(LOW_MEMORY)
 	ast_cli_register_multiple(utils_cli, ARRAY_LEN(utils_cli));
-#endif
-#endif
+#endif /* !LOW_MEMORY */
+
+#endif /* DEBUG_THREADS */
+
 	return 0;
 }
 




More information about the asterisk-commits mailing list