summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mutrace.c155
-rwxr-xr-xmutrace.in24
2 files changed, 150 insertions, 29 deletions
diff --git a/mutrace.c b/mutrace.c
index 8d5648c..03a0ca4 100644
--- a/mutrace.c
+++ b/mutrace.c
@@ -38,6 +38,7 @@
#include <sched.h>
#include <malloc.h>
#include <signal.h>
+#include <math.h>
#if !defined (__linux__) || !defined(__GLIBC__)
#error "This stuff only works on Linux!"
@@ -146,6 +147,44 @@ static volatile bool threads_existing = false;
static uint64_t nsec_timestamp_setup;
+/* Must be kept in sync with summary_order_details. */
+typedef enum {
+ ORDER_ID = 0,
+ ORDER_N_LOCKED,
+ ORDER_N_OWNER_CHANGED,
+ ORDER_N_CONTENDED,
+ ORDER_NSEC_LOCKED_TOTAL,
+ ORDER_NSEC_LOCKED_MAX,
+ ORDER_NSEC_LOCKED_AVG,
+ ORDER_NSEC_CONTENDED_TOTAL,
+ ORDER_NSEC_CONTENDED_AVG,
+ ORDER_NSEC_READ_CONTENDED_TOTAL,
+} SummaryOrder;
+#define ORDER_INVALID ORDER_NSEC_READ_CONTENDED_TOTAL + 1 /* first invalid order */
+
+typedef struct {
+ const char *command; /* as passed to --order command line argument */
+ const char *ui_string; /* as displayed by show_summary() */
+} SummaryOrderDetails;
+
+/* Must be kept in sync with SummaryOrder. */
+static const SummaryOrderDetails summary_order_details[] = {
+ { "id", "mutex number" },
+ { "n-locked", "lock count" },
+ { "n-owner-changed", "owner change count" },
+ { "n-contended", "contention count" },
+ { "nsec-locked-total", "total time locked" },
+ { "nsec-locked-max", "maximum time locked" },
+ { "nsec-locked-avg", "average time locked" },
+ { "nsec-contended-total", "total time contended" },
+ { "nsec-contended-avg", "average time contended" },
+ { "nsec-read-contended-total", "total time (read) contended" },
+};
+
+static SummaryOrder summary_order = ORDER_N_CONTENDED;
+
+static SummaryOrder summary_order_from_command(const char *command);
+
static void setup(void) __attribute ((constructor));
static void shutdown(void) __attribute ((destructor));
@@ -202,6 +241,17 @@ static int parse_env(const char *n, unsigned *t) {
return 0;
}
+/* Maximum tolerated relative error when comparing doubles */
+#define MAX_RELATIVE_ERROR 0.001
+
+static bool doubles_equal(double a, double b) {
+ /* Make sure we don't divide by zero. */
+ if (fpclassify(b) == FP_ZERO)
+ return (fpclassify(a) == FP_ZERO);
+
+ return ((a - b) / b <= MAX_RELATIVE_ERROR);
+}
+
#define LOAD_FUNC(name) \
do { \
*(void**) (&real_##name) = dlsym(RTLD_NEXT, #name); \
@@ -269,6 +319,7 @@ static void setup(void) {
pthread_mutex_t *m, *last;
int r;
unsigned t;
+ const char *s;
load_functions();
@@ -339,6 +390,15 @@ static void setup(void) {
else
show_n_max = t;
+ s = getenv("MUTRACE_SUMMARY_ORDER");
+ if (s != NULL) {
+ t = summary_order_from_command(s);
+ if (t == ORDER_INVALID)
+ fprintf(stderr, "mutrace: WARNING: Failed to parse $MUTRACE_SUMMARY_ORDER.\n");
+ else
+ summary_order = t;
+ }
+
if (getenv("MUTRACE_TRAP"))
raise_trap = true;
@@ -417,33 +477,60 @@ static int mutex_info_compare(const void *_a, const void *_b) {
*a = *(const struct mutex_info**) _a,
*b = *(const struct mutex_info**) _b;
- if (a->n_contended > b->n_contended)
- return -1;
- else if (a->n_contended < b->n_contended)
- return 1;
+ /* Order by the user's chosen ordering first, then fall back to a static
+ * ordering. */
+ switch (summary_order) {
+ #define ORDER_CASE(UCASE, lcase) \
+ case ORDER_##UCASE: \
+ if (a->lcase != b->lcase) \
+ return a->lcase - b->lcase; \
+ break;
- if (a->n_owner_changed > b->n_owner_changed)
- return -1;
- else if (a->n_owner_changed < b->n_owner_changed)
- return 1;
+ ORDER_CASE(ID, id)
+ ORDER_CASE(N_LOCKED, n_locked)
+ ORDER_CASE(N_OWNER_CHANGED, n_owner_changed)
+ ORDER_CASE(N_CONTENDED, n_contended)
+ ORDER_CASE(NSEC_LOCKED_TOTAL, nsec_locked_total)
+ ORDER_CASE(NSEC_LOCKED_MAX, nsec_locked_max)
+ ORDER_CASE(NSEC_CONTENDED_TOTAL, nsec_contended_total)
+ ORDER_CASE(NSEC_READ_CONTENDED_TOTAL, nsec_read_contended_total)
+ case ORDER_NSEC_LOCKED_AVG: {
+ double a_avg = a->nsec_locked_total / a->n_locked,
+ b_avg = b->nsec_locked_total / b->n_locked;
+
+ if (!doubles_equal(a_avg, b_avg))
+ return ((a_avg - b_avg) < 0.0) ? -1 : 1;
+ break;
+ }
+ case ORDER_NSEC_CONTENDED_AVG: {
+ double a_avg = a->nsec_contended_total / a->n_contended,
+ b_avg = b->nsec_contended_total / b->n_contended;
- if (a->n_locked > b->n_locked)
- return -1;
- else if (a->n_locked < b->n_locked)
- return 1;
+ if (!doubles_equal(a_avg, b_avg))
+ return ((a_avg - b_avg) < 0.0) ? -1 : 1;
+ break;
+ }
+ default:
+ /* Should never be reached. */
+ assert(0);
- if (a->nsec_locked_max > b->nsec_locked_max)
- return -1;
- else if (a->nsec_locked_max < b->nsec_locked_max)
- return 1;
+ #undef ORDER_CASE
+ }
- /* Let's make the output deterministic */
- if (a > b)
- return -1;
- else if (a < b)
- return 1;
+ /* Fall back to a static ordering. */
+ #define STATIC_ORDER(lcase) \
+ if (a->lcase != b->lcase) \
+ return a->lcase - b->lcase
- return 0;
+ STATIC_ORDER(n_contended);
+ STATIC_ORDER(n_owner_changed);
+ STATIC_ORDER(n_locked);
+ STATIC_ORDER(nsec_locked_max);
+
+ #undef STATIC_ORDER
+
+ /* Let's make the output deterministic */
+ return a - b;
}
static bool mutex_info_show(struct mutex_info *mi) {
@@ -559,6 +646,18 @@ static bool mutex_info_stat(struct mutex_info *mi) {
return true;
}
+static SummaryOrder summary_order_from_command(const char *command) {
+ unsigned int i;
+
+ for (i = 0; i < ORDER_INVALID; i++) {
+ if (strcmp(command, summary_order_details[i].command) == 0) {
+ return ORDER_ID;
+ }
+ }
+
+ return ORDER_INVALID;
+}
+
static void show_summary_internal(void) {
struct mutex_info *mi, **table;
unsigned n, u, i, m;
@@ -609,19 +708,19 @@ static void show_summary_internal(void) {
qsort(table, n, sizeof(table[0]), mutex_info_compare);
- for (i = 0, m = 0; i < n && (show_n_max <= 0 || m < show_n_max); i++)
- m += mutex_info_dump(table[i]) ? 1 : 0;
+ for (i = n, m = 0; i > 0 && (show_n_max <= 0 || m < show_n_max); i--)
+ m += mutex_info_dump(table[i - 1]) ? 1 : 0;
if (m > 0) {
fprintf(stderr,
"\n"
- "mutrace: Showing %u most contended mutexes:\n"
+ "mutrace: Showing %u mutexes in order of %s:\n"
"\n"
" Mutex # Locked Changed Cont. cont.Time[ms] tot.Time[ms] avg.Time[ms] Flags\n",
- m);
+ m, summary_order_details[summary_order].ui_string);
- for (i = 0, m = 0; i < n && (show_n_max <= 0 || m < show_n_max); i++)
- m += mutex_info_stat(table[i]) ? 1 : 0;
+ for (i = n, m = 0; i > 0 && (show_n_max <= 0 || m < show_n_max); i--)
+ m += mutex_info_stat(table[i - 1]) ? 1 : 0;
if (i < n)
diff --git a/mutrace.in b/mutrace.in
index 10b04b6..93e49ae 100755
--- a/mutrace.in
+++ b/mutrace.in
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser General Public
# License along with mutrace. If not, see <http://www.gnu.org/licenses/>.
-if ! TEMP=`getopt -o +ardh --long hash-size:,frames:,locked-min:,owner-changed-min:,contended-min:,max:,trap,help,all,debug-info -n mutrace -- "$@"` ; then
+if ! TEMP=`getopt -o +ardh --long hash-size:,frames:,locked-min:,owner-changed-min:,contended-min:,max:,order:,trap,help,all,debug-info -n mutrace -- "$@"` ; then
exit 1
fi
@@ -57,6 +57,11 @@ while : ; do
shift 2
;;
+ --order)
+ export MUTRACE_SUMMARY_ORDER="$2"
+ shift 2
+ ;;
+
--trap)
export MUTRACE_TRAP=1
shift 1
@@ -94,6 +99,8 @@ OPTIONS:
--frames=INTEGER Set number of frames to show in stack traces
-d, --debug-info Make use of debug information in stack traces
--max=INTEGER Show this many mutexes at maximum
+ --order=STRING Order the summary table by this column (see
+ below for valid column names)
--locked-min=INTEGER Only show mutexes that have been locked at
least this often
@@ -109,6 +116,21 @@ OPTIONS:
--trap Trigger a debugger trap each time a mutex
inconsistency is detected (for use in
conjunction with gdb)
+
+ORDER COLUMN NAMES:
+ id Mutex number
+ n-locked Total number of times mutex was locked
+ n-owner-changed Total number of times mutex ownership changed
+ n-contended Total number of times mutex was contested
+ nsec-locked-total Total time mutex was locked for writing
+ nsec-locked-max Maximum time mutex was continuously locked for
+ writing
+ nsec-locked-avg Average time mutex was continuously locked for
+ writing
+ nsec-contended-total Total time mutex was contended for writing
+ nsec-contended-avg Average time mutex was continuously contended
+ for writing
+ nsec-read-contended-total Total time mutex was contended for reading
EOF
exit 0
;;