Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a056155 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Ingo Molnar
Browse files

tracing/function-graph-tracer: adjustments of the trace informations



Impact: increase the visual qualities of the call-graph-tracer output

This patch applies various trace output formatting changes:

 - CPU is now a decimal number, followed by a parenthesis.

 - Overhead is now on the second column (gives a good visibility)

 - Cost is now on the third column, can't exceed 9999.99 us. It is
   followed by a virtual line based on a "|" character.

 - Functions calls are now the last column on the right. This way, we
   haven't dynamic column (which flow is harder to follow) on its right.

 - CPU and Overhead have their own option flag. They are default-on but you
   can disable them easily:

      echo nofuncgraph-cpu > trace_options
      echo nofuncgraph-overhead > trace_options

TODO:

_ Refactoring of the thread switch output.
_ Give a default-off option to output the thread and its pid on each row.
_ Provide headers
_ ....

Here is an example of the new trace style:

0)           |             mutex_unlock() {
0)      0.639 us |           __mutex_unlock_slowpath();
0)      1.607 us |         }
0)           |             remove_wait_queue() {
0)      0.616 us |           _spin_lock_irqsave();
0)      0.616 us |           _spin_unlock_irqrestore();
0)      2.779 us |         }
0)      0.495 us |         n_tty_set_room();
0) ! 9999.999 us |       }
0)           |           tty_ldisc_deref() {
0)      0.615 us |         _spin_lock_irqsave();
0)      0.616 us |         _spin_unlock_irqrestore();
0)      2.793 us |       }
0)           |           current_fs_time() {
0)      0.488 us |         current_kernel_time();
0)      0.495 us |         timespec_trunc();
0)      2.486 us |       }
0) ! 9999.999 us |     }
0) ! 9999.999 us |   }
0) ! 9999.999 us | }
0)           |     sys_read() {
0)      0.796 us |   fget_light();
0)           |       vfs_read() {
0)           |         rw_verify_area() {
0)           |           security_file_permission() {
0)      0.488 us |         cap_file_permission();
0)      1.720 us |       }
0)      3.  4 us |     }
0)           |         tty_read() {
0)      0.488 us |       tty_paranoia_check();
0)           |           tty_ldisc_ref_wait() {
0)           |             tty_ldisc_try() {
0)      0.615 us |           _spin_lock_irqsave();
0)      0.615 us |           _spin_unlock_irqrestore();
0)      5.436 us |         }
0)      6.427 us |       }

Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Acked-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 83a8df61
Loading
Loading
Loading
Loading
+93 −49
Original line number Original line Diff line number Diff line
@@ -14,20 +14,25 @@
#include "trace.h"
#include "trace.h"


#define TRACE_GRAPH_INDENT	2
#define TRACE_GRAPH_INDENT	2
/* Spaces between function call and time duration */
#define TRACE_GRAPH_TIMESPACE_ENTRY	"                    "
/* Spaces between function call and closing braces */
#define TRACE_GRAPH_TIMESPACE_RET	"                               "


/* Flag options */
#define TRACE_GRAPH_PRINT_OVERRUN	0x1
#define TRACE_GRAPH_PRINT_OVERRUN	0x1
#define TRACE_GRAPH_PRINT_CPU		0x2
#define TRACE_GRAPH_PRINT_OVERHEAD	0x4

static struct tracer_opt trace_opts[] = {
static struct tracer_opt trace_opts[] = {
	/* Display overruns or not */
	/* Display overruns ? */
	{ TRACER_OPT(overrun, TRACE_GRAPH_PRINT_OVERRUN) },
	{ TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) },
	/* Display CPU ? */
	{ TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) },
	/* Display Overhead ? */
	{ TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },
	{ } /* Empty entry */
	{ } /* Empty entry */
};
};


static struct tracer_flags tracer_flags = {
static struct tracer_flags tracer_flags = {
	.val = 0, /* Don't display overruns by default */
	/* Don't display overruns by default */
	.val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD,
	.opts = trace_opts
	.opts = trace_opts
};
};


