Browse Source

tools/perf: Add support for record transaction flags

Add support for recording and displaying the transaction flags.
They are essentially a new sort key. Also display them
in a nice way to the user.

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1379688044-14173-6-git-send-email-andi@firstfloor.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Andi Kleen 12 years ago
parent
commit
475eeab9f3

+ 3 - 1
tools/perf/Documentation/perf-record.txt

@@ -179,12 +179,14 @@ is enabled for all the sampling events. The sampled branch type is the same for
 The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
 The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
 Note that this feature may not be available on all processors.
 Note that this feature may not be available on all processors.
 
 
--W::
 --weight::
 --weight::
 Enable weightened sampling. An additional weight is recorded per sample and can be
 Enable weightened sampling. An additional weight is recorded per sample and can be
 displayed with the weight and local_weight sort keys.  This currently works for TSX
 displayed with the weight and local_weight sort keys.  This currently works for TSX
 abort events and some memory events in precise mode on modern Intel CPUs.
 abort events and some memory events in precise mode on modern Intel CPUs.
 
 
+--transaction::
+Record transaction flags for transaction related events.
+
 SEE ALSO
 SEE ALSO
 --------
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1]
 linkperf:perf-stat[1], linkperf:perf-list[1]

+ 4 - 0
tools/perf/Documentation/perf-report.txt

@@ -72,6 +72,10 @@ OPTIONS
 	- cpu: cpu number the task ran at the time of sample
 	- cpu: cpu number the task ran at the time of sample
 	- srcline: filename and line number executed at the time of sample.  The
 	- srcline: filename and line number executed at the time of sample.  The
 	DWARF debugging info must be provided.
 	DWARF debugging info must be provided.
+	- weight: Event specific weight, e.g. memory latency or transaction
+	abort cost. This is the global weight.
+	- local_weight: Local weight version of the weight above.
+	- transaction: Transaction abort flags.
 
 
 	By default, comm, dso and symbol keys are used.
 	By default, comm, dso and symbol keys are used.
 	(i.e. --sort comm,dso,symbol)
 	(i.e. --sort comm,dso,symbol)

+ 1 - 1
tools/perf/Documentation/perf-top.txt

@@ -113,7 +113,7 @@ Default is to monitor all CPUS.
 -s::
 -s::
 --sort::
 --sort::
 	Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight,
 	Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight,
-	local_weight, abort, in_tx
+	local_weight, abort, in_tx, transaction
 
 
 -n::
 -n::
 --show-nr-samples::
 --show-nr-samples::

+ 1 - 1
tools/perf/builtin-annotate.c

@@ -63,7 +63,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
 		return 0;
 		return 0;
 	}
 	}
 
 
-	he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1);
+	he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1, 0);
 	if (he == NULL)
 	if (he == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 

+ 5 - 3
tools/perf/builtin-diff.c

@@ -304,9 +304,10 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
 
 
 static int hists__add_entry(struct hists *self,
 static int hists__add_entry(struct hists *self,
 			    struct addr_location *al, u64 period,
 			    struct addr_location *al, u64 period,
-			    u64 weight)
+			    u64 weight, u64 transaction)
 {
 {
-	if (__hists__add_entry(self, al, NULL, period, weight) != NULL)
+	if (__hists__add_entry(self, al, NULL, period, weight, transaction)
+	    != NULL)
 		return 0;
 		return 0;
 	return -ENOMEM;
 	return -ENOMEM;
 }
 }
@@ -328,7 +329,8 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
 	if (al.filtered)
 	if (al.filtered)
 		return 0;
 		return 0;
 
 
-	if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) {
+	if (hists__add_entry(&evsel->hists, &al, sample->period,
+			     sample->weight, sample->transaction)) {
 		pr_warning("problem incrementing symbol period, skipping event\n");
 		pr_warning("problem incrementing symbol period, skipping event\n");
 		return -1;
 		return -1;
 	}
 	}

+ 2 - 0
tools/perf/builtin-record.c

@@ -894,6 +894,8 @@ const struct option record_options[] = {
 		     parse_branch_stack),
 		     parse_branch_stack),
 	OPT_BOOLEAN('W', "weight", &record.opts.sample_weight,
 	OPT_BOOLEAN('W', "weight", &record.opts.sample_weight,
 		    "sample by weight (on special events only)"),
 		    "sample by weight (on special events only)"),
