Commit 1149fcf7 authored by Masami Hiramatsu (Google)'s avatar Masami Hiramatsu (Google)
Browse files

tracing: Add an option to show symbols in _text+offset for function profiler

Function profiler shows the hit count of each function using its symbol
name. However, there are some same-name local symbols, which we can not
distinguish.
To solve this issue, this introduces an option to show the symbols
in "_text+OFFSET" format. This can avoid exposing the random shift of
KASLR. The functions in modules are shown as "MODNAME+OFFSET" where the
offset is from ".text".

E.g. for the kernel text symbols, specify vmlinux and the output to
 addr2line, you can find the actual function and source info;

  $ addr2line -fie vmlinux _text+3078208
  __balance_callbacks
  kernel/sched/core.c:5064

for modules, specify the module file and .text+OFFSET;

  $ addr2line -fie samples/trace_events/trace-events-sample.ko .text+8224
  do_simple_thread_func
  samples/trace_events/trace-events-sample.c:23

Link: https://lore.kernel.org/all/176187878064.994619.8878296550240416558.stgit@devnote2/



Suggested-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
parent bbec8e28
Loading
Loading
Loading
Loading
+25 −1
Original line number Diff line number Diff line
@@ -534,7 +534,9 @@ static int function_stat_headers(struct seq_file *m)

static int function_stat_show(struct seq_file *m, void *v)
{
	struct trace_array *tr = trace_get_global_array();
	struct ftrace_profile *rec = v;
	const char *refsymbol = NULL;
	char str[KSYM_SYMBOL_LEN];
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	static struct trace_seq s;
@@ -554,7 +556,29 @@ static int function_stat_show(struct seq_file *m, void *v)
		return 0;
#endif

	if (tr->trace_flags & TRACE_ITER(PROF_TEXT_OFFSET)) {
		unsigned long offset;

		if (core_kernel_text(rec->ip)) {
			refsymbol = "_text";
			offset = rec->ip - (unsigned long)_text;
		} else {
			struct module *mod;

			guard(rcu)();
			mod = __module_text_address(rec->ip);
			if (mod) {
				refsymbol = mod->name;
				/* Calculate offset from module's text entry address. */
				offset = rec->ip - (unsigned long)mod->mem[MOD_TEXT].base;
			}
		}
		if (refsymbol)
			snprintf(str, sizeof(str), "  %s+%#lx", refsymbol, offset);
	}
	if (!refsymbol)
		kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);

	seq_printf(m, "  %-30.30s  %10lu", str, rec->counter);

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+3 −2
Original line number Diff line number Diff line
@@ -522,7 +522,8 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_export);

/* trace_options that are only supported by global_trace */
#define TOP_LEVEL_TRACE_FLAGS (TRACE_ITER(PRINTK) |			\
	       TRACE_ITER(PRINTK_MSGONLY) | TRACE_ITER(RECORD_CMD))
	       TRACE_ITER(PRINTK_MSGONLY) | TRACE_ITER(RECORD_CMD) |	\
	       TRACE_ITER(PROF_TEXT_OFFSET))

/* trace_flags that are default zero for instances */
#define ZEROED_TRACE_FLAGS \
@@ -11291,7 +11292,7 @@ __init static int tracer_alloc_buffers(void)

#ifdef CONFIG_FUNCTION_TRACER
/* Used to set module cached ftrace filtering at boot up */
__init struct trace_array *trace_get_global_array(void)
struct trace_array *trace_get_global_array(void)
{
	return &global_trace;
}
+10 −1
Original line number Diff line number Diff line
@@ -1359,6 +1359,14 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
# define STACK_FLAGS
#endif

#ifdef CONFIG_FUNCTION_PROFILER
# define PROFILER_FLAGS					\
		C(PROF_TEXT_OFFSET,	"prof-text-offset"),
#else
# define PROFILER_FLAGS
# define TRACE_ITER_PROF_TEXT_OFFSET_BIT	-1
#endif

/*
 * trace_iterator_flags is an enumeration that defines bit
 * positions into trace_flags that controls the output.
@@ -1397,7 +1405,8 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
		FUNCTION_FLAGS					\
		FGRAPH_FLAGS					\
		STACK_FLAGS					\
		BRANCH_FLAGS
		BRANCH_FLAGS					\
		PROFILER_FLAGS

/*
 * By defining C, we can make TRACE_FLAGS a list of bit names