@@ -56,6 +61,36 @@ static void graph_trace_reset(struct trace_array *tr)
	unregister_ftrace_graph();
	unregister_ftrace_graph();
}
}


static inline int log10_cpu(int nb)
{
	if (nb / 100)
		return 3;
	if (nb / 10)
		return 2;
	return 1;
}

static enum print_line_t
print_graph_cpu(struct trace_seq *s, int cpu)
{
	int i;
	int ret;
	int log10_this = log10_cpu(cpu);
	int log10_all = log10_cpu(cpus_weight_nr(cpu_online_map));


	for (i = 0; i < log10_all - log10_this; i++) {
		ret = trace_seq_printf(s, " ");
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
	}
	ret = trace_seq_printf(s, "%d) ", cpu);
	if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
	return TRACE_TYPE_HANDLED;
}


/* If the pid changed since the last trace, output this event */
/* If the pid changed since the last trace, output this event */
static int verif_pid(struct trace_seq *s, pid_t pid, int cpu)
static int verif_pid(struct trace_seq *s, pid_t pid, int cpu)
{
{
@@ -67,8 +102,7 @@ static int verif_pid(struct trace_seq *s, pid_t pid, int cpu)
	last_pid[cpu] = pid;
	last_pid[cpu] = pid;
	comm = trace_find_cmdline(pid);
	comm = trace_find_cmdline(pid);


	return trace_seq_printf(s, "\nCPU[%03d] "
	return trace_seq_printf(s, "\n------------8<---------- thread %s-%d"
				    " ------------8<---------- thread %s-%d"
				    " ------------8<----------\n\n",
				    " ------------8<----------\n\n",
				    cpu, comm, pid);
				    cpu, comm, pid);
}
}
@@ -86,7 +120,7 @@ trace_branch_is_leaf(struct trace_iterator *iter,
	if (!ring_iter)
	if (!ring_iter)
		return false;
		return false;


	event = ring_buffer_iter_peek(iter->buffer_iter[iter->cpu], NULL);
	event = ring_buffer_iter_peek(ring_iter, NULL);


	if (!event)
	if (!event)
		return false;
		return false;
@@ -108,7 +142,7 @@ static inline int
print_graph_duration(unsigned long long duration, struct trace_seq *s)
print_graph_duration(unsigned long long duration, struct trace_seq *s)
{
{
	unsigned long nsecs_rem = do_div(duration, 1000);
	unsigned long nsecs_rem = do_div(duration, 1000);
	return trace_seq_printf(s, "+ %llu.%lu us\n", duration, nsecs_rem);
	return trace_seq_printf(s, "%4llu.%3lu us | ", duration, nsecs_rem);
}
}


/* Signal a overhead of time execution to the output */
/* Signal a overhead of time execution to the output */
@@ -136,8 +170,8 @@ print_graph_entry_leaf(struct trace_iterator *iter,
	struct ring_buffer_event *event;
	struct ring_buffer_event *event;
	struct ftrace_graph_ent *call;
	struct ftrace_graph_ent *call;
	unsigned long long duration;
	unsigned long long duration;
	int i;
	int ret;
	int ret;
	int i;


	event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
	event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
	ret_entry = ring_buffer_event_data(event);
	ret_entry = ring_buffer_event_data(event);
@@ -145,10 +179,21 @@ print_graph_entry_leaf(struct trace_iterator *iter,
	call = &entry->graph_ent;
	call = &entry->graph_ent;
	duration = graph_ret->rettime - graph_ret->calltime;
	duration = graph_ret->rettime - graph_ret->calltime;


	/* Must not exceed 8 characters: 9999.999 us */
	if (duration > 10000000ULL)
		duration = 9999999ULL;

	/* Overhead */
	/* Overhead */
	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
		ret = print_graph_overhead(duration, s);
		ret = print_graph_overhead(duration, s);
		if (!ret)
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
			return TRACE_TYPE_PARTIAL_LINE;
	}

	/* Duration */
	ret = print_graph_duration(duration, s);
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;


	/* Function */
	/* Function */
	for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
	for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
@@ -161,16 +206,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
	if (!ret)
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


	ret = trace_seq_printf(s, "();");
	ret = trace_seq_printf(s, "();\n");
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;

	/* Duration */
	ret = trace_seq_printf(s, TRACE_GRAPH_TIMESPACE_ENTRY);
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;

	ret = print_graph_duration(duration, s);
	if (!ret)
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


@@ -186,9 +222,14 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
	struct ftrace_graph_ent *call = &entry->graph_ent;
	struct ftrace_graph_ent *call = &entry->graph_ent;


	/* No overhead */
	/* No overhead */
	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
		ret = trace_seq_printf(s, "  ");
		ret = trace_seq_printf(s, "  ");
		if (!ret)
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
			return TRACE_TYPE_PARTIAL_LINE;
	}

	/* No time */
	ret = trace_seq_printf(s, "        |     ");


	/* Function */
	/* Function */
	for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
	for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
@@ -201,12 +242,7 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
	if (!ret)
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


	ret = trace_seq_printf(s, "() {");
	ret = trace_seq_printf(s, "() {\n");
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;

	/* No duration to print at this state */
	ret = trace_seq_printf(s, TRACE_GRAPH_TIMESPACE_ENTRY "-\n");
	if (!ret)
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


@@ -220,12 +256,16 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
	int ret;
	int ret;
	struct trace_entry *ent = iter->ent;
	struct trace_entry *ent = iter->ent;


	/* Pid */
	if (!verif_pid(s, ent->pid, cpu))
	if (!verif_pid(s, ent->pid, cpu))
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


	ret = trace_seq_printf(s, "CPU[%03d] ", cpu);
	/* Cpu */
	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
		ret = print_graph_cpu(s, cpu);
		if (!ret)
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
			return TRACE_TYPE_PARTIAL_LINE;
	}


	if (trace_branch_is_leaf(iter, field))
	if (trace_branch_is_leaf(iter, field))
		return print_graph_entry_leaf(iter, field, s);
		return print_graph_entry_leaf(iter, field, s);