+	OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction,
+		    "sample transaction flags (special events only)"),
 	OPT_END()
 	OPT_END()
 };
 };
 
 

+ 2 - 2
tools/perf/builtin-report.c

@@ -259,7 +259,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
 	}
 	}
 
 
 	he = __hists__add_entry(&evsel->hists, al, parent, sample->period,
 	he = __hists__add_entry(&evsel->hists, al, parent, sample->period,
-					sample->weight);
+				sample->weight, sample->transaction);
 	if (he == NULL)
 	if (he == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
@@ -787,7 +787,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
 		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
 		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
 		   " dso_to, dso_from, symbol_to, symbol_from, mispredict,"
 		   " dso_to, dso_from, symbol_to, symbol_from, mispredict,"
 		   " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, "
 		   " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, "
-		   "snoop, locked, abort, in_tx"),
+		   "snoop, locked, abort, in_tx, transaction"),
 	OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
 	OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
 		    "Show sample percentage for different cpu modes"),
 		    "Show sample percentage for different cpu modes"),
 	OPT_STRING('p', "parent", &parent_pattern, "regex",
 	OPT_STRING('p', "parent", &parent_pattern, "regex",

+ 2 - 3
tools/perf/builtin-top.c

@@ -247,9 +247,8 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
 
 
 	pthread_mutex_lock(&evsel->hists.lock);
 	pthread_mutex_lock(&evsel->hists.lock);
 	he = __hists__add_entry(&evsel->hists, al, NULL, sample->period,
 	he = __hists__add_entry(&evsel->hists, al, NULL, sample->period,
-				sample->weight);
+				sample->weight, sample->transaction);
 	pthread_mutex_unlock(&evsel->hists.lock);
 	pthread_mutex_unlock(&evsel->hists.lock);
-
 	if (he == NULL)
 	if (he == NULL)
 		return NULL;
 		return NULL;
 
 
@@ -1104,7 +1103,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
 		    "be more verbose (show counter open errors, etc)"),
 		    "be more verbose (show counter open errors, etc)"),
 	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
 	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
 		   "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight,"
 		   "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight,"
-		   " abort, in_tx"),
+		   " abort, in_tx, transaction"),
 	OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
 	OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
 		    "Show a column with the number of samples"),
 		    "Show a column with the number of samples"),
 	OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,
 	OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,

+ 1 - 0
tools/perf/perf.h

@@ -233,6 +233,7 @@ struct perf_record_opts {
 	u64	     default_interval;
 	u64	     default_interval;
 	u64	     user_interval;
 	u64	     user_interval;
 	u16	     stack_dump_size;
 	u16	     stack_dump_size;
+	bool	     sample_transaction;
 };
 };
 
 
 #endif
 #endif

+ 4 - 2
tools/perf/tests/hists_link.c

@@ -222,7 +222,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
 							  &sample) < 0)
 							  &sample) < 0)
 				goto out;
 				goto out;
 
 
-			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
+			he = __hists__add_entry(&evsel->hists, &al, NULL,
+						1, 1, 0);
 			if (he == NULL)
 			if (he == NULL)
 				goto out;
 				goto out;
 
 
@@ -244,7 +245,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
 							  &sample) < 0)
 							  &sample) < 0)
 				goto out;
 				goto out;
 
 
-			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
+			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1,
+						0);
 			if (he == NULL)
 			if (he == NULL)
 				goto out;
 				goto out;
 
 

+ 1 - 0
tools/perf/util/event.h

