OSDN Git Service

ftrace: Add symbols for ftrace trampolines
authorAdrian Hunter <adrian.hunter@intel.com>
Tue, 12 May 2020 12:19:13 +0000 (15:19 +0300)
committerPeter Zijlstra <peterz@infradead.org>
Mon, 15 Jun 2020 12:09:49 +0000 (14:09 +0200)
Symbols are needed for tools to describe instruction addresses. Pages
allocated for ftrace's purposes need symbols to be created for them.
Add such symbols to be visible via /proc/kallsyms.

Example on x86 with CONFIG_DYNAMIC_FTRACE=y

# echo function > /sys/kernel/debug/tracing/current_tracer
# cat /proc/kallsyms | grep '\[__builtin__ftrace\]'
ffffffffc0238000 t ftrace_trampoline    [__builtin__ftrace]

Note: This patch adds "__builtin__ftrace" as a module name in /proc/kallsyms for
symbols for pages allocated for ftrace's purposes, even though "__builtin__ftrace"
is not a module.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20200512121922.8997-7-adrian.hunter@intel.com
include/linux/ftrace.h
kernel/kallsyms.c
kernel/trace/ftrace.c

index e339dac..ce2c06f 100644 (file)
@@ -58,9 +58,6 @@ struct ftrace_direct_func;
 const char *
 ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
                   unsigned long *off, char **modname, char *sym);
-int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
-                          char *type, char *name,
-                          char *module_name, int *exported);
 #else
 static inline const char *
 ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
@@ -68,6 +65,13 @@ ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
 {
        return NULL;
 }
+#endif
+
+#if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_DYNAMIC_FTRACE)
+int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
+                          char *type, char *name,
+                          char *module_name, int *exported);
+#else
 static inline int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
                                         char *type, char *name,
                                         char *module_name, int *exported)
@@ -76,7 +80,6 @@ static inline int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *val
 }
 #endif
 
-
 #ifdef CONFIG_FUNCTION_TRACER
 
 extern int ftrace_enabled;
@@ -207,6 +210,7 @@ struct ftrace_ops {
        struct ftrace_ops_hash          old_hash;
        unsigned long                   trampoline;
        unsigned long                   trampoline_size;
+       struct list_head                list;
 #endif
 };
 
index c6cc293..834bfdc 100644 (file)
@@ -482,6 +482,11 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)
        return 1;
 }
 
+/*
+ * ftrace_mod_get_kallsym() may also get symbols for pages allocated for ftrace
+ * purposes. In that case "__builtin__ftrace" is used as a module name, even
+ * though "__builtin__ftrace" is not a module.
+ */
 static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter)
 {
        int ret = ftrace_mod_get_kallsym(iter->pos - iter->pos_mod_end,
index c163c35..31675b2 100644 (file)
@@ -2764,6 +2764,38 @@ void __weak arch_ftrace_trampoline_free(struct ftrace_ops *ops)
 {
 }
 
+/* List of trace_ops that have allocated trampolines */
+static LIST_HEAD(ftrace_ops_trampoline_list);
+
+static void ftrace_add_trampoline_to_kallsyms(struct ftrace_ops *ops)
+{
+       lockdep_assert_held(&ftrace_lock);
+       list_add_rcu(&ops->list, &ftrace_ops_trampoline_list);
+}
+
+static void ftrace_remove_trampoline_from_kallsyms(struct ftrace_ops *ops)
+{
+       lockdep_assert_held(&ftrace_lock);
+       list_del_rcu(&ops->list);
+}
+
+/*
+ * "__builtin__ftrace" is used as a module name in /proc/kallsyms for symbols
+ * for pages allocated for ftrace purposes, even though "__builtin__ftrace" is
+ * not a module.
+ */
+#define FTRACE_TRAMPOLINE_MOD "__builtin__ftrace"
+#define FTRACE_TRAMPOLINE_SYM "ftrace_trampoline"
+
+static void ftrace_trampoline_free(struct ftrace_ops *ops)
+{
+       if (ops && (ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP) &&
+           ops->trampoline)
+               ftrace_remove_trampoline_from_kallsyms(ops);
+
+       arch_ftrace_trampoline_free(ops);
+}
+
 static void ftrace_startup_enable(int command)
 {
        if (saved_ftrace_func != ftrace_trace_function) {
@@ -2934,7 +2966,7 @@ int ftrace_shutdown(struct ftrace_ops *ops, int command)
                        synchronize_rcu_tasks();
 
  free_ops:
-               arch_ftrace_trampoline_free(ops);
+               ftrace_trampoline_free(ops);
        }
 
        return 0;
@@ -6178,6 +6210,27 @@ struct ftrace_mod_map {
        unsigned int            num_funcs;
 };
 
+static int ftrace_get_trampoline_kallsym(unsigned int symnum,
+                                        unsigned long *value, char *type,
+                                        char *name, char *module_name,
+                                        int *exported)
+{
+       struct ftrace_ops *op;
+
+       list_for_each_entry_rcu(op, &ftrace_ops_trampoline_list, list) {
+               if (!op->trampoline || symnum--)
+                       continue;
+               *value = op->trampoline;
+               *type = 't';
+               strlcpy(name, FTRACE_TRAMPOLINE_SYM, KSYM_NAME_LEN);
+               strlcpy(module_name, FTRACE_TRAMPOLINE_MOD, MODULE_NAME_LEN);
+               *exported = 0;
+               return 0;
+       }
+
+       return -ERANGE;
+}
+
 #ifdef CONFIG_MODULES
 
 #define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next)
@@ -6514,6 +6567,7 @@ int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
 {
        struct ftrace_mod_map *mod_map;
        struct ftrace_mod_func *mod_func;
+       int ret;
 
        preempt_disable();
        list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) {
@@ -6540,8 +6594,10 @@ int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
                WARN_ON(1);
                break;
        }
+       ret = ftrace_get_trampoline_kallsym(symnum, value, type, name,
+                                           module_name, exported);
        preempt_enable();
-       return -ERANGE;
+       return ret;
 }
 
 #else
@@ -6553,6 +6609,18 @@ allocate_ftrace_mod_map(struct module *mod,
 {
        return NULL;
 }
+int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
+                          char *type, char *name, char *module_name,
+                          int *exported)
+{
+       int ret;
+
+       preempt_disable();
+       ret = ftrace_get_trampoline_kallsym(symnum, value, type, name,
+                                           module_name, exported);
+       preempt_enable();
+       return ret;
+}
 #endif /* CONFIG_MODULES */
 
 struct ftrace_init_func {
@@ -6733,7 +6801,12 @@ void __weak arch_ftrace_update_trampoline(struct ftrace_ops *ops)
 
 static void ftrace_update_trampoline(struct ftrace_ops *ops)
 {
+       unsigned long trampoline = ops->trampoline;
+
        arch_ftrace_update_trampoline(ops);
+       if (ops->trampoline && ops->trampoline != trampoline &&
+           (ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
+               ftrace_add_trampoline_to_kallsyms(ops);
 }
 
 void ftrace_init_trace_array(struct trace_array *tr)