summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_witness.c
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2008-05-07 21:41:36 +0000
committerattilio <attilio@FreeBSD.org>2008-05-07 21:41:36 +0000
commit0ce490cd030083500502567e22b65997c9131cd6 (patch)
tree0b99f8a74ea5c3904237d33a0522aba5e7292462 /sys/kern/subr_witness.c
parent57e87ccecba1cf8105d49f6403df79e7ec9960ed (diff)
downloadFreeBSD-src-0ce490cd030083500502567e22b65997c9131cd6.zip
FreeBSD-src-0ce490cd030083500502567e22b65997c9131cd6.tar.gz
Add a new witness sysctl which returns the relations between any lock
and its children in the form: "parent","child" so that head and bottom of an oriented graph can be easilly detected and various form of diagrams can be build. The sysctl is called debug.witness.graphs and it is read-only; in order to get the list of relations, a simple: #sysctl debug.witness.graphs will do the trick. This approach has been choosen in order to support easilly things like the DOT format and such. Soon, an auto-explicative awk script, which filters simple informations returned by the sysctl and converts them into a real DOT script, will be committed to the repository between examples. Discussed with: rwatson
Diffstat (limited to 'sys/kern/subr_witness.c')
-rw-r--r--sys/kern/subr_witness.c58
1 files changed, 58 insertions, 0 deletions
diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c
index f54e087..d2e84a5 100644
--- a/sys/kern/subr_witness.c
+++ b/sys/kern/subr_witness.c
@@ -98,6 +98,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
@@ -124,6 +125,7 @@ __FBSDID("$FreeBSD$");
#define WITNESS_COUNT 1024
#define WITNESS_CHILDCOUNT (WITNESS_COUNT * 4)
+#define WITNESS_SBUFSIZE 32768
/*
* XXX: This is somewhat bogus, as we assume here that at most 1024 threads
* will hold LOCK_NCHILDREN * 2 locks. We handle failure ok, and we should
@@ -214,7 +216,9 @@ static int isitmydescendant(struct witness *parent, struct witness *child);
static int itismychild(struct witness *parent, struct witness *child);
static void removechild(struct witness *parent, struct witness *child);
static int sysctl_debug_witness_watch(SYSCTL_HANDLER_ARGS);
+static int sysctl_debug_witness_graphs(SYSCTL_HANDLER_ARGS);
static const char *fixup_filename(const char *file);
+static void witness_addgraph(struct sbuf *sb, struct witness *w);
static struct witness *witness_get(void);
static void witness_free(struct witness *m);
static struct witness_child_list_entry *witness_child_get(void);
@@ -247,6 +251,8 @@ static int witness_watch = 1;
TUNABLE_INT("debug.witness.watch", &witness_watch);
SYSCTL_PROC(_debug_witness, OID_AUTO, watch, CTLFLAG_RW | CTLTYPE_INT, NULL, 0,
sysctl_debug_witness_watch, "I", "witness is watching lock operations");
+SYSCTL_PROC(_debug_witness, OID_AUTO, graphs, CTLTYPE_STRING | CTLFLAG_RD,
+ NULL, 0, sysctl_debug_witness_graphs, "A", "Show locks relation graphs");
#ifdef KDB
/*
@@ -619,6 +625,39 @@ sysctl_debug_witness_watch(SYSCTL_HANDLER_ARGS)
return (0);
}
+static int
+sysctl_debug_witness_graphs(SYSCTL_HANDLER_ARGS)
+{
+ struct witness *w;
+ struct sbuf *sb;
+ int error;
+
+ KASSERT(witness_cold == 0, ("%s: witness is still cold", __func__));
+
+ sb = sbuf_new(NULL, NULL, WITNESS_SBUFSIZE, SBUF_FIXEDLEN);
+ if (sb == NULL)
+ return (ENOMEM);
+
+ mtx_lock_spin(&w_mtx);
+ STAILQ_FOREACH(w, &w_all, w_list)
+ w->w_displayed = 0;
+ STAILQ_FOREACH(w, &w_all, w_list)
+ witness_addgraph(sb, w);
+ mtx_unlock_spin(&w_mtx);
+
+ if (sbuf_overflowed(sb)) {
+ sbuf_delete(sb);
+ panic("%s: sbuf overflowed, bump the static buffer size\n",
+ __func__);
+ }
+
+ sbuf_finish(sb);
+ error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
+ sbuf_delete(sb);
+
+ return (error);
+}
+
void
witness_init(struct lock_object *lock)
{
@@ -792,6 +831,25 @@ witness_display_list(void(*prnt)(const char *fmt, ...),
}
static void
+witness_addgraph(struct sbuf *sb, struct witness *parent)
+{
+ struct witness_child_list_entry *wcl;
+ int i;
+
+ if (parent->w_displayed != 0 || parent->w_refcount == 0 ||
+ parent->w_file == NULL)
+ return;
+
+ parent->w_displayed = 1;
+ for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next)
+ for (i = 0; i < wcl->wcl_count; i++) {
+ sbuf_printf(sb, "\"%s\",\"%s\"\n", parent->w_name,
+ wcl->wcl_children[i]->w_name);
+ witness_addgraph(sb, wcl->wcl_children[i]);
+ }
+}
+
+static void
witness_display(void(*prnt)(const char *fmt, ...))
{
struct witness *w;
OpenPOWER on IntegriCloud