@@ -111,6 +111,7 @@ struct perf_sample {
 	u64 stream_id;
 	u64 stream_id;
 	u64 period;
 	u64 period;
 	u64 weight;
 	u64 weight;
+	u64 transaction;
 	u32 cpu;
 	u32 cpu;
 	u32 raw_size;
 	u32 raw_size;
 	u64 data_src;
 	u64 data_src;

+ 9 - 0
tools/perf/util/evsel.c

@@ -681,6 +681,9 @@ void perf_evsel__config(struct perf_evsel *evsel,
 	attr->mmap2 = track && !perf_missing_features.mmap2;
 	attr->mmap2 = track && !perf_missing_features.mmap2;
 	attr->comm  = track;
 	attr->comm  = track;
 
 
+	if (opts->sample_transaction)
+		attr->sample_type	|= PERF_SAMPLE_TRANSACTION;
+
 	/*
 	/*
 	 * XXX see the function comment above
 	 * XXX see the function comment above
 	 *
 	 *
@@ -1470,6 +1473,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
 		array++;
 		array++;
 	}
 	}
 
 
+	data->transaction = 0;
+	if (type & PERF_SAMPLE_TRANSACTION) {
+		data->transaction = *array;
+		array++;
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 6 - 1
tools/perf/util/hist.c

@@ -160,6 +160,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
 	hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
 	hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
 	hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
 	hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
 	hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
 	hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
+
+	if (h->transaction)
+		hists__new_col_len(hists, HISTC_TRANSACTION,
+				   hist_entry__transaction_len());
 }
 }
 
 
 void hists__output_recalc_col_len(struct hists *hists, int max_rows)
 void hists__output_recalc_col_len(struct hists *hists, int max_rows)
@@ -466,7 +470,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self,
 struct hist_entry *__hists__add_entry(struct hists *self,
 struct hist_entry *__hists__add_entry(struct hists *self,
 				      struct addr_location *al,
 				      struct addr_location *al,
 				      struct symbol *sym_parent, u64 period,
 				      struct symbol *sym_parent, u64 period,
-				      u64 weight)
+				      u64 weight, u64 transaction)
 {
 {
 	struct hist_entry entry = {
 	struct hist_entry entry = {
 		.thread	= al->thread,
 		.thread	= al->thread,
@@ -487,6 +491,7 @@ struct hist_entry *__hists__add_entry(struct hists *self,
 		.hists	= self,
 		.hists	= self,
 		.branch_info = NULL,
 		.branch_info = NULL,
 		.mem_info = NULL,
 		.mem_info = NULL,
+		.transaction = transaction,
 	};
 	};
 
 
 	return add_hist_entry(self, &entry, al, period, weight);
 	return add_hist_entry(self, &entry, al, period, weight);

+ 3 - 1
tools/perf/util/hist.h

@@ -59,6 +59,7 @@ enum hist_column {
 	HISTC_MEM_TLB,
 	HISTC_MEM_TLB,
 	HISTC_MEM_LVL,
 	HISTC_MEM_LVL,
 	HISTC_MEM_SNOOP,
 	HISTC_MEM_SNOOP,
+	HISTC_TRANSACTION,
 	HISTC_NR_COLS, /* Last entry */
 	HISTC_NR_COLS, /* Last entry */
 };
 };
 
 
@@ -84,9 +85,10 @@ struct hists {
 struct hist_entry *__hists__add_entry(struct hists *self,
 struct hist_entry *__hists__add_entry(struct hists *self,
 				      struct addr_location *al,
 				      struct addr_location *al,
 				      struct symbol *parent, u64 period,
 				      struct symbol *parent, u64 period,
-				      u64 weight);
+				      u64 weight, u64 transaction);
 int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
 int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
 int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
 int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
+int hist_entry__transaction_len(void);
 int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size,
 int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size,
 			      struct hists *hists);
 			      struct hists *hists);
 void hist_entry__free(struct hist_entry *);
 void hist_entry__free(struct hist_entry *);

+ 3 - 0
tools/perf/util/session.c

@@ -858,6 +858,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
 	if (sample_type & PERF_SAMPLE_DATA_SRC)
 	if (sample_type & PERF_SAMPLE_DATA_SRC)
 		printf(" . data_src: 0x%"PRIx64"\n", sample->data_src);
 		printf(" . data_src: 0x%"PRIx64"\n", sample->data_src);
 
 
+	if (sample_type & PERF_SAMPLE_TRANSACTION)
+		printf("... transaction: %" PRIx64 "\n", sample->transaction);
+
 	if (sample_type & PERF_SAMPLE_READ)
 	if (sample_type & PERF_SAMPLE_READ)
 		sample_read__printf(sample, evsel->attr.read_format);
 		sample_read__printf(sample, evsel->attr.read_format);
 }
 }

