x86, bts, ftrace: a BTS ftrace plug-in prototype
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
@@ -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
@@ -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
@@ -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
@@ -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.
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]
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.
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 :-)
Quoted 26 lines
-----Original Message-----
From: Frédéric Weisbecker [mailto:[email protected]]
Sent: Dienstag, 25. November 2008 11:40
To: Metzger, Markus T2008/11/25 Markus Metzger <[email protected]>:
Quoted 1 line
+static enum
print_line_tbts_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 thesprint_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.
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.
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.
Signed-off-by Markus Metzger <[email protected]>
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
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