tracing: various updates for 2.6.34
Steven Rostedt
maintainerIngo,
Please pull the latest tip/tracing/core tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git
tip/tracing/core
Lai Jiangshan (7):
tracing: Have __dynamic_array() define a field
tracing: Add print_fmt field
tracing/syscalls: Init print_fmt for syscall events tracing/kprobes: Init print_fmt for kprobe events
tracing: Use defined fields and print_fmt to print formats
tracing: Remove show_format and related macros from TRACE_EVENT
tracing: Consolidate protection of reader access to the ring buffer
Steven Rostedt (2):
tracing: Have syscall tracing call its own init function
tracing: Add stack dump to trace_printk if stacktrace option is set
Wolfram Sang (2):
tracing: optimize recordmcount.pl for offsets-handling
tracing: Use appropriate perl constructs in recordmcount.pl
----
include/linux/ftrace_event.h | 3 +- include/linux/syscalls.h | 6 +- include/trace/ftrace.h | 161 +++++++++-------------------------------- include/trace/syscall.h | 4 - kernel/trace/trace.c | 144 ++++++++++++++++++++++++++----------- kernel/trace/trace_events.c | 77 +++++++++++--------- kernel/trace/trace_export.c | 87 ++++------------------- kernel/trace/trace_kprobe.c | 104 +++++++++++--------------- kernel/trace/trace_syscalls.c | 106 ++++++++++++++-------------- scripts/recordmcount.pl | 47 +++++------- 10 files changed, 310 insertions(+), 429 deletions(-)
Steven Rostedt
maintainerPATCH 1/11 tracing: Have __dynamic_array() define a field
From: Lai Jiangshan <[email protected]>
This is part of a patch set that removes the show_format method in the ftrace event macros.
This patch set requires that all fields are added to the ftrace_event_call->fields. This patch changes __dynamic_array() to call trace_define_field() to include fields that use __dynamic_array().
LKML-Reference: <[email protected]>
--- kernel/trace/trace_export.c | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-)
kernel/trace/trace_export.c +6 -1
@@ -175,7 +175,12 @@ ftrace_format_##name(struct ftrace_event_call *unused, \return ret;#undef __dynamic_array -#define __dynamic_array (type, item) +#define __dynamic_array (type, item) \ + ret = trace_define_field(event_call, #type, #item, \ + offsetof(typeof(field), item), \ + 0, is_signed_type(type), FILTER_OTHER);\ + if (ret) \ + return ret;#undef FTRACE_ENTRY#define FTRACE_ENTRY (name, struct_name, id, tstruct, print) \
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 2/11 tracing: Add print_fmt field
In reply to Steven RostedtFrom: Lai Jiangshan <[email protected]>
This is part of a patch set that removes the show_format method in the ftrace event macros.
The print_fmt field is added to hold the string that shows the print_fmt in the event format files. This patch only adds the field but it is currently not used. Later patches will use this field to enable us to remove the show_format field and function.
LKML-Reference: <[email protected]>
--- include/linux/ftrace_event.h | 1 + include/trace/ftrace.h | 28 +++++++++++++++++++++++++++- kernel/trace/trace_export.c | 7 +++++++ 3 files changed, 35 insertions(+), 1 deletions(-)
include/linux/ftrace_event.h +1 -0
@@ -121,6 +121,7 @@ struct ftrace_event_call {int (*regfunc)(struct ftrace_event_call *);void (*unregfunc)(struct ftrace_event_call *);int id; +const char *print_fmt;int (*raw_init)(struct ftrace_event_call *);int (*show_format)(struct ftrace_event_call *,struct trace_seq *);
include/trace/ftrace.h +27 -1
@@ -722,8 +722,20 @@ static struct trace_event ftrace_event_type_##call = { \#include TRACE_INCLUDE (TRACE_INCLUDE_FILE ) +#undef __entry +#define __entry REC + +#undef __print_flags +#undef __print_symbolic +#undef __get_dynamic_array +#undef __get_str + +#undef TP_printk +#define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args) +#undef DECLARE_EVENT_CLASS -#define DECLARE_EVENT_CLASS (call, proto, args, tstruct, assign, print) +#define DECLARE_EVENT_CLASS (call, proto, args, tstruct, assign, print) \ +static const char print_fmt_##call[] = print;#undef DEFINE_EVENT#define DEFINE_EVENT (template, call, proto, args) \
@@ -737,6 +749,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ .raw_init = trace_event_raw_init, \ .regfunc = ftrace_raw_reg_event_##call, \ .unregfunc = ftrace_raw_unreg_event_##call, \ + .print_fmt = print_fmt_##template, \ .show_format = ftrace_format_##template, \ .define_fields = ftrace_define_fields_##template, \ _TRACE_PROFILE_INIT(call) \
@@ -745,6 +758,8 @@ __attribute__((section("_ftrace_events"))) event_##call = { \#undef DEFINE_EVENT_PRINT#define DEFINE_EVENT_PRINT (template, call, proto, args, print) \ \ +static const char print_fmt_##call[] = print; \ + \ static struct ftrace_event_call __used \ __attribute__((__aligned__(4))) \ __attribute__((section("_ftrace_events"))) event_##call = { \
@@ -754,6 +769,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ .raw_init = trace_event_raw_init, \ .regfunc = ftrace_raw_reg_event_##call, \ .unregfunc = ftrace_raw_unreg_event_##call, \ + .print_fmt = print_fmt_##call, \ .show_format = ftrace_format_##call, \ .define_fields = ftrace_define_fields_##template, \ _TRACE_PROFILE_INIT(call) \
@@ -837,6 +853,16 @@ __attribute__((section("_ftrace_events"))) event_##call = { \#ifdef CONFIG_EVENT_PROFILE +#undef __entry +#define __entry entry + +#undef __get_dynamic_array +#define __get_dynamic_array (field) \ + ((void *)__entry + (__entry->__data_loc_##field & 0xffff)) + +#undef __get_str +#define __get_str (field) (char *)__get_dynamic_array(field) +#undef __perf_addr#define __perf_addr (a) __addr = (a)
kernel/trace/trace_export.c +7 -0
@@ -203,6 +203,9 @@ static int ftrace_raw_init_event(struct ftrace_event_call *call)return 0 ; } +#undef __entry +#define __entry REC +#undef __field#define __field (type, item)
@@ -218,6 +221,9 @@ static int ftrace_raw_init_event(struct ftrace_event_call *call)#undef __dynamic_array#define __dynamic_array (type, item) +#undef F_printk +#define F_printk(fmt, args...) #fmt ", " __stringify(args) +#undef FTRACE_ENTRY#define FTRACE_ENTRY (call, struct_name, type, tstruct, print) \ \
@@ -228,6 +234,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ .id = type, \ .system = __stringify(TRACE_SYSTEM), \ .raw_init = ftrace_raw_init_event, \ + .print_fmt = print, \ .show_format = ftrace_format_##call, \ .define_fields = ftrace_define_fields_##call, \ }; \
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 3/11 tracing/syscalls: Init print_fmt for syscall events
In reply to Steven RostedtFrom: Lai Jiangshan <[email protected]>
This is part of a patch set that removes the show_format method in the ftrace event macros.
Add the print_fmt initialization to the syscall events. The print_fmt is still not used, but will be in the follow up patches.
LKML-Reference: <[email protected]>
--- kernel/trace/trace_syscalls.c | 68 ++++++++++++++++++++++++++++++++++++++++- 1 files changed, 67 insertions(+), 1 deletions(-)
kernel/trace/trace_syscalls.c +67 -1
@@ -191,6 +191,67 @@ int syscall_enter_format(struct ftrace_event_call *call, struct trace_seq *s)return trace_seq_putc (s,'\n' ); } +static +int __set_enter_print_fmt (struct syscall_metadata *entry,char *buf,int len) +{ +int i; +int pos =0 ; + +/* When len=0, we just calculate the needed length */ +#define LEN_OR_ZERO (len ? len - pos : 0) + + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"" ); +for (i =0 ; i < entry->nb_args; i++) { + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"%s: 0x%%0%zulx%s" , + entry->args[i],sizeof (unsigned long ), + i == entry->nb_args -1 ?"" :", " ); + } + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"" ); + +for (i =0 ; i < entry->nb_args; i++) { + pos +=snprintf (buf + pos,LEN_OR_ZERO , +", ((unsigned long)(REC->%s))" , entry->args[i]); + } + +#undef LEN_OR_ZERO + +/* return the length of print_fmt */ +return pos; +} + +static int set_syscall_print_fmt (struct ftrace_event_call *call) +{ +char *print_fmt; +int len; +struct syscall_metadata *entry = call->data; + +if (entry->enter_event != call) { + call->print_fmt ="\"0x%lx\", REC->ret" ; +return 0 ; + } + +/* First: called with 0 length to calculate the needed length */ + len =__set_enter_print_fmt (entry,NULL ,0 ); + + print_fmt =kmalloc (len +1 ,GFP_KERNEL ); +if (!print_fmt) +return -ENOMEM ; + +/* Second: actually write the @print_fmt */ +__set_enter_print_fmt (entry, print_fmt, len +1 ); + call->print_fmt = print_fmt; + +return 0 ; +} + +static void free_syscall_print_fmt (struct ftrace_event_call *call) +{ +struct syscall_metadata *entry = call->data; + +if (entry->enter_event == call) +kfree (call->print_fmt); +} +int syscall_exit_format (struct ftrace_event_call *call,struct trace_seq *s) {int ret;
@@ -386,9 +447,14 @@ int init_syscall_trace(struct ftrace_event_call *call) {int id; +if (set_syscall_print_fmt (call) <0 ) +return -ENOMEM ; + id =register_ftrace_event (call->event); -if (!id) +if (!id) { +free_syscall_print_fmt (call);return -ENODEV ; + } call->id = id;INIT_LIST_HEAD (&call->fields);return 0 ;
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 4/11 tracing/kprobes: Init print_fmt for kprobe events
In reply to Steven RostedtFrom: Lai Jiangshan <[email protected]>
This is part of a patch set that removes the show_format method in the ftrace event macros.
Add the print_fmt initialization to the kprobe events. The print_fmt is still not used, but will be in the follow up patches.
LKML-Reference: <[email protected]>
--- kernel/trace/trace_kprobe.c | 64 ++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 63 insertions(+), 1 deletions(-)
kernel/trace/trace_kprobe.c +63 -1
@@ -1250,6 +1250,62 @@ static int kretprobe_event_show_format(struct ftrace_event_call *call, ", REC->" FIELD_STRING_RETIP); } +static int __set_print_fmt (struct trace_probe *tp,char *buf,int len) +{ +int i; +int pos =0 ; + +const char *fmt, *arg; + +if (!probe_is_return (tp)) { + fmt ="(%lx)" ; + arg ="REC->" FIELD_STRING_IP ; + }else { + fmt ="(%lx <- %lx)" ; + arg ="REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP ; + } + +/* When len=0, we just calculate the needed length */ +#define LEN_OR_ZERO (len ? len - pos : 0) + + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"%s" , fmt); + +for (i =0 ; i < tp->nr_args; i++) { + pos +=snprintf (buf + pos,LEN_OR_ZERO ," %s=%%lx" , + tp->args[i].name); + } + + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\", %s" , arg); + +for (i =0 ; i < tp->nr_args; i++) { + pos +=snprintf (buf + pos,LEN_OR_ZERO ,", REC->%s" , + tp->args[i].name); + } + +#undef LEN_OR_ZERO + +/* return the length of print_fmt */ +return pos; +} + +static int set_print_fmt (struct trace_probe *tp) +{ +int len; +char *print_fmt; + +/* First: called with 0 length to calculate the needed length */ + len =__set_print_fmt (tp,NULL ,0 ); + print_fmt =kmalloc (len +1 ,GFP_KERNEL ); +if (!print_fmt) +return -ENOMEM ; + +/* Second: actually write the @print_fmt */ +__set_print_fmt (tp, print_fmt, len +1 ); + tp->call.print_fmt = print_fmt; + +return 0 ; +} +#ifdef CONFIG_EVENT_PROFILE /* Kprobe profile handler */
@@ -1456,10 +1512,14 @@ static int register_probe_event(struct trace_probe *tp) call->show_format = kprobe_event_show_format; call->define_fields = kprobe_event_define_fields; } +if (set_print_fmt (tp) <0 ) +return -ENOMEM ; call->event = &tp->event; call->id =register_ftrace_event (&tp->event); -if (!call->id) +if (!call->id) { +kfree (call->print_fmt);return -ENODEV ; + } call->enabled =0 ; call->regfunc = probe_event_enable; call->unregfunc = probe_event_disable;
@@ -1472,6 +1532,7 @@ static int register_probe_event(struct trace_probe *tp) ret =trace_add_event_call (call);if (ret) {pr_info ("Failed to register kprobe event: %s\n" , call->name); +kfree (call->print_fmt);unregister_ftrace_event (&tp->event); }return ret;
@@ -1481,6 +1542,7 @@ static void unregister_probe_event(struct trace_probe *tp) {/* tp->event is unregistered in trace_remove_event_call() */ trace_remove_event_call (&tp->call); +kfree (tp->call.print_fmt); }/* Make a debugfs interface for controling probe points */
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 5/11 tracing: Have syscall tracing call its own init function
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
In the clean up of having all events call one specific function, the syscall event init was changed to call this helper function.
With the new print_fmt updates, the syscalls need to do special initializations. This patch converts the syscall events to call its own init function again.
--- include/linux/syscalls.h | 4 ++-- kernel/trace/trace_syscalls.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-)
include/linux/syscalls.h +2 -2
@@ -143,7 +143,7 @@ struct perf_event_attr; .name = "sys_enter"#sname, \ .system = "syscalls", \ .event = &enter_syscall_print_##sname, \ - .raw_init = trace_event_raw_init, \ + .raw_init = init_syscall_trace, \ .show_format = syscall_enter_format, \ .define_fields = syscall_enter_define_fields, \ .regfunc = reg_event_syscall_enter, \
@@ -165,7 +165,7 @@ struct perf_event_attr; .name = "sys_exit"#sname, \ .system = "syscalls", \ .event = &exit_syscall_print_##sname, \ - .raw_init = trace_event_raw_init, \ + .raw_init = init_syscall_trace, \ .show_format = syscall_exit_format, \ .define_fields = syscall_exit_define_fields, \ .regfunc = reg_event_syscall_exit, \
kernel/trace/trace_syscalls.c +6 -6
@@ -450,14 +450,14 @@ int init_syscall_trace(struct ftrace_event_call *call)if (set_syscall_print_fmt (call) <0 )return -ENOMEM ; - id =register_ftrace_event (call->event); -if (!id) { + id =trace_event_raw_init (call); + +if (id <0 ) {free_syscall_print_fmt (call); -return -ENODEV ; +return id; } - call->id = id; -INIT_LIST_HEAD (&call->fields); -return 0 ; + +return id; }int __init init_ftrace_syscalls (void )
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 6/11 tracing: Use defined fields and print_fmt to print formats
In reply to Steven RostedtFrom: Lai Jiangshan <[email protected]>
The calls ftrace_format_##call() and ftrace_define_fields_##call() are almost duplicate in functionality. With the addition of the print_fmt in previous patches, these two functions can be merged into one.
The trace_define_field() defines the fields and links them into the struct ftrace_event_call. The previous patches introduced the print_fmt field and this can now be used with the trace_define_field() to create the event format file fields and print_fmt field.
The struct ftrace_event_call->fields are used to print the fields The struct ftrace_event_call->print_fmt is used to print the "print fmt: XXXXXXXXXXX" line.
LKML-Reference: <[email protected]>
--- kernel/trace/trace_events.c | 65 +++++++++++++++++++++++++++---------------- 1 files changed, 41 insertions(+), 24 deletions(-)
kernel/trace/trace_events.c +41 -24
@@ -528,33 +528,16 @@ extern char *__bad_type_size(void);#type , "common_" #name, offsetof(typeof(field), name), \ sizeof(field.name), is_signed_type(type) -static int trace_write_header (struct trace_seq *s) -{ -struct trace_entry field; - -/* struct trace_entry */ -return trace_seq_printf (s, -"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" -"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" -"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" -"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" -"\tfield:%s %s;\toffset:%zu;\tsize:%zu;\tsigned:%u;\n" -"\n" , - FIELD(unsigned short, type), - FIELD(unsigned char, flags), - FIELD(unsigned char, preempt_count), -FIELD (int, pid), -FIELD (int, lock_depth)); -} -static ssize_t event_format_read (struct file *filp,char __user *ubuf,size_t cnt,loff_t *ppos) {struct ftrace_event_call *call = filp->private_data; +struct ftrace_event_field *field;struct trace_seq *s; +int common_field_count =5 ;char *buf; -int r; +int r =0 ;if (*ppos)return 0 ;
@@ -565,14 +548,48 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt,trace_seq_init (s); -/* If any of the first writes fail, so will the show_format. */ -trace_seq_printf (s,"name: %s\n" , call->name);trace_seq_printf (s,"ID: %d\n" , call->id);trace_seq_printf (s,"format:\n" ); -trace_write_header (s); - r = call->show_format (call, s); +list_for_each_entry_reverse (field, &call->fields, link) { +/* +* Smartly shows the array type(except dynamic array). +* Normal: +* field:TYPE VAR +* If TYPE := TYPE[LEN], it is shown: +* field:TYPE VAR[LEN] +*/ +const char *array_descriptor =strchr (field->type,'[' ); + +if (!strncmp (field->type,"__data_loc" ,10 )) + array_descriptor =NULL ; + +if (!array_descriptor) { + r =trace_seq_printf (s,"\tfield:%s %s;\toffset:%u;" +"\tsize:%u;\tsigned:%d;\n" , + field->type, field->name, field->offset, + field->size, !!field->is_signed); + }else { + r =trace_seq_printf (s,"\tfield:%.*s %s%s;\toffset:%u;" +"\tsize:%u;\tsigned:%d;\n" , + (int )(array_descriptor - field->type), + field->type, field->name, + array_descriptor, field->offset, + field->size, !!field->is_signed); + } + +if (--common_field_count ==0 ) + r =trace_seq_printf (s,"\n" ); + +if (!r) +break ; + } + +if (r) + r =trace_seq_printf (s,"\nprint fmt: %s\n" , + call->print_fmt); +if (!r) {/* * ug! The format output is bigger than a PAGE!!
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 7/11 tracing: Remove show_format and related macros from TRACE_EVENT
In reply to Steven RostedtFrom: Lai Jiangshan <[email protected]>
The previous patches added the use of print_fmt string and changes the trace_define_field() function to also create the fields and format output for the event format files.
text data bss dec hex filename
5857201 1355780 9336808 16549789 fc879d vmlinux
5884589 1351684 9337896 16574169 fce6d9 vmlinux-orig
The above shows the size of the vmlinux after this patch set compared to the vmlinux-orig which is before the patch set.
This saves us 27k on text, 1k on bss and adds just 4k of data.
The total savings of 24k in size.
LKML-Reference: <[email protected]>
--- include/linux/ftrace_event.h | 2 - include/linux/syscalls.h | 2 - include/trace/ftrace.h | 133 ++--------------------------------------- include/trace/syscall.h | 4 - kernel/trace/trace_events.c | 12 ---- kernel/trace/trace_export.c | 73 ---------------------- kernel/trace/trace_kprobe.c | 78 ------------------------ kernel/trace/trace_syscalls.c | 66 -------------------- 8 files changed, 6 insertions(+), 364 deletions(-)
include/linux/ftrace_event.h +0 -2
@@ -123,8 +123,6 @@ struct ftrace_event_call {int id;const char *print_fmt;int (*raw_init)(struct ftrace_event_call *); -int (*show_format)(struct ftrace_event_call *, -struct trace_seq *);int (*define_fields)(struct ftrace_event_call *);struct list_head fields;int filter_active;
include/linux/syscalls.h +0 -2
@@ -144,7 +144,6 @@ struct perf_event_attr; .system = "syscalls", \ .event = &enter_syscall_print_##sname, \ .raw_init = init_syscall_trace, \ - .show_format = syscall_enter_format, \ .define_fields = syscall_enter_define_fields, \ .regfunc = reg_event_syscall_enter, \ .unregfunc = unreg_event_syscall_enter, \
@@ -166,7 +165,6 @@ struct perf_event_attr; .system = "syscalls", \ .event = &exit_syscall_print_##sname, \ .raw_init = init_syscall_trace, \ - .show_format = syscall_exit_format, \ .define_fields = syscall_exit_define_fields, \ .regfunc = reg_event_syscall_exit, \ .unregfunc = unreg_event_syscall_exit, \
include/trace/ftrace.h +6 -127
163 lines not on this page · raw message
include/trace/syscall.h +0 -4
@@ -34,10 +34,6 @@ struct syscall_metadata {extern unsigned long arch_syscall_addr (int nr);extern int init_syscall_trace (struct ftrace_event_call *call); -extern int syscall_enter_format (struct ftrace_event_call *call, -struct trace_seq *s); -extern int syscall_exit_format (struct ftrace_event_call *call, -struct trace_seq *s);extern int syscall_enter_define_fields (struct ftrace_event_call *call);extern int syscall_exit_define_fields (struct ftrace_event_call *call);extern int reg_event_syscall_enter (struct ftrace_event_call *call);
kernel/trace/trace_events.c +0 -12
@@ -520,14 +520,6 @@ out:return ret; } -extern char *__bad_type_size (void ); - -#undef FIELD -#define FIELD (type, name) \ - sizeof(type) != sizeof(field.name) ? __bad_type_size() : \ - #type, "common_" #name, offsetof(typeof(field), name), \ - sizeof(field.name), is_signed_type(type) -static ssize_t event_format_read (struct file *filp,char __user *ubuf,size_t cnt,loff_t *ppos)
@@ -965,10 +957,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, filter); } -/* A trace may not want to export its format */ -if (!call->show_format) -return 0 ; -trace_create_file ("format" ,0444 , call->dir, call, format);
kernel/trace/trace_export.c +0 -73
85 lines not on this page · raw message
kernel/trace/trace_kprobe.c +0 -78
94 lines not on this page · raw message
kernel/trace/trace_syscalls.c +0 -66
78 lines not on this page · raw message
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 8/11 tracing: Consolidate protection of reader access to the ring buffer
In reply to Steven RostedtFrom: Lai Jiangshan <[email protected]>
At the beginning, access to the ring buffer was fully serialized by trace_types_lock. Patch d7350c3f4569 gives more freedom to readers, and patch b04cc6b1f6 adds code to protect trace_pipe and cpu#/trace_pipe.
But actually it is not enough, ring buffer readers are not always read-only, they may consume data.
This patch makes accesses to trace, trace_pipe, trace_pipe_raw cpu#/trace, cpu#/trace_pipe and cpu#/trace_pipe_raw serialized. And removes tracing_reader_cpumask which is used to protect trace_pipe.
Details:
Ring buffer serializes readers, but it is low level protection. The validity of the events (which returns by ring_buffer_peek() ..etc) are not protected by ring buffer.
The content of events may become garbage if we allow another process to consume these events concurrently:
A) the page of the consumed events may become a normal page
(not reader page) in ring buffer, and this page will be rewritten by the events producer.
B) The page of the consumed events may become a page for splice_read, and this page will be returned to system.
This patch adds trace_access_lock() and trace_access_unlock() primitives.
These primitives allow multi process access to different cpu ring buffers concurrently.
These primitives don't distinguish read-only and read-consume access. Multi read-only access is also serialized.
And we don't use these primitives when we open files, we only use them when we read files.
LKML-Reference: <[email protected]>
--- kernel/trace/trace.c | 136 +++++++++++++++++++++++++++++++++++-------------- 1 files changed, 97 insertions(+), 39 deletions(-)
kernel/trace/trace.c +97 -39
@@ -32,6 +32,7 @@#include <linux/splice.h> #include <linux/kdebug.h> #include <linux/string.h> +#include <linux/rwsem.h> #include <linux/ctype.h> #include <linux/init.h> #include <linux/poll.h>
@@ -102,9 +103,6 @@ static inline void ftrace_enable_cpu(void)static cpumask_var_t __read_mostly tracing_buffer_mask; -/* Define which cpu buffers are currently read in trace_pipe */ -static cpumask_var_t tracing_reader_cpumask; -#define for_each_tracing_cpu (cpu) \ for_each_cpu(cpu, tracing_buffer_mask)
@@ -243,12 +241,91 @@ static struct tracer *current_trace __read_mostly;/* * trace_types_lock is used to protect the trace_types list. -* This lock is also used to keep user access serialized. -* Accesses from userspace will grab this lock while userspace -* activities happen inside the kernel. */ static DEFINE_MUTEX (trace_types_lock); +/* +* serialize the access of the ring buffer +* +* ring buffer serializes readers, but it is low level protection. +* The validity of the events (which returns by ring_buffer_peek() ..etc) +* are not protected by ring buffer. +* +* The content of events may become garbage if we allow other process consumes +* these events concurrently: +* A) the page of the consumed events may become a normal page +* (not reader page) in ring buffer, and this page will be rewrited +* by events producer. +* B) The page of the consumed events may become a page for splice_read, +* and this page will be returned to system. +* +* These primitives allow multi process access to different cpu ring buffer +* concurrently. +* +* These primitives don't distinguish read-only and read-consume access. +* Multi read-only access are also serialized. +*/ + +#ifdef CONFIG_SMP +static DECLARE_RWSEM (all_cpu_access_lock); +static DEFINE_PER_CPU(struct mutex, cpu_access_lock); + +static inline void trace_access_lock (int cpu) +{ +if (cpu ==TRACE_PIPE_ALL_CPU ) { +/* gain it for accessing the whole ring buffer. */ +down_write (&all_cpu_access_lock); + }else { +/* gain it for accessing a cpu ring buffer. */ + +/* Firstly block other trace_access_lock(TRACE_PIPE_ALL_CPU). */ +down_read (&all_cpu_access_lock); + +/* Secondly block other access to this @cpu ring buffer. */ +mutex_lock (&per_cpu (cpu_access_lock, cpu)); + } +} + +static inline void trace_access_unlock (int cpu) +{ +if (cpu ==TRACE_PIPE_ALL_CPU ) { +up_write (&all_cpu_access_lock); + }else { +mutex_unlock (&per_cpu (cpu_access_lock, cpu)); +up_read (&all_cpu_access_lock); + } +} + +static inline void trace_access_lock_init (void ) +{ +int cpu; + +for_each_possible_cpu (cpu) +mutex_init (&per_cpu (cpu_access_lock, cpu)); +} + +#else + +static DEFINE_MUTEX (access_lock); + +static inline void trace_access_lock (int cpu) +{ + (void )cpu; +mutex_lock (&access_lock); +} + +static inline void trace_access_unlock (int cpu) +{ + (void )cpu; +mutex_unlock (&access_lock); +} + +static inline void trace_access_lock_init (void ) +{ +} + +#endif +/* trace_wait is a waitqueue for tasks blocked on trace_poll */ static DECLARE_WAIT_QUEUE_HEAD (trace_wait);
@@ -1580,12 +1657,6 @@ static void tracing_iter_reset(struct trace_iterator *iter, int cpu) }/* -* No necessary locking here. The worst thing which can -* happen is loosing events consumed at the same time -* by a trace_pipe reader. -* Other than that, we don't risk to crash the ring buffer -* because it serializes the readers. -* * The current tracer is copied to avoid a global locking * all around. */
@@ -1640,12 +1711,16 @@ static void *s_start(struct seq_file *m, loff_t *pos) }trace_event_read_lock (); +trace_access_lock (cpu_file);return p; }static void s_stop (struct seq_file *m,void *p) { +struct trace_iterator *iter = m->private; +atomic_dec (&trace_record_cmdline_disabled); +trace_access_unlock (iter->cpu_file);trace_event_read_unlock (); }
@@ -2836,22 +2911,6 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)mutex_lock (&trace_types_lock); -/* We only allow one reader per cpu */ -if (cpu_file ==TRACE_PIPE_ALL_CPU ) { -if (!cpumask_empty (tracing_reader_cpumask)) { - ret = -EBUSY ; - goto out; - } -cpumask_setall (tracing_reader_cpumask); - }else { -if (!cpumask_test_cpu (cpu_file, tracing_reader_cpumask)) -cpumask_set_cpu (cpu_file, tracing_reader_cpumask); -else { - ret = -EBUSY ; - goto out; - } - } -/* create a buffer to store the information to pass to userspace */ iter =kzalloc (sizeof (*iter),GFP_KERNEL );if (!iter) {
@@ -2907,12 +2966,6 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)mutex_lock (&trace_types_lock); -if (iter->cpu_file ==TRACE_PIPE_ALL_CPU ) -cpumask_clear (tracing_reader_cpumask); -else -cpumask_clear_cpu (iter->cpu_file, tracing_reader_cpumask); - -if (iter->trace->pipe_close) iter->trace->pipe_close (iter);
@@ -3074,6 +3127,7 @@ waitagain: iter->pos =-1 ;trace_event_read_lock (); +trace_access_lock (iter->cpu_file);while (find_next_entry_inc (iter) !=NULL ) {enum print_line_t ret;int len = iter->seq.len;
@@ -3090,6 +3144,7 @@ waitagain:if (iter->seq.len >= cnt)break ; } +trace_access_unlock (iter->cpu_file);trace_event_read_unlock ();/* Now copy what we have to the user */
@@ -3215,6 +3270,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, }trace_event_read_lock (); +trace_access_lock (iter->cpu_file);/* Fill as many pages as possible. */ for (i =0 , rem = len; i <PIPE_BUFFERS && rem; i++) {
@@ -3238,6 +3294,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,trace_seq_init (&iter->seq); } +trace_access_unlock (iter->cpu_file);trace_event_read_unlock ();mutex_unlock (&iter->mutex);
@@ -3539,10 +3596,12 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, info->read =0 ; +trace_access_lock (info->cpu); ret =ring_buffer_read_page (info->tr->buffer, &info->spare, count, info->cpu,0 ); +trace_access_unlock (info->cpu);if (ret <0 )return 0 ;
@@ -3670,6 +3729,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, len &=PAGE_MASK ; } +trace_access_lock (info->cpu); entries =ring_buffer_entries_cpu (info->tr->buffer, info->cpu);for (i =0 ; i <PIPE_BUFFERS && len && entries; i++, len -=PAGE_SIZE ) {
@@ -3717,6 +3777,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, entries =ring_buffer_entries_cpu (info->tr->buffer, info->cpu); } +trace_access_unlock (info->cpu); spd.nr_pages = i;/* did we read anything? */
@@ -4153,6 +4214,8 @@ static __init int tracer_init_debugfs(void)struct dentry *d_tracer;int cpu; +trace_access_lock_init (); + d_tracer =tracing_init_dentry (); trace_create_file("tracing_enabled", 0644, d_tracer,
@@ -4387,9 +4450,6 @@ __init static int tracer_alloc_buffers(void)if (!alloc_cpumask_var (&tracing_cpumask,GFP_KERNEL )) goto out_free_buffer_mask; -if (!zalloc_cpumask_var (&tracing_reader_cpumask,GFP_KERNEL )) - goto out_free_tracing_cpumask; -/* To save memory, keep the ring buffer size to its minimum */ if (ring_buffer_expanded) ring_buf_size = trace_buf_size;
@@ -4447,8 +4507,6 @@ __init static int tracer_alloc_buffers(void)return 0 ; out_free_cpumask: -free_cpumask_var (tracing_reader_cpumask); -out_free_tracing_cpumask:free_cpumask_var (tracing_cpumask); out_free_buffer_mask:free_cpumask_var (tracing_buffer_mask);
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 9/11 tracing: optimize recordmcount.pl for offsets-handling
In reply to Steven RostedtFrom: Wolfram Sang <[email protected]>
- move check for open file in front of the writing loop
- use perl-constructs to access the array
LKML-Reference: <[email protected]>
--- scripts/recordmcount.pl | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-)
scripts/recordmcount.pl +9 -9
@@ -432,14 +432,14 @@ sub update_funcs # Loop through all the mcount caller offsets and print a reference # to the caller based from the ref_func. - for (my $i=0; $i <= $#offsets; $i++) { - if (!$opened) { - open(FILE, ">$mcount_s") || die "can't create $mcount_s\n"; - $opened = 1; - print FILE "\t.section $mcount_section,\"a\",$section_type\n"; - print FILE "\t.align $alignment\n" if (defined($alignment)); - } - printf FILE "\t%s %s + %d\n", $type, $ref_func, $offsets[$i] - $offset; + if (!$opened) { + open(FILE, ">$mcount_s") || die "can't create $mcount_s\n"; + $opened = 1; + print FILE "\t.section $mcount_section,\"a\",$section_type\n"; + print FILE "\t.align $alignment\n" if (defined($alignment)); + } + foreach my $cur_offset (@offsets) { + printf FILE "\t%s %s + %d\n", $type, $ref_func, $cur_offset - $offset; } }
@@ -514,7 +514,7 @@ while (<IN>) { } # is this a call site to mcount? If so, record it to print later if ($text_found && /$mcount_regex/) { - $offsets[$#offsets + 1] = hex $1; + push(@offsets, hex $1); } }
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 10/11 tracing: Use appropriate perl constructs in recordmcount.pl
In reply to Steven RostedtFrom: Wolfram Sang <[email protected]>
Modified recordmcount.pl to use perl constructs that are still understandable by C hackers that are not perl programmers.
LKML-Reference: <[email protected]>
--- scripts/recordmcount.pl | 29 +++++++++++------------------ 1 files changed, 11 insertions(+), 18 deletions(-)
scripts/recordmcount.pl +11 -18
@@ -136,13 +136,14 @@ my %text_sections = ( ".text.unlikely" => 1, ); -$objdump = "objdump" if ((length $objdump) == 0); -$objcopy = "objcopy" if ((length $objcopy) == 0); -$cc = "gcc" if ((length $cc) == 0); -$ld = "ld" if ((length $ld) == 0); -$nm = "nm" if ((length $nm) == 0); -$rm = "rm" if ((length $rm) == 0); -$mv = "mv" if ((length $mv) == 0); +# Note: we are nice to C-programmers here, thus we skip the '||='-idiom. +$objdump = 'objdump' if (!$objdump); +$objcopy = 'objcopy' if (!$objcopy); +$cc = 'gcc' if (!$cc); +$ld = 'ld' if (!$ld); +$nm = 'nm' if (!$nm); +$rm = 'rm' if (!$rm); +$mv = 'mv' if (!$mv); #print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " . # "'$nm' '$rm' '$mv' '$inputfile'\n";
@@ -194,12 +195,8 @@ sub check_objcopy } } -if ($arch eq "x86") { - if ($bits == 64) { - $arch = "x86_64"; - } else { - $arch = "i386"; - } +if ($arch eq 'x86') { + $arch = ($bits == 64) ? 'x86_64' : 'i386'; } #
@@ -476,11 +473,7 @@ while (<IN>) { $read_headers = 0; # Only record text sections that we know are safe - if (defined($text_sections{$1})) { - $read_function = 1; - } else { - $read_function = 0; - } + $read_function = defined($text_sections{$1}); # print out any recorded offsets update_funcs();
Signature · 1 line
1.6.5
Steven Rostedt
maintainerPATCH 11/11 tracing: Add stack dump to trace_printk if stacktrace option is set
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
If the ftrace stacktrace option is set, then add the stack dumps to trace_printk.
--- kernel/trace/trace.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-)
kernel/trace/trace.c +6 -2
@@ -1392,8 +1392,10 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args) entry->fmt = fmt;memcpy (entry->buf, trace_buf,sizeof (u32) * len); -if (!filter_check_discard (call, entry, buffer, event)) +if (!filter_check_discard (call, entry, buffer, event)) {ring_buffer_unlock_commit (buffer, event); +ftrace_trace_stack (buffer, flags,6 , pc); + } out_unlock:arch_spin_unlock (&trace_buf_lock);
@@ -1466,8 +1468,10 @@ int trace_array_vprintk(struct trace_array *tr,memcpy (&entry->buf, trace_buf, len); entry->buf[len] ='\0' ; -if (!filter_check_discard (call, entry, buffer, event)) +if (!filter_check_discard (call, entry, buffer, event)) {ring_buffer_unlock_commit (buffer, event); +ftrace_trace_stack (buffer, irq_flags,6 , pc); + } out_unlock:arch_spin_unlock (&trace_buf_lock);
Signature · 1 line
1.6.5
Quoted 27 lines · Steven Rostedt
Ingo,
Please pull the latest tip/tracing/core tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace.git
tip/tracing/coreLai Jiangshan (7):
tracing: Have__dynamic_array()define a field
tracing: Addprint_fmtfield
tracing/syscalls: Initprint_fmtfor syscall events tracing/kprobes: Initprint_fmtfor kprobe events
tracing: Use defined fields andprint_fmtto print formats
tracing: Removeshow_formatand related macros fromTRACE_EVENT
tracing: Consolidate protection of reader access to the ring bufferSteven Rostedt (2):
tracing: Have syscall tracing call its own init function
tracing: Add stack dump totrace_printkif stacktrace option is setWolfram Sang (2):
tracing: optimize recordmcount.pl for offsets-handling
tracing: Use appropriate perl constructs in recordmcount.pl----
Quoted 11 lines
include/linux/ftrace_event.h | 3 +- include/linux/syscalls.h | 6 +- include/trace/ftrace.h | 161 +++++++++-------------------------------- include/trace/syscall.h | 4 - kernel/trace/trace.c | 144 ++++++++++++++++++++++++++----------- kernel/trace/trace_events.c | 77 +++++++++++--------- kernel/trace/trace_export.c | 87 ++++------------------- kernel/trace/trace_kprobe.c | 104 +++++++++++--------------- kernel/trace/trace_syscalls.c | 106 ++++++++++++++-------------- scripts/recordmcount.pl | 47 +++++------- 10 files changed, 310 insertions(+), 429 deletions(-)
Pulled, thanks a lot Steve!
Ingo