Commit 2a37a3df authored by Steven Rostedt's avatar Steven Rostedt Committed by Steven Rostedt

tracing/events: Convert format output to seq_file

Two new events were added that broke the current format output.

Both from the SCSI system: scsi_dispatch_cmd_done and scsi_dispatch_cmd_timeout

The reason is that their print_fmt exceeded a page size. Since the output
of the format used simple_read_from_buffer and trace_seq, it was limited
to a page size in output.

This patch converts the printing of the format of an event into seq_file,
which allows greater than a page size to be shown.

I diffed all event formats comparing the output with and without this
patch. All matched except for the above two, which showed just:

  FORMAT TOO BIG

without this patch, but now properly displays the output with this patch.

v2: Remove updating *pos in seq start function.
   [ Thanks to Li Zefan for pointing that out ]
Reviewed-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Kei Tokunaga <tokunaga.keiich@jp.fujitsu.com>
Cc: James Bottomley <James.Bottomley@suse.de>
Cc: Tomohiro Kusumi <kusumi.tomohiro@jp.fujitsu.com>
Cc: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent 465c6cca
...@@ -29,6 +29,8 @@ DEFINE_MUTEX(event_mutex); ...@@ -29,6 +29,8 @@ DEFINE_MUTEX(event_mutex);
LIST_HEAD(ftrace_events); LIST_HEAD(ftrace_events);
#define COMMON_FIELD_COUNT 5
struct list_head * struct list_head *
trace_get_fields(struct ftrace_event_call *event_call) trace_get_fields(struct ftrace_event_call *event_call)
{ {
...@@ -544,85 +546,155 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, ...@@ -544,85 +546,155 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
return ret; return ret;
} }
static ssize_t enum {
event_format_read(struct file *filp, char __user *ubuf, size_t cnt, FORMAT_HEADER = 1,
loff_t *ppos) FORMAT_PRINTFMT = 2,
};
static void *f_next(struct seq_file *m, void *v, loff_t *pos)
{ {
struct ftrace_event_call *call = filp->private_data; struct ftrace_event_call *call = m->private;
struct ftrace_event_field *field; struct ftrace_event_field *field;
struct list_head *head; struct list_head *head;
struct trace_seq *s; loff_t index = *pos;
int common_field_count = 5;
char *buf;
int r = 0;
if (*ppos)
return 0;
s = kmalloc(sizeof(*s), GFP_KERNEL); (*pos)++;
if (!s)
return -ENOMEM;
trace_seq_init(s); head = trace_get_fields(call);
trace_seq_printf(s, "name: %s\n", call->name); switch ((unsigned long)v) {
trace_seq_printf(s, "ID: %d\n", call->event.type); case FORMAT_HEADER:
trace_seq_printf(s, "format:\n");
head = trace_get_fields(call); if (unlikely(list_empty(head)))
list_for_each_entry_reverse(field, head, link) { return NULL;
/*
* 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) field = list_entry(head->prev, struct ftrace_event_field, link);
r = trace_seq_printf(s, "\n"); return field;
if (!r) case FORMAT_PRINTFMT:
break; /* all done */
return NULL;
} }
if (r) /*
r = trace_seq_printf(s, "\nprint fmt: %s\n", * To separate common fields from event fields, the
call->print_fmt); * LSB is set on the first event field. Clear it in case.
*/
v = (void *)((unsigned long)v & ~1L);
if (!r) { field = v;
/* if (field->link.prev == head)
* ug! The format output is bigger than a PAGE!! return (void *)FORMAT_PRINTFMT;
*/
buf = "FORMAT TOO BIG\n"; field = list_entry(field->link.prev, struct ftrace_event_field, link);
r = simple_read_from_buffer(ubuf, cnt, ppos,
buf, strlen(buf)); /* Set the LSB to notify f_show to print an extra newline */
goto out; if (index == COMMON_FIELD_COUNT)
field = (struct ftrace_event_field *)
((unsigned long)field | 1);
return field;
}
static void *f_start(struct seq_file *m, loff_t *pos)
{
loff_t l = 0;
void *p;
/* Start by showing the header */
if (!*pos)
return (void *)FORMAT_HEADER;
p = (void *)FORMAT_HEADER;
do {
p = f_next(m, p, &l);
} while (p && l < *pos);
return p;
}
static int f_show(struct seq_file *m, void *v)
{
struct ftrace_event_call *call = m->private;
struct ftrace_event_field *field;
const char *array_descriptor;
switch ((unsigned long)v) {
case FORMAT_HEADER:
seq_printf(m, "name: %s\n", call->name);
seq_printf(m, "ID: %d\n", call->event.type);
seq_printf(m, "format:\n");
return 0;
case FORMAT_PRINTFMT:
seq_printf(m, "\nprint fmt: %s\n",
call->print_fmt);
return 0;
} }
r = simple_read_from_buffer(ubuf, cnt, ppos, /*
s->buffer, s->len); * To separate common fields from event fields, the
out: * LSB is set on the first event field. Clear it and
kfree(s); * print a newline if it is set.
return r; */
if ((unsigned long)v & 1) {
seq_putc(m, '\n');
v = (void *)((unsigned long)v & ~1L);
}
field = v;
/*
* Smartly shows the array type(except dynamic array).
* Normal:
* field:TYPE VAR
* If TYPE := TYPE[LEN], it is shown:
* field:TYPE VAR[LEN]
*/
array_descriptor = strchr(field->type, '[');
if (!strncmp(field->type, "__data_loc", 10))
array_descriptor = NULL;
if (!array_descriptor)
seq_printf(m, "\tfield:%s %s;\toffset:%u;\tsize:%u;\tsigned:%d;\n",
field->type, field->name, field->offset,
field->size, !!field->is_signed);
else
seq_printf(m, "\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);
return 0;
}
static void f_stop(struct seq_file *m, void *p)
{
}
static const struct seq_operations trace_format_seq_ops = {
.start = f_start,
.next = f_next,
.stop = f_stop,
.show = f_show,
};
static int trace_format_open(struct inode *inode, struct file *file)
{
struct ftrace_event_call *call = inode->i_private;
struct seq_file *m;
int ret;
ret = seq_open(file, &trace_format_seq_ops);
if (ret < 0)
return ret;
m = file->private_data;
m->private = call;
return 0;
} }
static ssize_t static ssize_t
...@@ -820,8 +892,10 @@ static const struct file_operations ftrace_enable_fops = { ...@@ -820,8 +892,10 @@ static const struct file_operations ftrace_enable_fops = {
}; };
static const struct file_operations ftrace_event_format_fops = { static const struct file_operations ftrace_event_format_fops = {
.open = tracing_open_generic, .open = trace_format_open,
.read = event_format_read, .read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
}; };
static const struct file_operations ftrace_event_id_fops = { static const struct file_operations ftrace_event_id_fops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment