Skip to contentSkip to conversation

x86, bts, ftrace: a BTS ftrace plug-in prototype

PATCH In review Markus Metzger 4 participants 8 messages Nov 25, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 9/9 x86, bts, ftrace: a BTS ftrace plug-in prototype

A prototype for a BTS ftrace plug-in.

The tracer collects branch trace in a cyclic buffer for each cpu.

The tracer is not configurable and the trace for each snapshot is appended when doing cat /debug/tracing/trace.

This is a proof of concept that will be extended with future patches to become a (hopefully) useful tool.

---

kernel/trace/Kconfig +11 -0
Index: ftrace/kernel/trace/Kconfig
===================================================================
@@ -28,6 +28,9 @@
 config HAVE_FTRACE_MCOUNT_RECORD
 	bool
 
+config HAVE_HW_BRANCH_TRACER
+	bool
+
 config TRACER_MAX_TRACE
 	bool
 
@@ -233,6 +236,14 @@
 
 	  Say N if unsure.
 
+config BTS_TRACER
+	depends on HAVE_HW_BRANCH_TRACER
+	bool "Trace branches"
+	select TRACING
+	help
+	  This tracer records all branches on the system in a circular
+	  buffer giving access to the last N branches for each cpu.
+
 config DYNAMIC_FTRACE
 	bool "enable/disable ftrace tracepoints dynamically"
 	depends on FUNCTION_TRACER
kernel/trace/Makefile +1 -0
Index: ftrace/kernel/trace/Makefile
===================================================================
@@ -31,5 +31,6 @@
 obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
 obj-$(CONFIG_FUNCTION_RET_TRACER) += trace_functions_return.o
 obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
+obj-$(CONFIG_BTS_TRACER) += trace_bts.o
 
 libftrace-y := ftrace.o
kernel/trace/trace_bts.c new file +276 -0

276 lines not on this page · raw message

kernel/trace/trace.h +12 -0
Index: ftrace/kernel/trace/trace.h
===================================================================
@@ -27,6 +27,7 @@
 	TRACE_BOOT_RET,
 	TRACE_FN_RET,
 	TRACE_USER_STACK,
+	TRACE_BTS,
 
 	__TRACE_LAST_TYPE
 };
@@ -153,6 +154,12 @@
 	char			correct;
 };
 