@@ -242,37 +282,41 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
	int ret;
	int ret;
	unsigned long long duration = trace->rettime - trace->calltime;
	unsigned long long duration = trace->rettime - trace->calltime;


	/* Must not exceed 8 characters: xxxx.yyy us */
	if (duration > 10000000ULL)
		duration = 9999999ULL;

	/* Pid */
	/* Pid */
	if (!verif_pid(s, ent->pid, cpu))
	if (!verif_pid(s, ent->pid, cpu))
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


	/* Cpu */
	/* Cpu */
	ret = trace_seq_printf(s, "CPU[%03d] ", cpu);
	if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
		ret = print_graph_cpu(s, cpu);
		if (!ret)
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
			return TRACE_TYPE_PARTIAL_LINE;
	}


	/* Overhead */
	/* Overhead */
	if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
		ret = print_graph_overhead(duration, s);
		ret = print_graph_overhead(duration, s);
		if (!ret)
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
			return TRACE_TYPE_PARTIAL_LINE;

	/* Closing brace */
	for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) {
		ret = trace_seq_printf(s, " ");
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
	}
	}


	ret = trace_seq_printf(s, "} ");
	/* Duration */
	ret = print_graph_duration(duration, s);
	if (!ret)
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;


	/* Duration */
	/* Closing brace */
	ret = trace_seq_printf(s, TRACE_GRAPH_TIMESPACE_RET);
	for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) {
		ret = trace_seq_printf(s, " ");
		if (!ret)
		if (!ret)
			return TRACE_TYPE_PARTIAL_LINE;
			return TRACE_TYPE_PARTIAL_LINE;
	}


	ret = print_graph_duration(duration, s);
	ret = trace_seq_printf(s, "}\n");
	if (!ret)
	if (!ret)
		return TRACE_TYPE_PARTIAL_LINE;
		return TRACE_TYPE_PARTIAL_LINE;