+ 73 - 0
tools/perf/util/sort.c

@@ -907,6 +907,78 @@ struct sort_entry sort_in_tx = {
 	.se_width_idx	= HISTC_IN_TX,
 	.se_width_idx	= HISTC_IN_TX,
 };
 };
 
 
+static int64_t
+sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+	return left->transaction - right->transaction;
+}
+
+static inline char *add_str(char *p, const char *str)
+{
+	strcpy(p, str);
+	return p + strlen(str);
+}
+
+static struct txbit {
+	unsigned flag;
+	const char *name;
+	int skip_for_len;
+} txbits[] = {
+	{ PERF_TXN_ELISION,        "EL ",        0 },
+	{ PERF_TXN_TRANSACTION,    "TX ",        1 },
+	{ PERF_TXN_SYNC,           "SYNC ",      1 },
+	{ PERF_TXN_ASYNC,          "ASYNC ",     0 },
+	{ PERF_TXN_RETRY,          "RETRY ",     0 },
+	{ PERF_TXN_CONFLICT,       "CON ",       0 },
+	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
+	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 },
+	{ 0, NULL, 0 }
+};
+
+int hist_entry__transaction_len(void)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; txbits[i].name; i++) {
+		if (!txbits[i].skip_for_len)
+			len += strlen(txbits[i].name);
+	}
+	len += 4; /* :XX<space> */
+	return len;
+}
+
+static int hist_entry__transaction_snprintf(struct hist_entry *self, char *bf,
+					    size_t size, unsigned int width)
+{
+	u64 t = self->transaction;
+	char buf[128];
+	char *p = buf;
+	int i;
+
+	buf[0] = 0;
+	for (i = 0; txbits[i].name; i++)
+		if (txbits[i].flag & t)
+			p = add_str(p, txbits[i].name);
+	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
+		p = add_str(p, "NEITHER ");
+	if (t & PERF_TXN_ABORT_MASK) {
+		sprintf(p, ":%" PRIx64,
+			(t & PERF_TXN_ABORT_MASK) >>
+			PERF_TXN_ABORT_SHIFT);
+		p += strlen(p);
+	}
+
+	return repsep_snprintf(bf, size, "%-*s", width, buf);
+}
+
+struct sort_entry sort_transaction = {
+	.se_header	= "Transaction                ",
+	.se_cmp		= sort__transaction_cmp,
+	.se_snprintf	= hist_entry__transaction_snprintf,
+	.se_width_idx	= HISTC_TRANSACTION,
+};
+
 struct sort_dimension {
 struct sort_dimension {
 	const char		*name;
 	const char		*name;
 	struct sort_entry	*entry;
 	struct sort_entry	*entry;
@@ -925,6 +997,7 @@ static struct sort_dimension common_sort_dimensions[] = {
 	DIM(SORT_SRCLINE, "srcline", sort_srcline),
 	DIM(SORT_SRCLINE, "srcline", sort_srcline),
 	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
 	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
 	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
 	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
+	DIM(SORT_TRANSACTION, "transaction", sort_transaction),
 };
 };
 
 
 #undef DIM
 #undef DIM

+ 2 - 0
tools/perf/util/sort.h

@@ -85,6 +85,7 @@ struct hist_entry {
 	struct map_symbol	ms;
 	struct map_symbol	ms;
 	struct thread		*thread;
 	struct thread		*thread;
 	u64			ip;
 	u64			ip;
+	u64			transaction;
 	s32			cpu;
 	s32			cpu;
 
 
 	struct hist_entry_diff	diff;
 	struct hist_entry_diff	diff;
@@ -145,6 +146,7 @@ enum sort_type {
 	SORT_SRCLINE,
 	SORT_SRCLINE,
 	SORT_LOCAL_WEIGHT,
 	SORT_LOCAL_WEIGHT,
 	SORT_GLOBAL_WEIGHT,
 	SORT_GLOBAL_WEIGHT,
+	SORT_TRANSACTION,
 
 
 	/* branch stack specific sort keys */
 	/* branch stack specific sort keys */
 	__SORT_BRANCH_STACK,
 	__SORT_BRANCH_STACK,