+struct bts_entry {
+	struct trace_entry	ent;
+	unsigned long		from;
+	unsigned long		to;
+};
+
 /*
  * trace_flag_type is an enumeration that holds different
  * states when a trace occurs. These are:
@@ -258,6 +265,7 @@
 		IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\
 		IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \
 		IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\
+		IF_ASSIGN(var, ent, struct bts_entry, TRACE_BTS);\
 		__ftrace_bad_type();					\
 	} while (0)
 
@@ -392,6 +400,10 @@
 void
 trace_function_return(struct ftrace_retfunc *trace);
 
+void trace_bts(struct trace_array *tr,
+	       unsigned long from,
+	       unsigned long to);
+
 void tracing_start_cmdline_record(void);
 void tracing_stop_cmdline_record(void);
 void tracing_sched_switch_assign_trace(struct trace_array *tr);
arch/x86/Kconfig.cpu +1 -0
Index: ftrace/arch/x86/Kconfig.cpu
===================================================================
@@ -515,6 +515,7 @@
 config X86_DS
 	def_bool X86_PTRACE_BTS
 	depends on X86_DEBUGCTLMSR
+	select HAVE_HW_BRANCH_TRACER
 
 config X86_PTRACE_BTS
 	bool "Branch Trace Store"

---------------------------------------------------------------------
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen Germany
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Douglas Lusk, Peter Gleissner, Hannes Schwaderer
Registergericht: Muenchen HRB 47456 Ust.-IdNr.
VAT Registration No.: DE129385895
Citibank Frankfurt (BLZ 502 109 00) 600119052

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.

#1 lore raw

Markus Metzger <[email protected]> writes:

Basic idea looks very useful. Thanks.

Quoted 1 line

+static void bts_trace_cpuinit(struct cpuinfo_x86 *c)

That should be somewhere generic I think. Doesn't ptrace have it too?

-Andi

Quoted 58 lines
kernel/trace/trace_bts.c new file +58 -0
+{
+	switch (c->x86) {
+	case 0x6:
+		switch (c->x86_model) {
+		case 0x0 ... 0xC:
+			break;
+		case 0xD:
+		case 0xE: /* Pentium M */
+			sizeof_field = sizeof(long);
+			debugctl_mask = (1<<6)|(1<<7);
+			break;
+		default:
+			sizeof_field = 8;
+			debugctl_mask = (1<<6)|(1<<7);
+			break;
+		}
+		break;
+	case 0xF:
+		switch (c->x86_model) {
+		case 0x0:
+		case 0x1:
+		case 0x2: /* Netburst */
+			sizeof_field = sizeof(long);
+			debugctl_mask = (1<<2)|(1<<3);
+			break;
+		default:
+			/* sorry, don't know about them */
+			break;
+		}
+		break;
+	default:
+		/* sorry, don't know about them */
+		break;
+	}
+}
+
+static inline void bts_enable(void)
+{
+	unsigned long debugctl;
+
+	rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+	wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl | debugctl_mask);
+}
+
+static inline void bts_disable(void)
+{
+	unsigned long debugctl;
+
+	rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+	wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl & ~debugctl_mask);
+}
+
+static void bts_trace_reset(struct trace_array *tr)
+{
+	int cpu;
+
+	tr->time_start = ftrace_now(tr->cpu);
+
Signature · 1 line
[email protected]
#2 lore raw
Quoted 5 lines

-----Original Message-----
From: Andi Kleen [mailto:[email protected]]
Sent: Dienstag, 25. November 2008 11:13
To: Metzger, Markus T

Quoted 7 lines

Markus Metzger <[email protected]> writes:

Basic idea looks very useful. Thanks.

Quoted 1 line

+static void bts_trace_cpuinit(struct cpuinfo_x86 *c)

That should be somewhere generic I think. Doesn't ptrace have it too?

It does. All the BTS bits currently live in ptrace.c - that had been the only user, so far. I will move them into ds.c, but this will take a few patches.

Ingo said its important to have an early prototype. So I did the first version with very restricted functionality and some duplicate code.

regards,
markus.

Quoted 54 lines

-Andi

Quoted 50 lines
kernel/trace/trace_bts.c new file +50 -0
+{
+	switch (c->x86) {
+	case 0x6:
+		switch (c->x86_model) {
+		case 0x0 ... 0xC:
+			break;
+		case 0xD:
+		case 0xE: /* Pentium M */
+			sizeof_field = sizeof(long);
+			debugctl_mask = (1<<6)|(1<<7);
+			break;
+		default:
+			sizeof_field = 8;
+			debugctl_mask = (1<<6)|(1<<7);
+			break;
+		}
+		break;
+	case 0xF:
+		switch (c->x86_model) {
+		case 0x0:
+		case 0x1:
+		case 0x2: /* Netburst */
+			sizeof_field = sizeof(long);
+			debugctl_mask = (1<<2)|(1<<3);
+			break;
+		default:
+			/* sorry, don't know about them */
+			break;
+		}
+		break;
+	default:
+		/* sorry, don't know about them */
+		break;
+	}
+}
+
+static inline void bts_enable(void)
+{
+	unsigned long debugctl;
+
+	rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+	wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl | debugctl_mask);
+}
+
+static inline void bts_disable(void)
+{
+	unsigned long debugctl;
+
+	rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+	wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl & ~debugctl_mask);
Quoted 12 lines
Quoted 8 lines
kernel/trace/trace_bts.c new file +8 -0
+}
+
+static void bts_trace_reset(struct trace_array *tr)
+{
+	int cpu;
+
+	tr->time_start = ftrace_now(tr->cpu);
+
Signature · 1 line
[email protected]

---------------------------------------------------------------------
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen Germany
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Douglas Lusk, Peter Gleissner, Hannes Schwaderer
Registergericht: Muenchen HRB 47456 Ust.-IdNr.
VAT Registration No.: DE129385895
Citibank Frankfurt (BLZ 502 109 00) 600119052

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.

#3 lore raw
In reply to Markus Metzger

Hi Markus,

2008/11/25 Markus Metzger <[email protected]>:

Quoted 9 lines
kernel/trace/trace_bts.c new file +9 -0
+static enum print_line_t bts_trace_print_line(struct trace_iterator *iter)
+{
+       struct trace_entry *entry = iter->ent;
+       struct trace_seq *seq = &iter->seq;
+       struct bts_entry *it;
+
+       trace_assign_type(it, entry);
+
+       if (entry->type == TRACE_BTS) {

kernel/trace/trace_bts.c line 181

Quoted 10 lines
kernel/trace/trace_bts.c new file +10 -0
+               int ret;
+#ifdef CONFIG_KALLSYMS
+               char function[KSYM_SYMBOL_LEN];
+               sprint_symbol(function, it->from);
+#else
+               char *function = "<unknown>";
+#endif
+
+               ret = trace_seq_printf(seq, "%4d  0x%lx -> 0x%lx [%s]\n",
+                                      entry->cpu, it->from, it->to, function);

You can use seq_print_ip_sym() which handles the ifdef and the sprint_symbol...

kernel/trace/trace_bts.c line 201

Quoted 13 lines
kernel/trace/trace_bts.c new file +13 -0
+void trace_bts(struct trace_array *tr, unsigned long from, unsigned long to)
+{
+       struct ring_buffer_event *event;
+       struct bts_entry *entry;
+       unsigned long irq;
+
+       event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq);
+       if (!event)
+               return;
+       entry   = ring_buffer_event_data(event);
+       tracing_generic_entry_update(&entry->ent, 0, from);
+       entry->ent.type = TRACE_BTS;
+       entry->ent.cpu = smp_processor_id();

If you look at the struct trace_entry, you will see find the cpu field. It is already inserted automatically :-)

#4 lore raw
Quoted 26 lines

-----Original Message-----
From: Frédéric Weisbecker [mailto:[email protected]]
Sent: Dienstag, 25. November 2008 11:40
To: Metzger, Markus T

2008/11/25 Markus Metzger <[email protected]>:

Quoted 1 line

+static enum print_line_t bts_trace_print_line(struct

trace_iterator *iter)

Quoted 7 lines
kernel/trace/trace_bts.c new file +7 -0
+{
+       struct trace_entry *entry = iter->ent;
+       struct trace_seq *seq = &iter->seq;
+       struct bts_entry *it;
+
+       trace_assign_type(it, entry);
+

kernel/trace/trace_bts.c

Quoted 10 lines
kernel/trace/trace_bts.c new file +10 -0
+       if (entry->type == TRACE_BTS) {
+               int ret;
+#ifdef CONFIG_KALLSYMS
+               char function[KSYM_SYMBOL_LEN];
+               sprint_symbol(function, it->from);
+#else
+               char *function = "<unknown>";
+#endif
+
+               ret = trace_seq_printf(seq, "%4d  0x%lx -> 

0x%lx [%s]\n",

Quoted 6 lines
Quoted 1 line

+ entry->cpu, it->from,

it->to, function);

You can use seq_print_ip_sym() which handles the ifdef and the sprint_symbol...

Thanks.

Quoted 17 lines
Quoted 1 line

+void trace_bts(struct trace_array *tr, unsigned long from,

unsigned long to)

kernel/trace/trace_bts.c

Quoted 6 lines
kernel/trace/trace_bts.c new file +6 -0
+{
+       struct ring_buffer_event *event;
+       struct bts_entry *entry;
+       unsigned long irq;
+
+       event = ring_buffer_lock_reserve(tr->buffer, 

sizeof(*entry), &irq);

kernel/trace/trace_bts.c line 201

Quoted 6 lines
kernel/trace/trace_bts.c new file +6 -0
+       if (!event)
+               return;
+       entry   = ring_buffer_event_data(event);
+       tracing_generic_entry_update(&entry->ent, 0, from);
+       entry->ent.type = TRACE_BTS;
+       entry->ent.cpu = smp_processor_id();
Quoted 2 lines

If you look at the struct trace_entry, you will see find the cpu field. It is already inserted automatically :-)

I am using that field in struct trace_entry.

Without the assignment, though, I've seen funny cpu numbers. It seems that the field is not filled automatically.

regards,
markus.
---------------------------------------------------------------------
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen Germany
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Douglas Lusk, Peter Gleissner, Hannes Schwaderer
Registergericht: Muenchen HRB 47456 Ust.-IdNr.
VAT Registration No.: DE129385895
Citibank Frankfurt (BLZ 502 109 00) 600119052

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.

#5 lore raw

2008/11/25 Metzger, Markus T <[email protected]>:

Quoted 4 lines
Quoted 2 lines

If you look at the struct trace_entry, you will see find the cpu field. It is already inserted automatically :-)

I am using that field in struct trace_entry.

Right, I misread.

Quoted 2 lines

Without the assignment, though, I've seen funny cpu numbers. It seems that the field is not filled automatically.

Sorry, I thought it was done by tracing_generic_entry_update() but all of the fields in struct trace_entry are filled inside
this function except the cpu one.... Perhaps it should, I don't know.

#6 lore raw
In reply to Markus Metzger
Quoted 11 lines · Markus Metzger

A prototype for a BTS ftrace plug-in.

The tracer collects branch trace in a cyclic buffer for each cpu.

The tracer is not configurable and the trace for each snapshot is appended when doing cat /debug/tracing/trace.

This is a proof of concept that will be extended with future patches to become a (hopefully) useful tool.

very nice stuff!

i've created a new topic branch for this:

tip/tracing/hw-branch-tracing

and applied your patches there. (applied the earlier x86 patches in their appropriate x86 topic branches)

Integrated the end result into tip/master and have started testing it.

I've got a few more review comments as well - will post them as a reply to the respective patches.

Ingo

#7 lore raw
In reply to Markus Metzger

kernel/trace/trace_bts.c line 25

Quoted 7 lines · Markus Metzger

Index: ftrace/kernel/trace/trace_bts.c
===================================================================

kernel/trace/trace_bts.c new file +2 -0
 
+#define this_tracer per_cpu(tracer, smp_processor_id())
+#define this_buffer per_cpu(buffer, smp_processor_id())

please change these to inline functions.

kernel/trace/trace_bts.c line 33

Quoted 6 lines
kernel/trace/trace_bts.c new file +6 -0
+/*
+ * Information to interpret a BTS record.
+ * This will go into an in-kernel BTS interface.
+ */
+static unsigned char sizeof_field;
+static unsigned long debugctl_mask;

__read_mostly.

Quoted 42 lines
kernel/trace/trace_bts.c new file +42 -0
+static void bts_trace_cpuinit(struct cpuinfo_x86 *c)
+{
+	switch (c->x86) {
+	case 0x6:
+		switch (c->x86_model) {
+		case 0x0 ... 0xC:
+			break;
+		case 0xD:
+		case 0xE: /* Pentium M */
+			sizeof_field = sizeof(long);
+			debugctl_mask = (1<<6)|(1<<7);
+			break;
+		default:
+			sizeof_field = 8;
+			debugctl_mask = (1<<6)|(1<<7);
+			break;
+		}
+		break;
+	case 0xF:
+		switch (c->x86_model) {
+		case 0x0:
+		case 0x1:
+		case 0x2: /* Netburst */
+			sizeof_field = sizeof(long);
+			debugctl_mask = (1<<2)|(1<<3);
+			break;
+		default:
+			/* sorry, don't know about them */
+			break;
+		}
+		break;
+	default:
+		/* sorry, don't know about them */
+		break;
+	}
+}
+
+static inline void bts_enable(void)
+{
+	unsigned long debugctl;
+
+	rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);

kernel/trace/trace_bts.c line 88

Quoted 10 lines
kernel/trace/trace_bts.c new file +10 -0
+	wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl | debugctl_mask);
+}
+
+static inline void bts_disable(void)
+{
+	unsigned long debugctl;
+
+	rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+	wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl & ~debugctl_mask);
+}

these x86 architecture bits should move into arch/x86/kernel/ftrace.c. (or ds.c)

kernel/trace/trace_bts.c line 160

Quoted 11 lines
kernel/trace/trace_bts.c new file +11 -0
+static void bts_trace_print_header(struct seq_file *m)
+{
+#ifdef __i386__
+	seq_puts(m, "# CPU#    FROM           TO     FUNCTION\n");
+	seq_puts(m, "#  |       |             |         |\n");
+#else
+	seq_puts(m,
+		 "# CPU#        FROM                   TO         FUNCTION\n");
+	seq_puts(m,
+		 "#  |           |                     |             |\n");
+#endif

lets just standardize on the 64-bit width, ok?

kernel/trace/trace_bts.c line 178

Quoted 6 lines
kernel/trace/trace_bts.c new file +6 -0
+#ifdef CONFIG_KALLSYMS
+		char function[KSYM_SYMBOL_LEN];
+		sprint_symbol(function, it->from);
+#else
+		char *function = "<unknown>";
+#endif

just do this:

		char function[KSYM_SYMBOL_LEN];
		sprint_symbol(function, it->from);

that should do the right thing in the !KALLSYMS case too.

kernel/trace/trace_bts.c line 262

Quoted 3 lines
kernel/trace/trace_bts.c new file +3 -0
+struct tracer bts_tracer __read_mostly =
+{
+	.name		= "bts",

please rename it to: "hw-branch-tracer". "BTS" is an x86 concept and we want to keep the name generic.

Ingo