Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
authorIngo Molnar <mingo@elte.hu>
Thu, 21 Jul 2011 07:32:40 +0000 (09:32 +0200)
committerIngo Molnar <mingo@elte.hu>
Thu, 21 Jul 2011 07:32:40 +0000 (09:32 +0200)
26 files changed:
Documentation/trace/kprobetrace.txt
arch/x86/include/asm/perf_event_p4.h
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_p4.c
include/linux/ftrace_event.h
kernel/kprobes.c
kernel/trace/ftrace.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_entries.h
kernel/trace/trace_functions_graph.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_output.c
kernel/watchdog.c
tools/perf/Documentation/perf-probe.txt
tools/perf/Makefile
tools/perf/builtin-probe.c
tools/perf/util/dwarf-aux.c [new file with mode: 0644]
tools/perf/util/dwarf-aux.h [new file with mode: 0644]
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h
tools/perf/util/string.c
tools/perf/util/trace-event-info.c
tools/perf/util/util.h

index c83bd6b..d0d0bb9 100644 (file)
@@ -22,14 +22,15 @@ current_tracer. Instead of that, add probe points via
 
 Synopsis of kprobe_events
 -------------------------
-  p[:[GRP/]EVENT] SYMBOL[+offs]|MEMADDR [FETCHARGS]    : Set a probe
-  r[:[GRP/]EVENT] SYMBOL[+0] [FETCHARGS]               : Set a return probe
+  p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
+  r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]            : Set a return probe
   -:[GRP/]EVENT                                                : Clear a probe
 
  GRP           : Group name. If omitted, use "kprobes" for it.
  EVENT         : Event name. If omitted, the event name is generated
-                 based on SYMBOL+offs or MEMADDR.
- SYMBOL[+offs] : Symbol+offset where the probe is inserted.
+                 based on SYM+offs or MEMADDR.
+ MOD           : Module name which has given SYM.
+ SYM[+offs]    : Symbol+offset where the probe is inserted.
  MEMADDR       : Address where the probe is inserted.
 
  FETCHARGS     : Arguments. Each probe can have up to 128 args.
index 56fd9e3..4d86c86 100644 (file)
 #define P4_CONFIG_HT_SHIFT             63
 #define P4_CONFIG_HT                   (1ULL << P4_CONFIG_HT_SHIFT)
 
+/*
+ * If an event has alias it should be marked
+ * with a special bit. (Don't forget to check
+ * P4_PEBS_CONFIG_MASK and related bits on
+ * modification.)
+ */
+#define P4_CONFIG_ALIASABLE            (1 << 9)
+
 /*
  * The bits we allow to pass for RAW events
  */
        (p4_config_pack_escr(P4_CONFIG_MASK_ESCR))      | \
        (p4_config_pack_cccr(P4_CONFIG_MASK_CCCR))
 
+/*
+ * In case of event aliasing we need to preserve some
+ * caller bits otherwise the mapping won't be complete.
+ */
+#define P4_CONFIG_EVENT_ALIAS_MASK                       \
+       (p4_config_pack_escr(P4_CONFIG_MASK_ESCR)       | \
+        p4_config_pack_cccr(P4_CCCR_EDGE               | \
+                            P4_CCCR_THRESHOLD_MASK     | \
+                            P4_CCCR_COMPLEMENT         | \
+                            P4_CCCR_COMPARE))
+
+#define  P4_CONFIG_EVENT_ALIAS_IMMUTABLE_BITS            \
+       ((P4_CONFIG_HT)                                 | \
+        p4_config_pack_escr(P4_ESCR_T0_OS              | \
+                            P4_ESCR_T0_USR             | \
+                            P4_ESCR_T1_OS              | \
+                            P4_ESCR_T1_USR)            | \
+        p4_config_pack_cccr(P4_CCCR_OVF                | \
+                            P4_CCCR_CASCADE            | \
+                            P4_CCCR_FORCE_OVF          | \
+                            P4_CCCR_THREAD_ANY         | \
+                            P4_CCCR_OVF_PMI_T0         | \
+                            P4_CCCR_OVF_PMI_T1         | \
+                            P4_CONFIG_ALIASABLE))
+
 static inline bool p4_is_event_cascaded(u64 config)
 {
        u32 cccr = p4_config_unpack_cccr(config);
index c53d433..b7a010f 100644 (file)
@@ -274,7 +274,6 @@ struct x86_pmu {
        void            (*enable_all)(int added);
        void            (*enable)(struct perf_event *);
        void            (*disable)(struct perf_event *);
-       void            (*hw_watchdog_set_attr)(struct perf_event_attr *attr);
        int             (*hw_config)(struct perf_event *event);
        int             (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
        unsigned        eventsel;
@@ -360,12 +359,6 @@ static u64 __read_mostly hw_cache_extra_regs
                                [PERF_COUNT_HW_CACHE_OP_MAX]
                                [PERF_COUNT_HW_CACHE_RESULT_MAX];
 
-void hw_nmi_watchdog_set_attr(struct perf_event_attr *wd_attr)
-{
-       if (x86_pmu.hw_watchdog_set_attr)
-               x86_pmu.hw_watchdog_set_attr(wd_attr);
-}
-
 /*
  * Propagate event elapsed time into the generic event.
  * Can only be executed on the CPU where the event is active.
index fb901c5..8b7a0c3 100644 (file)
@@ -570,11 +570,92 @@ static __initconst const u64 p4_hw_cache_event_ids
  },
 };
 
+/*
+ * Because of Netburst being quite restricted in now
+ * many same events can run simultaneously, we use
+ * event aliases, ie different events which have the
+ * same functionallity but use non-intersected resources
+ * (ESCR/CCCR/couter registers). This allow us to run
+ * two or more semi-same events together. It is done
+ * transparently to a user space.
+ *
+ * Never set any cusom internal bits such as P4_CONFIG_HT,
+ * P4_CONFIG_ALIASABLE or bits for P4_PEBS_METRIC, they are
+ * either up-to-dated automatically either not appliable
+ * at all.
+ *
+ * And be really carefull choosing aliases!
+ */
+struct p4_event_alias {
+       u64 orig;
+       u64 alter;
+} p4_event_aliases[] = {
+       {
+               /*
+                * Non-halted cycles can be substituted with
+                * non-sleeping cycles (see Intel SDM Vol3b for
+                * details).
+                */
+       .orig   =
+               p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS)         |
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)),
+       .alter  =
+               p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_EXECUTION_EVENT)             |
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS0)|
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS1)|
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS2)|
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS3)|
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS0) |
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS1) |
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS2) |
+                                   P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS3))|
+               p4_config_pack_cccr(P4_CCCR_THRESHOLD(15) | P4_CCCR_COMPLEMENT          |
+                                   P4_CCCR_COMPARE),
+       },
+};
+
+static u64 p4_get_alias_event(u64 config)
+{
+       u64 config_match;
+       int i;
+
+       /*
+        * Probably we're lucky and don't have to do
+        * matching over all config bits.
+        */
+       if (!(config & P4_CONFIG_ALIASABLE))
+               return 0;
+
+       config_match = config & P4_CONFIG_EVENT_ALIAS_MASK;
+
+       /*
+        * If an event was previously swapped to the alter config
+        * we should swap it back otherwise contnention on registers
+        * will return back.
+        */
+       for (i = 0; i < ARRAY_SIZE(p4_event_aliases); i++) {
+               if (config_match == p4_event_aliases[i].orig) {
+                       config_match = p4_event_aliases[i].alter;
+                       break;
+               } else if (config_match == p4_event_aliases[i].alter) {
+                       config_match = p4_event_aliases[i].orig;
+                       break;
+               }
+       }
+
+       if (i >= ARRAY_SIZE(p4_event_aliases))
+               return 0;
+
+       return config_match |
+               (config & P4_CONFIG_EVENT_ALIAS_IMMUTABLE_BITS);
+}
+
 static u64 p4_general_events[PERF_COUNT_HW_MAX] = {
   /* non-halted CPU clocks */
   [PERF_COUNT_HW_CPU_CYCLES] =
        p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS)         |
-               P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)),
+               P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING))       |
+               P4_CONFIG_ALIASABLE,
 
   /*
    * retired instructions
@@ -719,31 +800,6 @@ static int p4_validate_raw_event(struct perf_event *event)
        return 0;
 }
 
-static void p4_hw_watchdog_set_attr(struct perf_event_attr *wd_attr)
-{
-       /*
-        * Watchdog ticks are special on Netburst, we use
-        * that named "non-sleeping" ticks as recommended
-        * by Intel SDM Vol3b.
-        */
-       WARN_ON_ONCE(wd_attr->type      != PERF_TYPE_HARDWARE ||
-                    wd_attr->config    != PERF_COUNT_HW_CPU_CYCLES);
-
-       wd_attr->type   = PERF_TYPE_RAW;
-       wd_attr->config =
-               p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_EXECUTION_EVENT)             |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS0)            |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS1)            |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS2)            |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS3)            |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS0)             |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS1)             |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS2)             |
-                       P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS3))            |
-               p4_config_pack_cccr(P4_CCCR_THRESHOLD(15) | P4_CCCR_COMPLEMENT          |
-                       P4_CCCR_COMPARE);
-}
-
 static int p4_hw_config(struct perf_event *event)
 {
        int cpu = get_cpu();
@@ -1159,6 +1215,8 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
        struct p4_event_bind *bind;
        unsigned int i, thread, num;
        int cntr_idx, escr_idx;
+       u64 config_alias;
+       int pass;
 
        bitmap_zero(used_mask, X86_PMC_IDX_MAX);
        bitmap_zero(escr_mask, P4_ESCR_MSR_TABLE_SIZE);
@@ -1167,6 +1225,17 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
 
                hwc = &cpuc->event_list[i]->hw;
                thread = p4_ht_thread(cpu);
+               pass = 0;
+
+again:
+               /*
+                * Aliases are swappable so we may hit circular
+                * lock if both original config and alias need
+                * resources (MSR registers) which already busy.
+                */
+               if (pass > 2)
+                       goto done;
+
                bind = p4_config_get_bind(hwc->config);
                escr_idx = p4_get_escr_idx(bind->escr_msr[thread]);
                if (unlikely(escr_idx == -1))
@@ -1180,8 +1249,17 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
                }
 
                cntr_idx = p4_next_cntr(thread, used_mask, bind);
-               if (cntr_idx == -1 || test_bit(escr_idx, escr_mask))
-                       goto done;
+               if (cntr_idx == -1 || test_bit(escr_idx, escr_mask)) {
+                       /*
+                        * Probably an event alias is still available.
+                        */
+                       config_alias = p4_get_alias_event(hwc->config);
+                       if (!config_alias)
+                               goto done;
+                       hwc->config = config_alias;
+                       pass++;
+                       goto again;
+               }
 
                p4_pmu_swap_config_ts(hwc, cpu);
                if (assign)
@@ -1218,7 +1296,6 @@ static __initconst const struct x86_pmu p4_pmu = {
        .cntval_bits            = ARCH_P4_CNTRVAL_BITS,
        .cntval_mask            = ARCH_P4_CNTRVAL_MASK,
        .max_period             = (1ULL << (ARCH_P4_CNTRVAL_BITS - 1)) - 1,
-       .hw_watchdog_set_attr   = p4_hw_watchdog_set_attr,
        .hw_config              = p4_hw_config,
        .schedule_events        = p4_pmu_schedule_events,
        /*
index b1e69ee..96efa67 100644 (file)
@@ -76,6 +76,7 @@ struct trace_iterator {
        struct trace_entry      *ent;
        unsigned long           lost_events;
        int                     leftover;
+       int                     ent_size;
        int                     cpu;
        u64                     ts;
 
index 7798181..b30fd54 100644 (file)
@@ -1255,19 +1255,29 @@ static int __kprobes in_kprobes_functions(unsigned long addr)
 /*
  * If we have a symbol_name argument, look it up and add the offset field
  * to it. This way, we can specify a relative address to a symbol.
+ * This returns encoded errors if it fails to look up symbol or invalid
+ * combination of parameters.
  */
 static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
 {
        kprobe_opcode_t *addr = p->addr;
+
+       if ((p->symbol_name && p->addr) ||
+           (!p->symbol_name && !p->addr))
+               goto invalid;
+
        if (p->symbol_name) {
-               if (addr)
-                       return NULL;
                kprobe_lookup_name(p->symbol_name, addr);
+               if (!addr)
+                       return ERR_PTR(-ENOENT);
        }
 
-       if (!addr)
-               return NULL;
-       return (kprobe_opcode_t *)(((char *)addr) + p->offset);
+       addr = (kprobe_opcode_t *)(((char *)addr) + p->offset);
+       if (addr)
+               return addr;
+
+invalid:
+       return ERR_PTR(-EINVAL);
 }
 
 /* Check passed kprobe is valid and return kprobe in kprobe_table. */
@@ -1311,8 +1321,8 @@ int __kprobes register_kprobe(struct kprobe *p)
        kprobe_opcode_t *addr;
 
        addr = kprobe_addr(p);
-       if (!addr)
-               return -EINVAL;
+       if (IS_ERR(addr))
+               return PTR_ERR(addr);
        p->addr = addr;
 
        ret = check_kprobe_rereg(p);
@@ -1335,6 +1345,8 @@ int __kprobes register_kprobe(struct kprobe *p)
         */
        probed_mod = __module_text_address((unsigned long) p->addr);
        if (probed_mod) {
+               /* Return -ENOENT if fail. */
+               ret = -ENOENT;
                /*
                 * We must hold a refcount of the probed module while updating
                 * its code to prohibit unexpected unloading.
@@ -1351,6 +1363,7 @@ int __kprobes register_kprobe(struct kprobe *p)
                        module_put(probed_mod);
                        goto fail_with_jump_label;
                }
+               /* ret will be updated by following code */
        }
        preempt_enable();
        jump_label_unlock();
@@ -1399,7 +1412,7 @@ out:
 fail_with_jump_label:
        preempt_enable();
        jump_label_unlock();
-       return -EINVAL;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(register_kprobe);
 
@@ -1686,8 +1699,8 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
 
        if (kretprobe_blacklist_size) {
                addr = kprobe_addr(&rp->kp);
-               if (!addr)
-                       return -EINVAL;
+               if (IS_ERR(addr))
+                       return PTR_ERR(addr);
 
                for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
                        if (kretprobe_blacklist[i].addr == addr)
index a0e246e..c3e4575 100644 (file)
@@ -88,6 +88,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly = {
 static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;
 static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
 ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
+static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;
 ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
 ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
 static struct ftrace_ops global_ops;
@@ -146,9 +147,11 @@ void clear_ftrace_function(void)
 {
        ftrace_trace_function = ftrace_stub;
        __ftrace_trace_function = ftrace_stub;
+       __ftrace_trace_function_delay = ftrace_stub;
        ftrace_pid_function = ftrace_stub;
 }
 
+#undef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
 #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
 /*
  * For those archs that do not test ftrace_trace_stop in their
@@ -207,8 +210,13 @@ static void update_ftrace_function(void)
 
 #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
        ftrace_trace_function = func;
+#else
+#ifdef CONFIG_DYNAMIC_FTRACE
+       /* do not update till all functions have been modified */
+       __ftrace_trace_function_delay = func;
 #else
        __ftrace_trace_function = func;
+#endif
        ftrace_trace_function = ftrace_test_stop_func;
 #endif
 }
@@ -1170,8 +1178,14 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash)
        return NULL;
 }
 
+static void
+ftrace_hash_rec_disable(struct ftrace_ops *ops, int filter_hash);
+static void
+ftrace_hash_rec_enable(struct ftrace_ops *ops, int filter_hash);
+
 static int
-ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
+ftrace_hash_move(struct ftrace_ops *ops, int enable,
+                struct ftrace_hash **dst, struct ftrace_hash *src)
 {
        struct ftrace_func_entry *entry;
        struct hlist_node *tp, *tn;
@@ -1181,8 +1195,15 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
        unsigned long key;
        int size = src->count;
        int bits = 0;
+       int ret;
        int i;
 
+       /*
+        * Remove the current set, update the hash and add
+        * them back.
+        */
+       ftrace_hash_rec_disable(ops, enable);
+
        /*
         * If the new source is empty, just free dst and assign it
         * the empty_hash.
@@ -1203,9 +1224,10 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
        if (bits > FTRACE_HASH_MAX_BITS)
                bits = FTRACE_HASH_MAX_BITS;
 
+       ret = -ENOMEM;
        new_hash = alloc_ftrace_hash(bits);
        if (!new_hash)
-               return -ENOMEM;
+               goto out;
 
        size = 1 << src->size_bits;
        for (i = 0; i < size; i++) {
@@ -1224,7 +1246,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
        rcu_assign_pointer(*dst, new_hash);
        free_ftrace_hash_rcu(old_hash);
 
-       return 0;
+       ret = 0;
+ out:
+       /*
+        * Enable regardless of ret:
+        *  On success, we enable the new hash.
+        *  On failure, we re-enable the original hash.
+        */
+       ftrace_hash_rec_enable(ops, enable);
+
+       return ret;
 }
 
 /*
@@ -1584,6 +1615,12 @@ static int __ftrace_modify_code(void *data)
 {
        int *command = data;
 
+       /*
+        * Do not call function tracer while we update the code.
+        * We are in stop machine, no worrying about races.
+        */
+       function_trace_stop++;
+
        if (*command & FTRACE_ENABLE_CALLS)
                ftrace_replace_code(1);
        else if (*command & FTRACE_DISABLE_CALLS)
@@ -1597,6 +1634,18 @@ static int __ftrace_modify_code(void *data)
        else if (*command & FTRACE_STOP_FUNC_RET)
                ftrace_disable_ftrace_graph_caller();
 
+#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
+       /*
+        * For archs that call ftrace_test_stop_func(), we must
+        * wait till after we update all the function callers
+        * before we update the callback. This keeps different
+        * ops that record different functions from corrupting
+        * each other.
+        */
+       __ftrace_trace_function = __ftrace_trace_function_delay;
+#endif
+       function_trace_stop--;
+
        return 0;
 }
 
@@ -2865,7 +2914,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
                ftrace_match_records(hash, buf, len);
 
        mutex_lock(&ftrace_lock);
-       ret = ftrace_hash_move(orig_hash, hash);
+       ret = ftrace_hash_move(ops, enable, orig_hash, hash);
+       if (!ret && ops->flags & FTRACE_OPS_FL_ENABLED
+           && ftrace_enabled)
+               ftrace_run_update_code(FTRACE_ENABLE_CALLS);
+
        mutex_unlock(&ftrace_lock);
 
        mutex_unlock(&ftrace_regex_lock);
@@ -3048,18 +3101,12 @@ ftrace_regex_release(struct inode *inode, struct file *file)
                        orig_hash = &iter->ops->notrace_hash;
 
                mutex_lock(&ftrace_lock);
-               /*
-                * Remove the current set, update the hash and add
-                * them back.
-                */
-               ftrace_hash_rec_disable(iter->ops, filter_hash);
-               ret = ftrace_hash_move(orig_hash, iter->hash);
-               if (!ret) {
-                       ftrace_hash_rec_enable(iter->ops, filter_hash);
-                       if (iter->ops->flags & FTRACE_OPS_FL_ENABLED
-                           && ftrace_enabled)
-                               ftrace_run_update_code(FTRACE_ENABLE_CALLS);
-               }
+               ret = ftrace_hash_move(iter->ops, filter_hash,
+                                      orig_hash, iter->hash);
+               if (!ret && (iter->ops->flags & FTRACE_OPS_FL_ENABLED)
+                   && ftrace_enabled)
+                       ftrace_run_update_code(FTRACE_ENABLE_CALLS);
+
                mutex_unlock(&ftrace_lock);
        }
        free_ftrace_hash(iter->hash);
@@ -3338,7 +3385,7 @@ static int ftrace_process_locs(struct module *mod,
 {
        unsigned long *p;
        unsigned long addr;
-       unsigned long flags;
+       unsigned long flags = 0; /* Shut up gcc */
 
        mutex_lock(&ftrace_lock);
        p = start;
@@ -3356,12 +3403,18 @@ static int ftrace_process_locs(struct module *mod,
        }
 
        /*
-        * Disable interrupts to prevent interrupts from executing
-        * code that is being modified.
+        * We only need to disable interrupts on start up
+        * because we are modifying code that an interrupt
+        * may execute, and the modification is not atomic.
+        * But for modules, nothing runs the code we modify
+        * until we are finished with it, and there's no
+        * reason to cause large interrupt latencies while we do it.
         */
-       local_irq_save(flags);
+       if (!mod)
+               local_irq_save(flags);
        ftrace_update_code(mod);
-       local_irq_restore(flags);
+       if (!mod)
+               local_irq_restore(flags);
        mutex_unlock(&ftrace_lock);
 
        return 0;
index d9c1612..e5df02c 100644 (file)
@@ -1248,6 +1248,15 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data,
 }
 
 #ifdef CONFIG_STACKTRACE
+
+#define FTRACE_STACK_MAX_ENTRIES (PAGE_SIZE / sizeof(unsigned long))
+struct ftrace_stack {
+       unsigned long           calls[FTRACE_STACK_MAX_ENTRIES];
+};
+
+static DEFINE_PER_CPU(struct ftrace_stack, ftrace_stack);
+static DEFINE_PER_CPU(int, ftrace_stack_reserve);
+
 static void __ftrace_trace_stack(struct ring_buffer *buffer,
                                 unsigned long flags,
                                 int skip, int pc, struct pt_regs *regs)
@@ -1256,25 +1265,77 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
        struct ring_buffer_event *event;
        struct stack_entry *entry;
        struct stack_trace trace;
+       int use_stack;
+       int size = FTRACE_STACK_ENTRIES;
+
+       trace.nr_entries        = 0;
+       trace.skip              = skip;
+
+       /*
+        * Since events can happen in NMIs there's no safe way to
+        * use the per cpu ftrace_stacks. We reserve it and if an interrupt
+        * or NMI comes in, it will just have to use the default
+        * FTRACE_STACK_SIZE.
+        */
+       preempt_disable_notrace();
+
+       use_stack = ++__get_cpu_var(ftrace_stack_reserve);
+       /*
+        * We don't need any atomic variables, just a barrier.
+        * If an interrupt comes in, we don't care, because it would
+        * have exited and put the counter back to what we want.
+        * We just need a barrier to keep gcc from moving things
+        * around.
+        */
+       barrier();
+       if (use_stack == 1) {
+               trace.entries           = &__get_cpu_var(ftrace_stack).calls[0];
+               trace.max_entries       = FTRACE_STACK_MAX_ENTRIES;
+
+               if (regs)
+                       save_stack_trace_regs(regs, &trace);
+               else
+                       save_stack_trace(&trace);
+
+               if (trace.nr_entries > size)
+                       size = trace.nr_entries;
+       } else
+               /* From now on, use_stack is a boolean */
+               use_stack = 0;
+
+       size *= sizeof(unsigned long);
 
        event = trace_buffer_lock_reserve(buffer, TRACE_STACK,
-                                         sizeof(*entry), flags, pc);
+                                         sizeof(*entry) + size, flags, pc);
        if (!event)
-               return;
-       entry   = ring_buffer_event_data(event);
-       memset(&entry->caller, 0, sizeof(entry->caller));
+               goto out;
+       entry = ring_buffer_event_data(event);
 
-       trace.nr_entries        = 0;
-       trace.max_entries       = FTRACE_STACK_ENTRIES;
-       trace.skip              = skip;
-       trace.entries           = entry->caller;
+       memset(&entry->caller, 0, size);
+
+       if (use_stack)
+               memcpy(&entry->caller, trace.entries,
+                      trace.nr_entries * sizeof(unsigned long));
+       else {
+               trace.max_entries       = FTRACE_STACK_ENTRIES;
+               trace.entries           = entry->caller;
+               if (regs)
+                       save_stack_trace_regs(regs, &trace);
+               else
+                       save_stack_trace(&trace);
+       }
+
+       entry->size = trace.nr_entries;
 
-       if (regs)
-               save_stack_trace_regs(regs, &trace);
-       else
-               save_stack_trace(&trace);
        if (!filter_check_discard(call, entry, buffer, event))
                ring_buffer_unlock_commit(buffer, event);
+
+ out:
+       /* Again, don't let gcc optimize things here */
+       barrier();
+       __get_cpu_var(ftrace_stack_reserve)--;
+       preempt_enable_notrace();
+
 }
 
 void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags,
@@ -1562,7 +1623,12 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
 
        ftrace_enable_cpu();
 
-       return event ? ring_buffer_event_data(event) : NULL;
+       if (event) {
+               iter->ent_size = ring_buffer_event_length(event);
+               return ring_buffer_event_data(event);
+       }
+       iter->ent_size = 0;
+       return NULL;
 }
 
 static struct trace_entry *
index 30a94c2..3f381d0 100644 (file)
@@ -278,6 +278,29 @@ struct tracer {
 };
 
 
+/* Only current can touch trace_recursion */
+#define trace_recursion_inc() do { (current)->trace_recursion++; } while (0)
+#define trace_recursion_dec() do { (current)->trace_recursion--; } while (0)
+
+/* Ring buffer has the 10 LSB bits to count */
+#define trace_recursion_buffer() ((current)->trace_recursion & 0x3ff)
+
+/* for function tracing recursion */
+#define TRACE_INTERNAL_BIT             (1<<11)
+#define TRACE_GLOBAL_BIT               (1<<12)
+/*
+ * Abuse of the trace_recursion.
+ * As we need a way to maintain state if we are tracing the function
+ * graph in irq because we want to trace a particular function that
+ * was called in irq context but we have irq tracing off. Since this
+ * can only be modified by current, we can reuse trace_recursion.
+ */
+#define TRACE_IRQ_BIT                  (1<<13)
+
+#define trace_recursion_set(bit)       do { (current)->trace_recursion |= (bit); } while (0)
+#define trace_recursion_clear(bit)     do { (current)->trace_recursion &= ~(bit); } while (0)
+#define trace_recursion_test(bit)      ((current)->trace_recursion & (bit))
+
 #define TRACE_PIPE_ALL_CPU     -1
 
 int tracer_init(struct tracer *t, struct trace_array *tr);
@@ -516,8 +539,18 @@ static inline int ftrace_graph_addr(unsigned long addr)
                return 1;
 
        for (i = 0; i < ftrace_graph_count; i++) {
-               if (addr == ftrace_graph_funcs[i])
+               if (addr == ftrace_graph_funcs[i]) {
+                       /*
+                        * If no irqs are to be traced, but a set_graph_function
+                        * is set, and called by an interrupt handler, we still
+                        * want to trace it.
+                        */
+                       if (in_irq())
+                               trace_recursion_set(TRACE_IRQ_BIT);
+                       else
+                               trace_recursion_clear(TRACE_IRQ_BIT);
                        return 1;
+               }
        }
 
        return 0;
@@ -795,19 +828,4 @@ extern const char *__stop___trace_bprintk_fmt[];
        FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
 #include "trace_entries.h"
 
-/* Only current can touch trace_recursion */
-#define trace_recursion_inc() do { (current)->trace_recursion++; } while (0)
-#define trace_recursion_dec() do { (current)->trace_recursion--; } while (0)
-
-/* Ring buffer has the 10 LSB bits to count */
-#define trace_recursion_buffer() ((current)->trace_recursion & 0x3ff)
-
-/* for function tracing recursion */
-#define TRACE_INTERNAL_BIT             (1<<11)
-#define TRACE_GLOBAL_BIT               (1<<12)
-
-#define trace_recursion_set(bit)       do { (current)->trace_recursion |= (bit); } while (0)
-#define trace_recursion_clear(bit)     do { (current)->trace_recursion &= ~(bit); } while (0)
-#define trace_recursion_test(bit)      ((current)->trace_recursion & (bit))
-
 #endif /* _LINUX_KERNEL_TRACE_H */
index e32744c..9336590 100644 (file)
@@ -161,7 +161,8 @@ FTRACE_ENTRY(kernel_stack, stack_entry,
        TRACE_STACK,
 
        F_STRUCT(
-               __array(        unsigned long,  caller, FTRACE_STACK_ENTRIES    )
+               __field(        int,            size    )
+               __dynamic_array(unsigned long,  caller  )
        ),
 
        F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n"
index e8d6bb5..a7d2a4c 100644 (file)
@@ -227,7 +227,7 @@ int __trace_graph_entry(struct trace_array *tr,
 
 static inline int ftrace_graph_ignore_irqs(void)
 {
-       if (!ftrace_graph_skip_irqs)
+       if (!ftrace_graph_skip_irqs || trace_recursion_test(TRACE_IRQ_BIT))
                return 0;
 
        return in_irq();
index 7db7b68..5fb3697 100644 (file)
@@ -343,6 +343,14 @@ DEFINE_BASIC_FETCH_FUNCS(deref)
 DEFINE_FETCH_deref(string)
 DEFINE_FETCH_deref(string_size)
 
+static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data)
+{
+       if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
+               update_deref_fetch_param(data->orig.data);
+       else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
+               update_symbol_cache(data->orig.data);
+}
+
 static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
 {
        if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
@@ -376,6 +384,19 @@ DEFINE_BASIC_FETCH_FUNCS(bitfield)
 #define fetch_bitfield_string NULL
 #define fetch_bitfield_string_size NULL
 
+static __kprobes void
+update_bitfield_fetch_param(struct bitfield_fetch_param *data)
+{
+       /*
+        * Don't check the bitfield itself, because this must be the
+        * last fetch function.
+        */
+       if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
+               update_deref_fetch_param(data->orig.data);
+       else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
+               update_symbol_cache(data->orig.data);
+}
+
 static __kprobes void
 free_bitfield_fetch_param(struct bitfield_fetch_param *data)
 {
@@ -389,6 +410,7 @@ free_bitfield_fetch_param(struct bitfield_fetch_param *data)
                free_symbol_cache(data->orig.data);
        kfree(data);
 }
+
 /* Default (unsigned long) fetch type */
 #define __DEFAULT_FETCH_TYPE(t) u##t
 #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
@@ -536,6 +558,7 @@ struct probe_arg {
 /* Flags for trace_probe */
 #define TP_FLAG_TRACE  1
 #define TP_FLAG_PROFILE        2
+#define TP_FLAG_REGISTERED 4
 
 struct trace_probe {
        struct list_head        list;
@@ -555,16 +578,49 @@ struct trace_probe {
        (sizeof(struct probe_arg) * (n)))
 
 
-static __kprobes int probe_is_return(struct trace_probe *tp)
+static __kprobes int trace_probe_is_return(struct trace_probe *tp)
 {
        return tp->rp.handler != NULL;
 }
 
-static __kprobes const char *probe_symbol(struct trace_probe *tp)
+static __kprobes const char *trace_probe_symbol(struct trace_probe *tp)
 {
        return tp->symbol ? tp->symbol : "unknown";
 }
 
+static __kprobes unsigned long trace_probe_offset(struct trace_probe *tp)
+{
+       return tp->rp.kp.offset;
+}
+
+static __kprobes bool trace_probe_is_enabled(struct trace_probe *tp)
+{
+       return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE));
+}
+
+static __kprobes bool trace_probe_is_registered(struct trace_probe *tp)
+{
+       return !!(tp->flags & TP_FLAG_REGISTERED);
+}
+
+static __kprobes bool trace_probe_has_gone(struct trace_probe *tp)
+{
+       return !!(kprobe_gone(&tp->rp.kp));
+}
+
+static __kprobes bool trace_probe_within_module(struct trace_probe *tp,
+                                               struct module *mod)
+{
+       int len = strlen(mod->name);
+       const char *name = trace_probe_symbol(tp);
+       return strncmp(mod->name, name, len) == 0 && name[len] == ':';
+}
+
+static __kprobes bool trace_probe_is_on_module(struct trace_probe *tp)
+{
+       return !!strchr(trace_probe_symbol(tp), ':');
+}
+
 static int register_probe_event(struct trace_probe *tp);
 static void unregister_probe_event(struct trace_probe *tp);
 
@@ -646,6 +702,16 @@ error:
        return ERR_PTR(ret);
 }
 
+static void update_probe_arg(struct probe_arg *arg)
+{
+       if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn))
+               update_bitfield_fetch_param(arg->fetch.data);
+       else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
+               update_deref_fetch_param(arg->fetch.data);
+       else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
+               update_symbol_cache(arg->fetch.data);
+}
+
 static void free_probe_arg(struct probe_arg *arg)
 {
        if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn))
@@ -671,7 +737,7 @@ static void free_trace_probe(struct trace_probe *tp)
        kfree(tp);
 }
 
-static struct trace_probe *find_probe_event(const char *event,
+static struct trace_probe *find_trace_probe(const char *event,
                                            const char *group)
 {
        struct trace_probe *tp;
@@ -683,13 +749,96 @@ static struct trace_probe *find_probe_event(const char *event,
        return NULL;
 }
 
+/* Enable trace_probe - @flag must be TP_FLAG_TRACE or TP_FLAG_PROFILE */
+static int enable_trace_probe(struct trace_probe *tp, int flag)
+{
+       int ret = 0;
+
+       tp->flags |= flag;
+       if (trace_probe_is_enabled(tp) && trace_probe_is_registered(tp) &&
+           !trace_probe_has_gone(tp)) {
+               if (trace_probe_is_return(tp))
+                       ret = enable_kretprobe(&tp->rp);
+               else
+                       ret = enable_kprobe(&tp->rp.kp);
+       }
+
+       return ret;
+}
+
+/* Disable trace_probe - @flag must be TP_FLAG_TRACE or TP_FLAG_PROFILE */
+static void disable_trace_probe(struct trace_probe *tp, int flag)
+{
+       tp->flags &= ~flag;
+       if (!trace_probe_is_enabled(tp) && trace_probe_is_registered(tp)) {
+               if (trace_probe_is_return(tp))
+                       disable_kretprobe(&tp->rp);
+               else
+                       disable_kprobe(&tp->rp.kp);
+       }
+}
+
+/* Internal register function - just handle k*probes and flags */
+static int __register_trace_probe(struct trace_probe *tp)
+{
+       int i, ret;
+
+       if (trace_probe_is_registered(tp))
+               return -EINVAL;
+
+       for (i = 0; i < tp->nr_args; i++)
+               update_probe_arg(&tp->args[i]);
+
+       /* Set/clear disabled flag according to tp->flag */
+       if (trace_probe_is_enabled(tp))
+               tp->rp.kp.flags &= ~KPROBE_FLAG_DISABLED;
+       else
+               tp->rp.kp.flags |= KPROBE_FLAG_DISABLED;
+
+       if (trace_probe_is_return(tp))
+               ret = register_kretprobe(&tp->rp);
+       else
+               ret = register_kprobe(&tp->rp.kp);
+
+       if (ret == 0)
+               tp->flags |= TP_FLAG_REGISTERED;
+       else {
+               pr_warning("Could not insert probe at %s+%lu: %d\n",
+                          trace_probe_symbol(tp), trace_probe_offset(tp), ret);
+               if (ret == -ENOENT && trace_probe_is_on_module(tp)) {
+                       pr_warning("This probe might be able to register after"
+                                  "target module is loaded. Continue.\n");
+                       ret = 0;
+               } else if (ret == -EILSEQ) {
+                       pr_warning("Probing address(0x%p) is not an "
+                                  "instruction boundary.\n",
+                                  tp->rp.kp.addr);
+                       ret = -EINVAL;
+               }
+       }
+
+       return ret;
+}
+
+/* Internal unregister function - just handle k*probes and flags */
+static void __unregister_trace_probe(struct trace_probe *tp)
+{
+       if (trace_probe_is_registered(tp)) {
+               if (trace_probe_is_return(tp))
+                       unregister_kretprobe(&tp->rp);
+               else
+                       unregister_kprobe(&tp->rp.kp);
+               tp->flags &= ~TP_FLAG_REGISTERED;
+               /* Cleanup kprobe for reuse */
+               if (tp->rp.kp.symbol_name)
+                       tp->rp.kp.addr = NULL;
+       }
+}
+
 /* Unregister a trace_probe and probe_event: call with locking probe_lock */
 static void unregister_trace_probe(struct trace_probe *tp)
 {
-       if (probe_is_return(tp))
-               unregister_kretprobe(&tp->rp);
-       else
-               unregister_kprobe(&tp->rp.kp);
+       __unregister_trace_probe(tp);
        list_del(&tp->list);
        unregister_probe_event(tp);
 }
@@ -702,41 +851,65 @@ static int register_trace_probe(struct trace_probe *tp)
 
        mutex_lock(&probe_lock);
 
-       /* register as an event */
-       old_tp = find_probe_event(tp->call.name, tp->call.class->system);
+       /* Delete old (same name) event if exist */
+       old_tp = find_trace_probe(tp->call.name, tp->call.class->system);
        if (old_tp) {
-               /* delete old event */
                unregister_trace_probe(old_tp);
                free_trace_probe(old_tp);
        }
+
+       /* Register new event */
        ret = register_probe_event(tp);
        if (ret) {
                pr_warning("Failed to register probe event(%d)\n", ret);
                goto end;
        }
 
-       tp->rp.kp.flags |= KPROBE_FLAG_DISABLED;
-       if (probe_is_return(tp))
-               ret = register_kretprobe(&tp->rp);
-       else
-               ret = register_kprobe(&tp->rp.kp);
-
-       if (ret) {
-               pr_warning("Could not insert probe(%d)\n", ret);
-               if (ret == -EILSEQ) {
-                       pr_warning("Probing address(0x%p) is not an "
-                                  "instruction boundary.\n",
-                                  tp->rp.kp.addr);
-                       ret = -EINVAL;
-               }
+       /* Register k*probe */
+       ret = __register_trace_probe(tp);
+       if (ret < 0)
                unregister_probe_event(tp);
-       else
+       else
                list_add_tail(&tp->list, &probe_list);
+
 end:
        mutex_unlock(&probe_lock);
        return ret;
 }
 
+/* Module notifier call back, checking event on the module */
+static int trace_probe_module_callback(struct notifier_block *nb,
+                                      unsigned long val, void *data)
+{
+       struct module *mod = data;
+       struct trace_probe *tp;
+       int ret;
+
+       if (val != MODULE_STATE_COMING)
+               return NOTIFY_DONE;
+
+       /* Update probes on coming module */
+       mutex_lock(&probe_lock);
+       list_for_each_entry(tp, &probe_list, list) {
+               if (trace_probe_within_module(tp, mod)) {
+                       __unregister_trace_probe(tp);
+                       ret = __register_trace_probe(tp);
+                       if (ret)
+                               pr_warning("Failed to re-register probe %s on"
+                                          "%s: %d\n",
+                                          tp->call.name, mod->name, ret);
+               }
+       }
+       mutex_unlock(&probe_lock);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block trace_probe_module_nb = {
+       .notifier_call = trace_probe_module_callback,
+       .priority = 1   /* Invoked after kprobe module callback */
+};
+
 /* Split symbol and offset. */
 static int split_symbol_offset(char *symbol, unsigned long *offset)
 {
@@ -962,8 +1135,8 @@ static int create_trace_probe(int argc, char **argv)
 {
        /*
         * Argument syntax:
-        *  - Add kprobe: p[:[GRP/]EVENT] KSYM[+OFFS]|KADDR [FETCHARGS]
-        *  - Add kretprobe: r[:[GRP/]EVENT] KSYM[+0] [FETCHARGS]
+        *  - Add kprobe: p[:[GRP/]EVENT] [MOD:]KSYM[+OFFS]|KADDR [FETCHARGS]
+        *  - Add kretprobe: r[:[GRP/]EVENT] [MOD:]KSYM[+0] [FETCHARGS]
         * Fetch args:
         *  $retval     : fetch return value
         *  $stack      : fetch stack address
@@ -1025,7 +1198,7 @@ static int create_trace_probe(int argc, char **argv)
                        return -EINVAL;
                }
                mutex_lock(&probe_lock);
-               tp = find_probe_event(event, group);
+               tp = find_trace_probe(event, group);
                if (!tp) {
                        mutex_unlock(&probe_lock);
                        pr_info("Event %s/%s doesn't exist.\n", group, event);
@@ -1144,7 +1317,7 @@ error:
        return ret;
 }
 
-static void cleanup_all_probes(void)
+static void release_all_trace_probes(void)
 {
        struct trace_probe *tp;
 
@@ -1158,7 +1331,6 @@ static void cleanup_all_probes(void)
        mutex_unlock(&probe_lock);
 }
 
-
 /* Probes listing interfaces */
 static void *probes_seq_start(struct seq_file *m, loff_t *pos)
 {
@@ -1181,15 +1353,16 @@ static int probes_seq_show(struct seq_file *m, void *v)
        struct trace_probe *tp = v;
        int i;
 
-       seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p');
+       seq_printf(m, "%c", trace_probe_is_return(tp) ? 'r' : 'p');
        seq_printf(m, ":%s/%s", tp->call.class->system, tp->call.name);
 
        if (!tp->symbol)
                seq_printf(m, " 0x%p", tp->rp.kp.addr);
        else if (tp->rp.kp.offset)
-               seq_printf(m, " %s+%u", probe_symbol(tp), tp->rp.kp.offset);
+               seq_printf(m, " %s+%u", trace_probe_symbol(tp),
+                          tp->rp.kp.offset);
        else
-               seq_printf(m, " %s", probe_symbol(tp));
+               seq_printf(m, " %s", trace_probe_symbol(tp));
 
        for (i = 0; i < tp->nr_args; i++)
                seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm);
@@ -1209,7 +1382,7 @@ static int probes_open(struct inode *inode, struct file *file)
 {
        if ((file->f_mode & FMODE_WRITE) &&
            (file->f_flags & O_TRUNC))
-               cleanup_all_probes();
+               release_all_trace_probes();
 
        return seq_open(file, &probes_seq_op);
 }
@@ -1513,30 +1686,6 @@ partial:
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
-static int probe_event_enable(struct ftrace_event_call *call)
-{
-       struct trace_probe *tp = (struct trace_probe *)call->data;
-
-       tp->flags |= TP_FLAG_TRACE;
-       if (probe_is_return(tp))
-               return enable_kretprobe(&tp->rp);
-       else
-               return enable_kprobe(&tp->rp.kp);
-}
-
-static void probe_event_disable(struct ftrace_event_call *call)
-{
-       struct trace_probe *tp = (struct trace_probe *)call->data;
-
-       tp->flags &= ~TP_FLAG_TRACE;
-       if (!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE))) {
-               if (probe_is_return(tp))
-                       disable_kretprobe(&tp->rp);
-               else
-                       disable_kprobe(&tp->rp.kp);
-       }
-}
-
 #undef DEFINE_FIELD
 #define DEFINE_FIELD(type, item, name, is_signed)                      \
        do {                                                            \
@@ -1598,7 +1747,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len)
 
        const char *fmt, *arg;
 
-       if (!probe_is_return(tp)) {
+       if (!trace_probe_is_return(tp)) {
                fmt = "(%lx)";
                arg = "REC->" FIELD_STRING_IP;
        } else {
@@ -1715,49 +1864,25 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
        head = this_cpu_ptr(call->perf_events);
        perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head);
 }
-
-static int probe_perf_enable(struct ftrace_event_call *call)
-{
-       struct trace_probe *tp = (struct trace_probe *)call->data;
-
-       tp->flags |= TP_FLAG_PROFILE;
-
-       if (probe_is_return(tp))
-               return enable_kretprobe(&tp->rp);
-       else
-               return enable_kprobe(&tp->rp.kp);
-}
-
-static void probe_perf_disable(struct ftrace_event_call *call)
-{
-       struct trace_probe *tp = (struct trace_probe *)call->data;
-
-       tp->flags &= ~TP_FLAG_PROFILE;
-
-       if (!(tp->flags & TP_FLAG_TRACE)) {
-               if (probe_is_return(tp))
-                       disable_kretprobe(&tp->rp);
-               else
-                       disable_kprobe(&tp->rp.kp);
-       }
-}
 #endif /* CONFIG_PERF_EVENTS */
 
 static __kprobes
 int kprobe_register(struct ftrace_event_call *event, enum trace_reg type)
 {
+       struct trace_probe *tp = (struct trace_probe *)event->data;
+
        switch (type) {
        case TRACE_REG_REGISTER:
-               return probe_event_enable(event);
+               return enable_trace_probe(tp, TP_FLAG_TRACE);
        case TRACE_REG_UNREGISTER:
-               probe_event_disable(event);
+               disable_trace_probe(tp, TP_FLAG_TRACE);
                return 0;
 
 #ifdef CONFIG_PERF_EVENTS
        case TRACE_REG_PERF_REGISTER:
-               return probe_perf_enable(event);
+               return enable_trace_probe(tp, TP_FLAG_PROFILE);
        case TRACE_REG_PERF_UNREGISTER:
-               probe_perf_disable(event);
+               disable_trace_probe(tp, TP_FLAG_PROFILE);
                return 0;
 #endif
        }
@@ -1807,7 +1932,7 @@ static int register_probe_event(struct trace_probe *tp)
 
        /* Initialize ftrace_event_call */
        INIT_LIST_HEAD(&call->class->fields);
-       if (probe_is_return(tp)) {
+       if (trace_probe_is_return(tp)) {
                call->event.funcs = &kretprobe_funcs;
                call->class->define_fields = kretprobe_event_define_fields;
        } else {
@@ -1846,6 +1971,9 @@ static __init int init_kprobe_trace(void)
        struct dentry *d_tracer;
        struct dentry *entry;
 
+       if (register_module_notifier(&trace_probe_module_nb))
+               return -EINVAL;
+
        d_tracer = tracing_init_dentry();
        if (!d_tracer)
                return 0;
@@ -1899,12 +2027,12 @@ static __init int kprobe_trace_self_tests_init(void)
                warn++;
        } else {
                /* Enable trace point */
-               tp = find_probe_event("testprobe", KPROBE_EVENT_SYSTEM);
+               tp = find_trace_probe("testprobe", KPROBE_EVENT_SYSTEM);
                if (WARN_ON_ONCE(tp == NULL)) {
                        pr_warning("error on getting new probe.\n");
                        warn++;
                } else
-                       probe_event_enable(&tp->call);
+                       enable_trace_probe(tp, TP_FLAG_TRACE);
        }
 
        ret = command_trace_probe("r:testprobe2 kprobe_trace_selftest_target "
@@ -1914,12 +2042,12 @@ static __init int kprobe_trace_self_tests_init(void)
                warn++;
        } else {
                /* Enable trace point */
-               tp = find_probe_event("testprobe2", KPROBE_EVENT_SYSTEM);
+               tp = find_trace_probe("testprobe2", KPROBE_EVENT_SYSTEM);
                if (WARN_ON_ONCE(tp == NULL)) {
                        pr_warning("error on getting new probe.\n");
                        warn++;
                } else
-                       probe_event_enable(&tp->call);
+                       enable_trace_probe(tp, TP_FLAG_TRACE);
        }
 
        if (warn)
@@ -1940,7 +2068,7 @@ static __init int kprobe_trace_self_tests_init(void)
        }
 
 end:
-       cleanup_all_probes();
+       release_all_trace_probes();
        if (warn)
                pr_cont("NG: Some tests are failed. Please check them.\n");
        else
index e37de49..5199930 100644 (file)
@@ -1107,19 +1107,20 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
 {
        struct stack_entry *field;
        struct trace_seq *s = &iter->seq;
-       int i;
+       unsigned long *p;
+       unsigned long *end;
 
        trace_assign_type(field, iter->ent);
+       end = (unsigned long *)((long)iter->ent + iter->ent_size);
 
        if (!trace_seq_puts(s, "<stack trace>\n"))
                goto partial;
-       for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
-               if (!field->caller[i] || (field->caller[i] == ULONG_MAX))
-                       break;
+
+       for (p = field->caller; p && *p != ULONG_MAX && p < end; p++) {
                if (!trace_seq_puts(s, " => "))
                        goto partial;
 
-               if (!seq_print_ip_sym(s, field->caller[i], flags))
+               if (!seq_print_ip_sym(s, *p, flags))
                        goto partial;
                if (!trace_seq_puts(s, "\n"))
                        goto partial;
index a933e3a..36491cd 100644 (file)
@@ -200,7 +200,6 @@ static int is_softlockup(unsigned long touch_ts)
 }
 
 #ifdef CONFIG_HARDLOCKUP_DETECTOR
-void __weak hw_nmi_watchdog_set_attr(struct perf_event_attr *wd_attr) { }
 
 static struct perf_event_attr wd_hw_attr = {
        .type           = PERF_TYPE_HARDWARE,
@@ -372,7 +371,6 @@ static int watchdog_nmi_enable(int cpu)
 
        wd_attr = &wd_hw_attr;
        wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);
-       hw_nmi_watchdog_set_attr(wd_attr);
 
        /* Try to register using hardware perf events */
        event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
index 02bafce..2780d9c 100644 (file)
@@ -34,9 +34,11 @@ OPTIONS
        Specify vmlinux path which has debuginfo (Dwarf binary).
 
 -m::
---module=MODNAME::
+--module=MODNAME|PATH::
        Specify module name in which perf-probe searches probe points
-       or lines.
+       or lines. If a path of module file is passed, perf-probe
+       treat it as an offline module (this means you can add a probe on
+        a module which has not been loaded yet).
 
 -s::
 --source=PATH::
index 940257b..d0861bb 100644 (file)
@@ -279,6 +279,7 @@ LIB_H += util/thread.h
 LIB_H += util/thread_map.h
 LIB_H += util/trace-event.h
 LIB_H += util/probe-finder.h
+LIB_H += util/dwarf-aux.h
 LIB_H += util/probe-event.h
 LIB_H += util/pstack.h
 LIB_H += util/cpumap.h
@@ -435,6 +436,7 @@ else
        BASIC_CFLAGS += -DDWARF_SUPPORT
        EXTLIBS += -lelf -ldw
        LIB_OBJS += $(OUTPUT)util/probe-finder.o
+       LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
 endif # PERF_HAVE_DWARF_REGS
 endif # NO_DWARF
 
index 2c0e64d..5f2a5c7 100644 (file)
@@ -242,7 +242,8 @@ static const struct option options[] = {
        OPT_STRING('s', "source", &symbol_conf.source_prefix,
                   "directory", "path to kernel source"),
        OPT_STRING('m', "module", &params.target_module,
-                  "modname", "target module name"),
+                  "modname|path",
+                  "target module name (for online) or path (for offline)"),
 #endif
        OPT__DRY_RUN(&probe_event_dry_run),
        OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
new file mode 100644 (file)
index 0000000..fddf40f
--- /dev/null
@@ -0,0 +1,663 @@
+/*
+ * dwarf-aux.c : libdw auxiliary interfaces
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <stdbool.h>
+#include "util.h"
+#include "debug.h"
+#include "dwarf-aux.h"
+
+/**
+ * cu_find_realpath - Find the realpath of the target file
+ * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit)
+ * @fname:  The tail filename of the target file
+ *
+ * Find the real(long) path of @fname in @cu_die.
+ */
+const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
+{
+       Dwarf_Files *files;
+       size_t nfiles, i;
+       const char *src = NULL;
+       int ret;
+
+       if (!fname)
+               return NULL;
+
+       ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
+       if (ret != 0)
+               return NULL;
+
+       for (i = 0; i < nfiles; i++) {
+               src = dwarf_filesrc(files, i, NULL, NULL);
+               if (strtailcmp(src, fname) == 0)
+                       break;
+       }
+       if (i == nfiles)
+               return NULL;
+       return src;
+}
+
+/**
+ * cu_get_comp_dir - Get the path of compilation directory
+ * @cu_die: a CU DIE
+ *
+ * Get the path of compilation directory of given @cu_die.
+ * Since this depends on DW_AT_comp_dir, older gcc will not
+ * embedded it. In that case, this returns NULL.
+ */
+const char *cu_get_comp_dir(Dwarf_Die *cu_die)
+{
+       Dwarf_Attribute attr;
+       if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
+               return NULL;
+       return dwarf_formstring(&attr);
+}
+
+/**
+ * cu_find_lineinfo - Get a line number and file name for given address
+ * @cu_die: a CU DIE
+ * @addr: An address
+ * @fname: a pointer which returns the file name string
+ * @lineno: a pointer which returns the line number
+ *
+ * Find a line number and file name for @addr in @cu_die.
+ */
+int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr,
+                   const char **fname, int *lineno)
+{
+       Dwarf_Line *line;
+       Dwarf_Addr laddr;
+
+       line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr);
+       if (line && dwarf_lineaddr(line, &laddr) == 0 &&
+           addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) {
+               *fname = dwarf_linesrc(line, NULL, NULL);
+               if (!*fname)
+                       /* line number is useless without filename */
+                       *lineno = 0;
+       }
+
+       return *lineno ?: -ENOENT;
+}
+
+/**
+ * die_compare_name - Compare diename and tname
+ * @dw_die: a DIE
+ * @tname: a string of target name
+ *
+ * Compare the name of @dw_die and @tname. Return false if @dw_die has no name.
+ */
+bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
+{
+       const char *name;
+       name = dwarf_diename(dw_die);
+       return name ? (strcmp(tname, name) == 0) : false;
+}
+
+/**
+ * die_get_call_lineno - Get callsite line number of inline-function instance
+ * @in_die: a DIE of an inlined function instance
+ *
+ * Get call-site line number of @in_die. This means from where the inline
+ * function is called.
+ */
+int die_get_call_lineno(Dwarf_Die *in_die)
+{
+       Dwarf_Attribute attr;
+       Dwarf_Word ret;
+
+       if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
+               return -ENOENT;
+
+       dwarf_formudata(&attr, &ret);
+       return (int)ret;
+}
+
+/**
+ * die_get_type - Get type DIE
+ * @vr_die: a DIE of a variable
+ * @die_mem: where to store a type DIE
+ *
+ * Get a DIE of the type of given variable (@vr_die), and store
+ * it to die_mem. Return NULL if fails to get a type DIE.
+ */
+Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+       Dwarf_Attribute attr;
+
+       if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
+           dwarf_formref_die(&attr, die_mem))
+               return die_mem;
+       else
+               return NULL;
+}
+
+/* Get a type die, but skip qualifiers */
+static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+       int tag;
+
+       do {
+               vr_die = die_get_type(vr_die, die_mem);
+               if (!vr_die)
+                       break;
+               tag = dwarf_tag(vr_die);
+       } while (tag == DW_TAG_const_type ||
+                tag == DW_TAG_restrict_type ||
+                tag == DW_TAG_volatile_type ||
+                tag == DW_TAG_shared_type);
+
+       return vr_die;
+}
+
+/**
+ * die_get_real_type - Get a type die, but skip qualifiers and typedef
+ * @vr_die: a DIE of a variable
+ * @die_mem: where to store a type DIE
+ *
+ * Get a DIE of the type of given variable (@vr_die), and store
+ * it to die_mem. Return NULL if fails to get a type DIE.
+ * If the type is qualifiers (e.g. const) or typedef, this skips it
+ * and tries to find real type (structure or basic types, e.g. int).
+ */
+Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+       do {
+               vr_die = __die_get_real_type(vr_die, die_mem);
+       } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
+
+       return vr_die;
+}
+
+/* Get attribute and translate it as a udata */
+static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name,
+                             Dwarf_Word *result)
+{
+       Dwarf_Attribute attr;
+
+       if (dwarf_attr(tp_die, attr_name, &attr) == NULL ||
+           dwarf_formudata(&attr, result) != 0)
+               return -ENOENT;
+
+       return 0;
+}
+
+/**
+ * die_is_signed_type - Check whether a type DIE is signed or not
+ * @tp_die: a DIE of a type
+ *
+ * Get the encoding of @tp_die and return true if the encoding
+ * is signed.
+ */
+bool die_is_signed_type(Dwarf_Die *tp_die)
+{
+       Dwarf_Word ret;
+
+       if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret))
+               return false;
+
+       return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
+               ret == DW_ATE_signed_fixed);
+}
+
+/**
+ * die_get_data_member_location - Get the data-member offset
+ * @mb_die: a DIE of a member of a data structure
+ * @offs: The offset of the member in the data structure
+ *
+ * Get the offset of @mb_die in the data structure including @mb_die, and
+ * stores result offset to @offs. If any error occurs this returns errno.
+ */
+int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
+{
+       Dwarf_Attribute attr;
+       Dwarf_Op *expr;
+       size_t nexpr;
+       int ret;
+
+       if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
+               return -ENOENT;
+
+       if (dwarf_formudata(&attr, offs) != 0) {
+               /* DW_AT_data_member_location should be DW_OP_plus_uconst */
+               ret = dwarf_getlocation(&attr, &expr, &nexpr);
+               if (ret < 0 || nexpr == 0)
+                       return -ENOENT;
+
+               if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
+                       pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
+                                expr[0].atom, nexpr);
+                       return -ENOTSUP;
+               }
+               *offs = (Dwarf_Word)expr[0].number;
+       }
+       return 0;
+}
+
+/**
+ * die_find_child - Generic DIE search function in DIE tree
+ * @rt_die: a root DIE
+ * @callback: a callback function
+ * @data: a user data passed to the callback function
+ * @die_mem: a buffer for result DIE
+ *
+ * Trace DIE tree from @rt_die and call @callback for each child DIE.
+ * If @callback returns DIE_FIND_CB_END, this stores the DIE into
+ * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE,
+ * this continues to trace the tree. Optionally, @callback can return
+ * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only
+ * the children and trace only the siblings respectively.
+ * Returns NULL if @callback can't find any appropriate DIE.
+ */
+Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
+                         int (*callback)(Dwarf_Die *, void *),
+                         void *data, Dwarf_Die *die_mem)
+{
+       Dwarf_Die child_die;
+       int ret;
+
+       ret = dwarf_child(rt_die, die_mem);
+       if (ret != 0)
+               return NULL;
+
+       do {
+               ret = callback(die_mem, data);
+               if (ret == DIE_FIND_CB_END)
+                       return die_mem;
+
+               if ((ret & DIE_FIND_CB_CHILD) &&
+                   die_find_child(die_mem, callback, data, &child_die)) {
+                       memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
+                       return die_mem;
+               }
+       } while ((ret & DIE_FIND_CB_SIBLING) &&
+                dwarf_siblingof(die_mem, die_mem) == 0);
+
+       return NULL;
+}
+
+struct __addr_die_search_param {
+       Dwarf_Addr      addr;
+       Dwarf_Die       *die_mem;
+};
+
+/* die_find callback for non-inlined function search */
+static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
+{
+       struct __addr_die_search_param *ad = data;
+
+       if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
+           dwarf_haspc(fn_die, ad->addr)) {
+               memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
+               return DWARF_CB_ABORT;
+       }
+       return DWARF_CB_OK;
+}
+
+/**
+ * die_find_realfunc - Search a non-inlined function at given address
+ * @cu_die: a CU DIE which including @addr
+ * @addr: target address
+ * @die_mem: a buffer for result DIE
+ *
+ * Search a non-inlined function DIE which includes @addr. Stores the
+ * DIE to @die_mem and returns it if found. Returns NULl if failed.
+ */
+Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
+                                   Dwarf_Die *die_mem)
+{
+       struct __addr_die_search_param ad;
+       ad.addr = addr;
+       ad.die_mem = die_mem;
+       /* dwarf_getscopes can't find subprogram. */
+       if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
+               return NULL;
+       else
+               return die_mem;
+}
+
+/* die_find callback for inline function search */
+static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
+{
+       Dwarf_Addr *addr = data;
+
+       if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
+           dwarf_haspc(die_mem, *addr))
+               return DIE_FIND_CB_END;
+
+       return DIE_FIND_CB_CONTINUE;
+}
+
+/**
+ * die_find_inlinefunc - Search an inlined function at given address
+ * @cu_die: a CU DIE which including @addr
+ * @addr: target address
+ * @die_mem: a buffer for result DIE
+ *
+ * Search an inlined function DIE which includes @addr. Stores the
+ * DIE to @die_mem and returns it if found. Returns NULl if failed.
+ * If several inlined functions are expanded recursively, this trace
+ * it and returns deepest one.
+ */
+Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
+                              Dwarf_Die *die_mem)
+{
+       Dwarf_Die tmp_die;
+
+       sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die);
+       if (!sp_die)
+               return NULL;
+
+       /* Inlined function could be recursive. Trace it until fail */
+       while (sp_die) {
+               memcpy(die_mem, sp_die, sizeof(Dwarf_Die));
+               sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr,
+                                       &tmp_die);
+       }
+
+       return die_mem;
+}
+
+/* Line walker internal parameters */
+struct __line_walk_param {
+       const char *fname;
+       line_walk_callback_t callback;
+       void *data;
+       int retval;
+};
+
+static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
+{
+       struct __line_walk_param *lw = data;
+       Dwarf_Addr addr;
+       int lineno;
+
+       if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
+               lineno = die_get_call_lineno(in_die);
+               if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
+                       lw->retval = lw->callback(lw->fname, lineno, addr,
+                                                 lw->data);
+                       if (lw->retval != 0)
+                               return DIE_FIND_CB_END;
+               }
+       }
+       return DIE_FIND_CB_SIBLING;
+}
+
+/* Walk on lines of blocks included in given DIE */
+static int __die_walk_funclines(Dwarf_Die *sp_die,
+                               line_walk_callback_t callback, void *data)
+{
+       struct __line_walk_param lw = {
+               .callback = callback,
+               .data = data,
+               .retval = 0,
+       };
+       Dwarf_Die die_mem;
+       Dwarf_Addr addr;
+       int lineno;
+
+       /* Handle function declaration line */
+       lw.fname = dwarf_decl_file(sp_die);
+       if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
+           dwarf_entrypc(sp_die, &addr) == 0) {
+               lw.retval = callback(lw.fname, lineno, addr, data);
+               if (lw.retval != 0)
+                       goto done;
+       }
+       die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
+done:
+       return lw.retval;
+}
+
+static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
+{
+       struct __line_walk_param *lw = data;
+
+       lw->retval = __die_walk_funclines(sp_die, lw->callback, lw->data);
+       if (lw->retval != 0)
+               return DWARF_CB_ABORT;
+
+       return DWARF_CB_OK;
+}
+
+/**
+ * die_walk_lines - Walk on lines inside given DIE
+ * @rt_die: a root DIE (CU or subprogram)
+ * @callback: callback routine
+ * @data: user data
+ *
+ * Walk on all lines inside given @rt_die and call @callback on each line.
+ * If the @rt_die is a function, walk only on the lines inside the function,
+ * otherwise @rt_die must be a CU DIE.
+ * Note that this walks not only dwarf line list, but also function entries
+ * and inline call-site.
+ */
+int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
+{
+       Dwarf_Lines *lines;
+       Dwarf_Line *line;
+       Dwarf_Addr addr;
+       const char *fname;
+       int lineno, ret = 0;
+       Dwarf_Die die_mem, *cu_die;
+       size_t nlines, i;
+
+       /* Get the CU die */
+       if (dwarf_tag(rt_die) == DW_TAG_subprogram)
+               cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL);
+       else
+               cu_die = rt_die;
+       if (!cu_die) {
+               pr_debug2("Failed to get CU from subprogram\n");
+               return -EINVAL;
+       }
+
+       /* Get lines list in the CU */
+       if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
+               pr_debug2("Failed to get source lines on this CU.\n");
+               return -ENOENT;
+       }
+       pr_debug2("Get %zd lines from this CU\n", nlines);
+
+       /* Walk on the lines on lines list */
+       for (i = 0; i < nlines; i++) {
+               line = dwarf_onesrcline(lines, i);
+               if (line == NULL ||
+                   dwarf_lineno(line, &lineno) != 0 ||
+                   dwarf_lineaddr(line, &addr) != 0) {
+                       pr_debug2("Failed to get line info. "
+                                 "Possible error in debuginfo.\n");
+                       continue;
+               }
+               /* Filter lines based on address */
+               if (rt_die != cu_die)
+                       /*
+                        * Address filtering
+                        * The line is included in given function, and
+                        * no inline block includes it.
+                        */
+                       if (!dwarf_haspc(rt_die, addr) ||
+                           die_find_inlinefunc(rt_die, addr, &die_mem))
+                               continue;
+               /* Get source line */
+               fname = dwarf_linesrc(line, NULL, NULL);
+
+               ret = callback(fname, lineno, addr, data);
+               if (ret != 0)
+                       return ret;
+       }
+
+       /*
+        * Dwarf lines doesn't include function declarations and inlined
+        * subroutines. We have to check functions list or given function.
+        */
+       if (rt_die != cu_die)
+               ret = __die_walk_funclines(rt_die, callback, data);
+       else {
+               struct __line_walk_param param = {
+                       .callback = callback,
+                       .data = data,
+                       .retval = 0,
+               };
+               dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
+               ret = param.retval;
+       }
+
+       return ret;
+}
+
+struct __find_variable_param {
+       const char *name;
+       Dwarf_Addr addr;
+};
+
+static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
+{
+       struct __find_variable_param *fvp = data;
+       int tag;
+
+       tag = dwarf_tag(die_mem);
+       if ((tag == DW_TAG_formal_parameter ||
+            tag == DW_TAG_variable) &&
+           die_compare_name(die_mem, fvp->name))
+               return DIE_FIND_CB_END;
+
+       if (dwarf_haspc(die_mem, fvp->addr))
+               return DIE_FIND_CB_CONTINUE;
+       else
+               return DIE_FIND_CB_SIBLING;
+}
+
+/**
+ * die_find_variable_at - Find a given name variable at given address
+ * @sp_die: a function DIE
+ * @name: variable name
+ * @addr: address
+ * @die_mem: a buffer for result DIE
+ *
+ * Find a variable DIE called @name at @addr in @sp_die.
+ */
+Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
+                               Dwarf_Addr addr, Dwarf_Die *die_mem)
+{
+       struct __find_variable_param fvp = { .name = name, .addr = addr};
+
+       return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
+                             die_mem);
+}
+
+static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
+{
+       const char *name = data;
+
+       if ((dwarf_tag(die_mem) == DW_TAG_member) &&
+           die_compare_name(die_mem, name))
+               return DIE_FIND_CB_END;
+
+       return DIE_FIND_CB_SIBLING;
+}
+
+/**
+ * die_find_member - Find a given name member in a data structure
+ * @st_die: a data structure type DIE
+ * @name: member name
+ * @die_mem: a buffer for result DIE
+ *
+ * Find a member DIE called @name in @st_die.
+ */
+Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
+                          Dwarf_Die *die_mem)
+{
+       return die_find_child(st_die, __die_find_member_cb, (void *)name,
+                             die_mem);
+}
+
+/**
+ * die_get_typename - Get the name of given variable DIE
+ * @vr_die: a variable DIE
+ * @buf: a buffer for result type name
+ * @len: a max-length of @buf
+ *
+ * Get the name of @vr_die and stores it to @buf. Return the actual length
+ * of type name if succeeded. Return -E2BIG if @len is not enough long, and
+ * Return -ENOENT if failed to find type name.
+ * Note that the result will stores typedef name if possible, and stores
+ * "*(function_type)" if the type is a function pointer.
+ */
+int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+{
+       Dwarf_Die type;
+       int tag, ret, ret2;
+       const char *tmp = "";
+
+       if (__die_get_real_type(vr_die, &type) == NULL)
+               return -ENOENT;
+
+       tag = dwarf_tag(&type);
+       if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
+               tmp = "*";
+       else if (tag == DW_TAG_subroutine_type) {
+               /* Function pointer */
+               ret = snprintf(buf, len, "(function_type)");
+               return (ret >= len) ? -E2BIG : ret;
+       } else {
+               if (!dwarf_diename(&type))
+                       return -ENOENT;
+               if (tag == DW_TAG_union_type)
+                       tmp = "union ";
+               else if (tag == DW_TAG_structure_type)
+                       tmp = "struct ";
+               /* Write a base name */
+               ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
+               return (ret >= len) ? -E2BIG : ret;
+       }
+       ret = die_get_typename(&type, buf, len);
+       if (ret > 0) {
+               ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
+               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+       }
+       return ret;
+}
+
+/**
+ * die_get_varname - Get the name and type of given variable DIE
+ * @vr_die: a variable DIE
+ * @buf: a buffer for type and variable name
+ * @len: the max-length of @buf
+ *
+ * Get the name and type of @vr_die and stores it in @buf as "type\tname".
+ */
+int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+{
+       int ret, ret2;
+
+       ret = die_get_typename(vr_die, buf, len);
+       if (ret < 0) {
+               pr_debug("Failed to get type, make it unknown.\n");
+               ret = snprintf(buf, len, "(unknown_type)");
+       }
+       if (ret > 0) {
+               ret2 = snprintf(buf + ret, len - ret, "\t%s",
+                               dwarf_diename(vr_die));
+               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+       }
+       return ret;
+}
+
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
new file mode 100644 (file)
index 0000000..bc3b211
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef _DWARF_AUX_H
+#define _DWARF_AUX_H
+/*
+ * dwarf-aux.h : libdw auxiliary interfaces
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <dwarf.h>
+#include <elfutils/libdw.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/version.h>
+
+/* Find the realpath of the target file */
+extern const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
+
+/* Get DW_AT_comp_dir (should be NULL with older gcc) */
+extern const char *cu_get_comp_dir(Dwarf_Die *cu_die);
+
+/* Get a line number and file name for given address */
+extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
+                           const char **fname, int *lineno);
+
+/* Compare diename and tname */
+extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
+
+/* Get callsite line number of inline-function instance */
+extern int die_get_call_lineno(Dwarf_Die *in_die);
+
+/* Get type die */
+extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem);
+
+/* Get a type die, but skip qualifiers and typedef */
+extern Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem);
+
+/* Check whether the DIE is signed or not */
+extern bool die_is_signed_type(Dwarf_Die *tp_die);
+
+/* Get data_member_location offset */
+extern int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs);
+
+/* Return values for die_find_child() callbacks */
+enum {
+       DIE_FIND_CB_END = 0,            /* End of Search */
+       DIE_FIND_CB_CHILD = 1,          /* Search only children */
+       DIE_FIND_CB_SIBLING = 2,        /* Search only siblings */
+       DIE_FIND_CB_CONTINUE = 3,       /* Search children and siblings */
+};
+
+/* Search child DIEs */
+extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
+                                int (*callback)(Dwarf_Die *, void *),
+                                void *data, Dwarf_Die *die_mem);
+
+/* Search a non-inlined function including given address */
+extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
+                                   Dwarf_Die *die_mem);
+
+/* Search an inlined function including given address */
+extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
+                                     Dwarf_Die *die_mem);
+
+/* Walker on lines (Note: line number will not be sorted) */
+typedef int (* line_walk_callback_t) (const char *fname, int lineno,
+                                     Dwarf_Addr addr, void *data);
+
+/*
+ * Walk on lines inside given DIE. If the DIE is a subprogram, walk only on
+ * the lines inside the subprogram, otherwise the DIE must be a CU DIE.
+ */
+extern int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback,
+                         void *data);
+
+/* Find a variable called 'name' at given address */
+extern Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
+                                      Dwarf_Addr addr, Dwarf_Die *die_mem);
+
+/* Find a member called 'name' */
+extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
+                                 Dwarf_Die *die_mem);
+
+/* Get the name of given variable DIE */
+extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len);
+
+/* Get the name and type of given variable DIE, stored as "type\tname" */
+extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len);
+#endif
index f022316..b82d54f 100644 (file)
@@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module)
        struct rb_node *nd;
        struct map_groups *grp = &machine.kmaps;
 
+       /* A file path -- this is an offline module */
+       if (module && strchr(module, '/'))
+               return machine__new_module(&machine, 0, module);
+
        if (!module)
                module = "kernel";
 
@@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module)
 }
 
 #ifdef DWARF_SUPPORT
-static int open_vmlinux(const char *module)
+/* Open new debuginfo of given module */
+static struct debuginfo *open_debuginfo(const char *module)
 {
-       const char *path = kernel_get_module_path(module);
-       if (!path) {
-               pr_err("Failed to find path of %s module.\n",
-                      module ?: "kernel");
-               return -ENOENT;
+       const char *path;
+
+       /* A file path -- this is an offline module */
+       if (module && strchr(module, '/'))
+               path = module;
+       else {
+               path = kernel_get_module_path(module);
+
+               if (!path) {
+                       pr_err("Failed to find path of %s module.\n",
+                              module ?: "kernel");
+                       return NULL;
+               }
        }
-       pr_debug("Try to open %s\n", path);
-       return open(path, O_RDONLY);
+       return debuginfo__new(path);
 }
 
 /*
@@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
        struct map *map;
        u64 addr;
        int ret = -ENOENT;
+       struct debuginfo *dinfo;
 
        sym = __find_kernel_function_by_name(tp->symbol, &map);
        if (sym) {
                addr = map->unmap_ip(map, sym->start + tp->offset);
                pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol,
                         tp->offset, addr);
-               ret = find_perf_probe_point((unsigned long)addr, pp);
+
+               dinfo = debuginfo__new_online_kernel(addr);
+               if (dinfo) {
+                       ret = debuginfo__find_probe_point(dinfo,
+                                                (unsigned long)addr, pp);
+                       debuginfo__delete(dinfo);
+               } else {
+                       pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n",
+                                addr);
+                       ret = -ENOENT;
+               }
        }
        if (ret <= 0) {
                pr_debug("Failed to find corresponding probes from "
@@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
        return 0;
 }
 
+static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
+                                           int ntevs, const char *module)
+{
+       int i, ret = 0;
+       char *tmp;
+
+       if (!module)
+               return 0;
+
+       tmp = strrchr(module, '/');
+       if (tmp) {
+               /* This is a module path -- get the module name */
+               module = strdup(tmp + 1);
+               if (!module)
+                       return -ENOMEM;
+               tmp = strchr(module, '.');
+               if (tmp)
+                       *tmp = '\0';
+               tmp = (char *)module;   /* For free() */
+       }
+
+       for (i = 0; i < ntevs; i++) {
+               tevs[i].point.module = strdup(module);
+               if (!tevs[i].point.module) {
+                       ret = -ENOMEM;
+                       break;
+               }
+       }
+
+       if (tmp)
+               free(tmp);
+
+       return ret;
+}
+
 /* Try to find perf_probe_event with debuginfo */
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
-                                          struct probe_trace_event **tevs,
-                                          int max_tevs, const char *module)
+                                         struct probe_trace_event **tevs,
+                                         int max_tevs, const char *module)
 {
        bool need_dwarf = perf_probe_event_need_dwarf(pev);
-       int fd, ntevs;
+       struct debuginfo *dinfo = open_debuginfo(module);
+       int ntevs, ret = 0;
 
-       fd = open_vmlinux(module);
-       if (fd < 0) {
+       if (!dinfo) {
                if (need_dwarf) {
                        pr_warning("Failed to open debuginfo file.\n");
-                       return fd;
+                       return -ENOENT;
                }
-               pr_debug("Could not open vmlinux. Try to use symbols.\n");
+               pr_debug("Could not open debuginfo. Try to use symbols.\n");
                return 0;
        }
 
-       /* Searching trace events corresponding to probe event */
-       ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs);
+       /* Searching trace events corresponding to a probe event */
+       ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
+
+       debuginfo__delete(dinfo);
 
        if (ntevs > 0) {        /* Succeeded to find trace events */
                pr_debug("find %d probe_trace_events.\n", ntevs);
-               return ntevs;
+               if (module)
+                       ret = add_module_to_probe_trace_events(*tevs, ntevs,
+                                                              module);
+               return ret < 0 ? ret : ntevs;
        }
 
        if (ntevs == 0) {       /* No error but failed to find probe point. */
@@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module)
 {
        int l = 1;
        struct line_node *ln;
+       struct debuginfo *dinfo;
        FILE *fp;
-       int fd, ret;
+       int ret;
        char *tmp;
 
        /* Search a line range */
@@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module)
        if (ret < 0)
                return ret;
 
-       fd = open_vmlinux(module);
-       if (fd < 0) {
+       dinfo = open_debuginfo(module);
+       if (!dinfo) {
                pr_warning("Failed to open debuginfo file.\n");
-               return fd;
+               return -ENOENT;
        }
 
-       ret = find_line_range(fd, lr);
+       ret = debuginfo__find_line_range(dinfo, lr);
+       debuginfo__delete(dinfo);
        if (ret == 0) {
                pr_warning("Specified source line is not found.\n");
                return -ENOENT;
@@ -448,7 +513,8 @@ end:
        return ret;
 }
 
-static int show_available_vars_at(int fd, struct perf_probe_event *pev,
+static int show_available_vars_at(struct debuginfo *dinfo,
+                                 struct perf_probe_event *pev,
                                  int max_vls, struct strfilter *_filter,
                                  bool externs)
 {
@@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
                return -EINVAL;
        pr_debug("Searching variables at %s\n", buf);
 
-       ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
+       ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,
+                                               max_vls, externs);
        if (ret <= 0) {
                pr_err("Failed to find variables at %s (%d)\n", buf, ret);
                goto end;
@@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
                        int max_vls, const char *module,
                        struct strfilter *_filter, bool externs)
 {
-       int i, fd, ret = 0;
+       int i, ret = 0;
+       struct debuginfo *dinfo;
 
        ret = init_vmlinux();
        if (ret < 0)
                return ret;
 
+       dinfo = open_debuginfo(module);
+       if (!dinfo) {
+               pr_warning("Failed to open debuginfo file.\n");
+               return -ENOENT;
+       }
+
        setup_pager();
 
-       for (i = 0; i < npevs && ret >= 0; i++) {
-               fd = open_vmlinux(module);
-               if (fd < 0) {
-                       pr_warning("Failed to open debug information file.\n");
-                       ret = fd;
-                       break;
-               }
-               ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter,
+       for (i = 0; i < npevs && ret >= 0; i++)
+               ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter,
                                             externs);
-       }
+
+       debuginfo__delete(dinfo);
        return ret;
 }
 
@@ -990,7 +1059,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
 
 /* Parse probe_events event into struct probe_point */
 static int parse_probe_trace_command(const char *cmd,
-                                       struct probe_trace_event *tev)
+                                    struct probe_trace_event *tev)
 {
        struct probe_trace_point *tp = &tev->point;
        char pr;
@@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd,
 
        tp->retprobe = (pr == 'r');
 
-       /* Scan function name and offset */
-       ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol,
+       /* Scan module name(if there), function name and offset */
+       p = strchr(argv[1], ':');
+       if (p) {
+               tp->module = strndup(argv[1], p - argv[1]);
+               p++;
+       } else
+               p = argv[1];
+       ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol,
                     &tp->offset);
        if (ret == 1)
                tp->offset = 0;
@@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
        if (buf == NULL)
                return NULL;
 
-       len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
+       len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
                         tp->retprobe ? 'r' : 'p',
                         tev->group, tev->event,
+                        tp->module ?: "", tp->module ? ":" : "",
                         tp->symbol, tp->offset);
        if (len <= 0)
                goto error;
@@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
                free(tev->group);
        if (tev->point.symbol)
                free(tev->point.symbol);
+       if (tev->point.module)
+               free(tev->point.module);
        for (i = 0; i < tev->nargs; i++) {
                if (tev->args[i].name)
                        free(tev->args[i].name);
@@ -1729,7 +1807,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
        /* Convert perf_probe_event with debuginfo */
        ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
        if (ret != 0)
-               return ret;
+               return ret;     /* Found in debuginfo or got an error */
 
        /* Allocate trace event buffer */
        tev = *tevs = zalloc(sizeof(struct probe_trace_event));
@@ -1742,6 +1820,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
                ret = -ENOMEM;
                goto error;
        }
+       tev->point.module = strdup(module);
+       if (tev->point.module == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
        tev->point.offset = pev->point.offset;
        tev->point.retprobe = pev->point.retprobe;
        tev->nargs = pev->nargs;
index 3434fc9..a7dee83 100644 (file)
@@ -10,6 +10,7 @@ extern bool probe_event_dry_run;
 /* kprobe-tracer tracing point */
 struct probe_trace_point {
        char            *symbol;        /* Base symbol */
+       char            *module;        /* Module name */
        unsigned long   offset;         /* Offset from symbol */
        bool            retprobe;       /* Return probe flag */
 };
index 3b9d0b8..3e44a3e 100644 (file)
 /* Kprobe tracer basic type is up to u64 */
 #define MAX_BASIC_TYPE_BITS    64
 
-/*
- * Compare the tail of two strings.
- * Return 0 if whole of either string is same as another's tail part.
- */
-static int strtailcmp(const char *s1, const char *s2)
-{
-       int i1 = strlen(s1);
-       int i2 = strlen(s2);
-       while (--i1 >= 0 && --i2 >= 0) {
-               if (s1[i1] != s2[i2])
-                       return s1[i1] - s2[i2];
-       }
-       return 0;
-}
-
 /* Line number list operations */
 
 /* Add a line to line number list */
@@ -131,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = {
 };
 
 /* Get a Dwarf from offline image */
-static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
+static int debuginfo__init_offline_dwarf(struct debuginfo *self,
+                                        const char *path)
 {
        Dwfl_Module *mod;
-       Dwarf *dbg = NULL;
+       int fd;
 
-       if (!dwflp)
-               return NULL;
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return fd;
 
-       *dwflp = dwfl_begin(&offline_callbacks);
-       if (!*dwflp)
-               return NULL;
+       self->dwfl = dwfl_begin(&offline_callbacks);
+       if (!self->dwfl)
+               goto error;
 
-       mod = dwfl_report_offline(*dwflp, "", "", fd);
+       mod = dwfl_report_offline(self->dwfl, "", "", fd);
        if (!mod)
                goto error;
 
-       dbg = dwfl_module_getdwarf(mod, bias);
-       if (!dbg) {
+       self->dbg = dwfl_module_getdwarf(mod, &self->bias);
+       if (!self->dbg)
+               goto error;
+
+       return 0;
 error:
-               dwfl_end(*dwflp);
-               *dwflp = NULL;
-       }
-       return dbg;
+       if (self->dwfl)
+               dwfl_end(self->dwfl);
+       else
+               close(fd);
+       memset(self, 0, sizeof(*self));
+
+       return -ENOENT;
 }
 
 #if _ELFUTILS_PREREQ(0, 148)
@@ -189,597 +182,81 @@ static const Dwfl_Callbacks kernel_callbacks = {
 };
 
 /* Get a Dwarf from live kernel image */
-static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
-                                         Dwarf_Addr *bias)
+static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
+                                              Dwarf_Addr addr)
 {
-       Dwarf *dbg;
-
-       if (!dwflp)
-               return NULL;
-
-       *dwflp = dwfl_begin(&kernel_callbacks);
-       if (!*dwflp)
-               return NULL;
+       self->dwfl = dwfl_begin(&kernel_callbacks);
+       if (!self->dwfl)
+               return -EINVAL;
 
        /* Load the kernel dwarves: Don't care the result here */
-       dwfl_linux_kernel_report_kernel(*dwflp);
-       dwfl_linux_kernel_report_modules(*dwflp);
+       dwfl_linux_kernel_report_kernel(self->dwfl);
+       dwfl_linux_kernel_report_modules(self->dwfl);
 
-       dbg = dwfl_addrdwarf(*dwflp, addr, bias);
+       self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias);
        /* Here, check whether we could get a real dwarf */
-       if (!dbg) {
+       if (!self->dbg) {
                pr_debug("Failed to find kernel dwarf at %lx\n",
                         (unsigned long)addr);
-               dwfl_end(*dwflp);
-               *dwflp = NULL;
+               dwfl_end(self->dwfl);
+               memset(self, 0, sizeof(*self));
+               return -ENOENT;
        }
-       return dbg;
+
+       return 0;
 }
 #else
 /* With older elfutils, this just support kernel module... */
-static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp,
-                                         Dwarf_Addr *bias)
+static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
+                                              Dwarf_Addr addr __used)
 {
-       int fd;
        const char *path = kernel_get_module_path("kernel");
 
        if (!path) {
                pr_err("Failed to find vmlinux path\n");
-               return NULL;
+               return -ENOENT;
        }
 
        pr_debug2("Use file %s for debuginfo\n", path);
-       fd = open(path, O_RDONLY);
-       if (fd < 0)
-               return NULL;
-
-       return dwfl_init_offline_dwarf(fd, dwflp, bias);
+       return debuginfo__init_offline_dwarf(self, path);
 }
 #endif
 
-/* Dwarf wrappers */
-
-/* Find the realpath of the target file. */
-static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
-{
-       Dwarf_Files *files;
-       size_t nfiles, i;
-       const char *src = NULL;
-       int ret;
-
-       if (!fname)
-               return NULL;
-
-       ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
-       if (ret != 0)
-               return NULL;
-
-       for (i = 0; i < nfiles; i++) {
-               src = dwarf_filesrc(files, i, NULL, NULL);
-               if (strtailcmp(src, fname) == 0)
-                       break;
-       }
-       if (i == nfiles)
-               return NULL;
-       return src;
-}
-
-/* Get DW_AT_comp_dir (should be NULL with older gcc) */
-static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
-{
-       Dwarf_Attribute attr;
-       if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
-               return NULL;
-       return dwarf_formstring(&attr);
-}
-
-/* Get a line number and file name for given address */
-static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
-                           const char **fname, int *lineno)
-{
-       Dwarf_Line *line;
-       Dwarf_Addr laddr;
-
-       line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr);
-       if (line && dwarf_lineaddr(line, &laddr) == 0 &&
-           addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) {
-               *fname = dwarf_linesrc(line, NULL, NULL);
-               if (!*fname)
-                       /* line number is useless without filename */
-                       *lineno = 0;
-       }
-
-       return *lineno ?: -ENOENT;
-}
-
-/* Compare diename and tname */
-static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
-{
-       const char *name;
-       name = dwarf_diename(dw_die);
-       return name ? (strcmp(tname, name) == 0) : false;
-}
-
-/* Get callsite line number of inline-function instance */
-static int die_get_call_lineno(Dwarf_Die *in_die)
-{
-       Dwarf_Attribute attr;
-       Dwarf_Word ret;
-
-       if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
-               return -ENOENT;
-
-       dwarf_formudata(&attr, &ret);
-       return (int)ret;
-}
-
-/* Get type die */
-static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
-{
-       Dwarf_Attribute attr;
-
-       if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
-           dwarf_formref_die(&attr, die_mem))
-               return die_mem;
-       else
-               return NULL;
-}
-
-/* Get a type die, but skip qualifiers */
-static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
-{
-       int tag;
-
-       do {
-               vr_die = die_get_type(vr_die, die_mem);
-               if (!vr_die)
-                       break;
-               tag = dwarf_tag(vr_die);
-       } while (tag == DW_TAG_const_type ||
-                tag == DW_TAG_restrict_type ||
-                tag == DW_TAG_volatile_type ||
-                tag == DW_TAG_shared_type);
-
-       return vr_die;
-}
-
-/* Get a type die, but skip qualifiers and typedef */
-static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
-{
-       do {
-               vr_die = __die_get_real_type(vr_die, die_mem);
-       } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
-
-       return vr_die;
-}
-
-static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name,
-                             Dwarf_Word *result)
-{
-       Dwarf_Attribute attr;
-
-       if (dwarf_attr(tp_die, attr_name, &attr) == NULL ||
-           dwarf_formudata(&attr, result) != 0)
-               return -ENOENT;
-
-       return 0;
-}
-
-static bool die_is_signed_type(Dwarf_Die *tp_die)
-{
-       Dwarf_Word ret;
-
-       if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret))
-               return false;
-
-       return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
-               ret == DW_ATE_signed_fixed);
-}
-
-static int die_get_byte_size(Dwarf_Die *tp_die)
-{
-       Dwarf_Word ret;
-
-       if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret))
-               return 0;
-
-       return (int)ret;
-}
-
-static int die_get_bit_size(Dwarf_Die *tp_die)
-{
-       Dwarf_Word ret;
-
-       if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret))
-               return 0;
-
-       return (int)ret;
-}
-
-static int die_get_bit_offset(Dwarf_Die *tp_die)
-{
-       Dwarf_Word ret;
-
-       if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret))
-               return 0;
-
-       return (int)ret;
-}
-
-/* Get data_member_location offset */
-static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
-{
-       Dwarf_Attribute attr;
-       Dwarf_Op *expr;
-       size_t nexpr;
-       int ret;
-
-       if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
-               return -ENOENT;
-
-       if (dwarf_formudata(&attr, offs) != 0) {
-               /* DW_AT_data_member_location should be DW_OP_plus_uconst */
-               ret = dwarf_getlocation(&attr, &expr, &nexpr);
-               if (ret < 0 || nexpr == 0)
-                       return -ENOENT;
-
-               if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
-                       pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
-                                expr[0].atom, nexpr);
-                       return -ENOTSUP;
-               }
-               *offs = (Dwarf_Word)expr[0].number;
-       }
-       return 0;
-}
-
-/* Return values for die_find callbacks */
-enum {
-       DIE_FIND_CB_FOUND = 0,          /* End of Search */
-       DIE_FIND_CB_CHILD = 1,          /* Search only children */
-       DIE_FIND_CB_SIBLING = 2,        /* Search only siblings */
-       DIE_FIND_CB_CONTINUE = 3,       /* Search children and siblings */
-};
-
-/* Search a child die */
-static Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
-                                int (*callback)(Dwarf_Die *, void *),
-                                void *data, Dwarf_Die *die_mem)
+struct debuginfo *debuginfo__new(const char *path)
 {
-       Dwarf_Die child_die;
-       int ret;
-
-       ret = dwarf_child(rt_die, die_mem);
-       if (ret != 0)
+       struct debuginfo *self = zalloc(sizeof(struct debuginfo));
+       if (!self)
                return NULL;
 
-       do {
-               ret = callback(die_mem, data);
-               if (ret == DIE_FIND_CB_FOUND)
-                       return die_mem;
-
-               if ((ret & DIE_FIND_CB_CHILD) &&
-                   die_find_child(die_mem, callback, data, &child_die)) {
-                       memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
-                       return die_mem;
-               }
-       } while ((ret & DIE_FIND_CB_SIBLING) &&
-                dwarf_siblingof(die_mem, die_mem) == 0);
-
-       return NULL;
-}
-
-struct __addr_die_search_param {
-       Dwarf_Addr      addr;
-       Dwarf_Die       *die_mem;
-};
-
-static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
-{
-       struct __addr_die_search_param *ad = data;
-
-       if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
-           dwarf_haspc(fn_die, ad->addr)) {
-               memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
-               return DWARF_CB_ABORT;
+       if (debuginfo__init_offline_dwarf(self, path) < 0) {
+               free(self);
+               self = NULL;
        }
-       return DWARF_CB_OK;
-}
-
-/* Search a real subprogram including this line, */
-static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr,
-                                          Dwarf_Die *die_mem)
-{
-       struct __addr_die_search_param ad;
-       ad.addr = addr;
-       ad.die_mem = die_mem;
-       /* dwarf_getscopes can't find subprogram. */
-       if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
-               return NULL;
-       else
-               return die_mem;
-}
-
-/* die_find callback for inline function search */
-static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
-{
-       Dwarf_Addr *addr = data;
-
-       if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
-           dwarf_haspc(die_mem, *addr))
-               return DIE_FIND_CB_FOUND;
 
-       return DIE_FIND_CB_CONTINUE;
+       return self;
 }
 
-/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
-static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
-                                     Dwarf_Die *die_mem)
+struct debuginfo *debuginfo__new_online_kernel(unsigned long addr)
 {
-       Dwarf_Die tmp_die;
-
-       sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die);
-       if (!sp_die)
+       struct debuginfo *self = zalloc(sizeof(struct debuginfo));
+       if (!self)
                return NULL;
 
-       /* Inlined function could be recursive. Trace it until fail */
-       while (sp_die) {
-               memcpy(die_mem, sp_die, sizeof(Dwarf_Die));
-               sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr,
-                                       &tmp_die);
-       }
-
-       return die_mem;
-}
-
-/* Walker on lines (Note: line number will not be sorted) */
-typedef int (* line_walk_handler_t) (const char *fname, int lineno,
-                                    Dwarf_Addr addr, void *data);
-
-struct __line_walk_param {
-       const char *fname;
-       line_walk_handler_t handler;
-       void *data;
-       int retval;
-};
-
-static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
-{
-       struct __line_walk_param *lw = data;
-       Dwarf_Addr addr;
-       int lineno;
-
-       if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
-               lineno = die_get_call_lineno(in_die);
-               if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
-                       lw->retval = lw->handler(lw->fname, lineno, addr,
-                                                lw->data);
-                       if (lw->retval != 0)
-                               return DIE_FIND_CB_FOUND;
-               }
-       }
-       return DIE_FIND_CB_SIBLING;
-}
-
-/* Walk on lines of blocks included in given DIE */
-static int __die_walk_funclines(Dwarf_Die *sp_die,
-                               line_walk_handler_t handler, void *data)
-{
-       struct __line_walk_param lw = {
-               .handler = handler,
-               .data = data,
-               .retval = 0,
-       };
-       Dwarf_Die die_mem;
-       Dwarf_Addr addr;
-       int lineno;
-
-       /* Handle function declaration line */
-       lw.fname = dwarf_decl_file(sp_die);
-       if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
-           dwarf_entrypc(sp_die, &addr) == 0) {
-               lw.retval = handler(lw.fname, lineno, addr, data);
-               if (lw.retval != 0)
-                       goto done;
-       }
-       die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
-done:
-       return lw.retval;
-}
-
-static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
-{
-       struct __line_walk_param *lw = data;
-
-       lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data);
-       if (lw->retval != 0)
-               return DWARF_CB_ABORT;
-
-       return DWARF_CB_OK;
-}
-
-/*
- * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on
- * the lines inside the subprogram, otherwise PDIE must be a CU DIE.
- */
-static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler,
-                         void *data)
-{
-       Dwarf_Lines *lines;
-       Dwarf_Line *line;
-       Dwarf_Addr addr;
-       const char *fname;
-       int lineno, ret = 0;
-       Dwarf_Die die_mem, *cu_die;
-       size_t nlines, i;
-
-       /* Get the CU die */
-       if (dwarf_tag(pdie) == DW_TAG_subprogram)
-               cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL);
-       else
-               cu_die = pdie;
-       if (!cu_die) {
-               pr_debug2("Failed to get CU from subprogram\n");
-               return -EINVAL;
-       }
-
-       /* Get lines list in the CU */
-       if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
-               pr_debug2("Failed to get source lines on this CU.\n");
-               return -ENOENT;
-       }
-       pr_debug2("Get %zd lines from this CU\n", nlines);
-
-       /* Walk on the lines on lines list */
-       for (i = 0; i < nlines; i++) {
-               line = dwarf_onesrcline(lines, i);
-               if (line == NULL ||
-                   dwarf_lineno(line, &lineno) != 0 ||
-                   dwarf_lineaddr(line, &addr) != 0) {
-                       pr_debug2("Failed to get line info. "
-                                 "Possible error in debuginfo.\n");
-                       continue;
-               }
-               /* Filter lines based on address */
-               if (pdie != cu_die)
-                       /*
-                        * Address filtering
-                        * The line is included in given function, and
-                        * no inline block includes it.
-                        */
-                       if (!dwarf_haspc(pdie, addr) ||
-                           die_find_inlinefunc(pdie, addr, &die_mem))
-                               continue;
-               /* Get source line */
-               fname = dwarf_linesrc(line, NULL, NULL);
-
-               ret = handler(fname, lineno, addr, data);
-               if (ret != 0)
-                       return ret;
-       }
-
-       /*
-        * Dwarf lines doesn't include function declarations and inlined
-        * subroutines. We have to check functions list or given function.
-        */
-       if (pdie != cu_die)
-               ret = __die_walk_funclines(pdie, handler, data);
-       else {
-               struct __line_walk_param param = {
-                       .handler = handler,
-                       .data = data,
-                       .retval = 0,
-               };
-               dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
-               ret = param.retval;
+       if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) {
+               free(self);
+               self = NULL;
        }
 
-       return ret;
-}
-
-struct __find_variable_param {
-       const char *name;
-       Dwarf_Addr addr;
-};
-
-static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
-{
-       struct __find_variable_param *fvp = data;
-       int tag;
-
-       tag = dwarf_tag(die_mem);
-       if ((tag == DW_TAG_formal_parameter ||
-            tag == DW_TAG_variable) &&
-           die_compare_name(die_mem, fvp->name))
-               return DIE_FIND_CB_FOUND;
-
-       if (dwarf_haspc(die_mem, fvp->addr))
-               return DIE_FIND_CB_CONTINUE;
-       else
-               return DIE_FIND_CB_SIBLING;
-}
-
-/* Find a variable called 'name' at given address */
-static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
-                                      Dwarf_Addr addr, Dwarf_Die *die_mem)
-{
-       struct __find_variable_param fvp = { .name = name, .addr = addr};
-
-       return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
-                             die_mem);
-}
-
-static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
-{
-       const char *name = data;
-
-       if ((dwarf_tag(die_mem) == DW_TAG_member) &&
-           die_compare_name(die_mem, name))
-               return DIE_FIND_CB_FOUND;
-
-       return DIE_FIND_CB_SIBLING;
-}
-
-/* Find a member called 'name' */
-static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
-                                 Dwarf_Die *die_mem)
-{
-       return die_find_child(st_die, __die_find_member_cb, (void *)name,
-                             die_mem);
-}
-
-/* Get the name of given variable DIE */
-static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
-{
-       Dwarf_Die type;
-       int tag, ret, ret2;
-       const char *tmp = "";
-
-       if (__die_get_real_type(vr_die, &type) == NULL)
-               return -ENOENT;
-
-       tag = dwarf_tag(&type);
-       if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
-               tmp = "*";
-       else if (tag == DW_TAG_subroutine_type) {
-               /* Function pointer */
-               ret = snprintf(buf, len, "(function_type)");
-               return (ret >= len) ? -E2BIG : ret;
-       } else {
-               if (!dwarf_diename(&type))
-                       return -ENOENT;
-               if (tag == DW_TAG_union_type)
-                       tmp = "union ";
-               else if (tag == DW_TAG_structure_type)
-                       tmp = "struct ";
-               /* Write a base name */
-               ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
-               return (ret >= len) ? -E2BIG : ret;
-       }
-       ret = die_get_typename(&type, buf, len);
-       if (ret > 0) {
-               ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
-               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
-       }
-       return ret;
+       return self;
 }
 
-/* Get the name and type of given variable DIE, stored as "type\tname" */
-static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+void debuginfo__delete(struct debuginfo *self)
 {
-       int ret, ret2;
-
-       ret = die_get_typename(vr_die, buf, len);
-       if (ret < 0) {
-               pr_debug("Failed to get type, make it unknown.\n");
-               ret = snprintf(buf, len, "(unknown_type)");
+       if (self) {
+               if (self->dwfl)
+                       dwfl_end(self->dwfl);
+               free(self);
        }
-       if (ret > 0) {
-               ret2 = snprintf(buf + ret, len - ret, "\t%s",
-                               dwarf_diename(vr_die));
-               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
-       }
-       return ret;
 }
 
 /*
@@ -897,6 +374,7 @@ static int convert_variable_type(Dwarf_Die *vr_die,
        struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
        Dwarf_Die type;
        char buf[16];
+       int bsize, boffs, total;
        int ret;
 
        /* TODO: check all types */
@@ -906,11 +384,15 @@ static int convert_variable_type(Dwarf_Die *vr_die,
                return (tvar->type == NULL) ? -ENOMEM : 0;
        }
 
-       if (die_get_bit_size(vr_die) != 0) {
+       bsize = dwarf_bitsize(vr_die);
+       if (bsize > 0) {
                /* This is a bitfield */
-               ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die),
-                               die_get_bit_offset(vr_die),
-                               BYTES_TO_BITS(die_get_byte_size(vr_die)));
+               boffs = dwarf_bitoffset(vr_die);
+               total = dwarf_bytesize(vr_die);
+               if (boffs < 0 || total < 0)
+                       return -ENOENT;
+               ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs,
+                               BYTES_TO_BITS(total));
                goto formatted;
        }
 
@@ -958,10 +440,11 @@ static int convert_variable_type(Dwarf_Die *vr_die,
                return (tvar->type == NULL) ? -ENOMEM : 0;
        }
 
-       ret = BYTES_TO_BITS(die_get_byte_size(&type));
-       if (!ret)
+       ret = dwarf_bytesize(&type);
+       if (ret <= 0)
                /* No size ... try to use default type */
                return 0;
+       ret = BYTES_TO_BITS(ret);
 
        /* Check the bitwidth */
        if (ret > MAX_BASIC_TYPE_BITS) {
@@ -1025,7 +508,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
                        else
                                *ref_ptr = ref;
                }
-               ref->offset += die_get_byte_size(&type) * field->index;
+               ref->offset += dwarf_bytesize(&type) * field->index;
                if (!field->next)
                        /* Save vr_die for converting types */
                        memcpy(die_mem, vr_die, sizeof(*die_mem));
@@ -1245,8 +728,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
 
        /* If no real subprogram, find a real one */
        if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
-               sp_die = die_find_real_subprogram(&pf->cu_die,
-                                                 pf->addr, &die_mem);
+               sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem);
                if (!sp_die) {
                        pr_warning("Failed to find probe point in any "
                                   "functions.\n");
@@ -1504,28 +986,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
 }
 
 /* Find probe points from debuginfo */
-static int find_probes(int fd, struct probe_finder *pf)
+static int debuginfo__find_probes(struct debuginfo *self,
+                                 struct probe_finder *pf)
 {
        struct perf_probe_point *pp = &pf->pev->point;
        Dwarf_Off off, noff;
        size_t cuhl;
        Dwarf_Die *diep;
-       Dwarf *dbg = NULL;
-       Dwfl *dwfl;
-       Dwarf_Addr bias;        /* Currently ignored */
        int ret = 0;
 
-       dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
-       if (!dbg) {
-               pr_warning("No debug information found in the vmlinux - "
-                       "please rebuild with CONFIG_DEBUG_INFO=y.\n");
-               close(fd);      /* Without dwfl_end(), fd isn't closed. */
-               return -EBADF;
-       }
-
 #if _ELFUTILS_PREREQ(0, 142)
        /* Get the call frame information from this dwarf */
-       pf->cfi = dwarf_getcfi(dbg);
+       pf->cfi = dwarf_getcfi(self->dbg);
 #endif
 
        off = 0;
@@ -1544,7 +1016,8 @@ static int find_probes(int fd, struct probe_finder *pf)
                        .data = pf,
                };
 
-               dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0);
+               dwarf_getpubnames(self->dbg, pubname_search_cb,
+                                 &pubname_param, 0);
                if (pubname_param.found) {
                        ret = probe_point_search_cb(&pf->sp_die, &probe_param);
                        if (ret)
@@ -1553,9 +1026,9 @@ static int find_probes(int fd, struct probe_finder *pf)
        }
 
        /* Loop on CUs (Compilation Unit) */
-       while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
+       while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
                /* Get the DIE(Debugging Information Entry) of this CU */
-               diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
+               diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die);
                if (!diep)
                        continue;
 
@@ -1582,8 +1055,6 @@ static int find_probes(int fd, struct probe_finder *pf)
 
 found:
        line_list__free(&pf->lcache);
-       if (dwfl)
-               dwfl_end(dwfl);
 
        return ret;
 }
@@ -1629,8 +1100,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
 }
 
 /* Find probe_trace_events specified by perf_probe_event from debuginfo */
-int find_probe_trace_events(int fd, struct perf_probe_event *pev,
-                           struct probe_trace_event **tevs, int max_tevs)
+int debuginfo__find_trace_events(struct debuginfo *self,
+                                struct perf_probe_event *pev,
+                                struct probe_trace_event **tevs, int max_tevs)
 {
        struct trace_event_finder tf = {
                        .pf = {.pev = pev, .callback = add_probe_trace_event},
@@ -1645,7 +1117,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev,
        tf.tevs = *tevs;
        tf.ntevs = 0;
 
-       ret = find_probes(fd, &tf.pf);
+       ret = debuginfo__find_probes(self, &tf.pf);
        if (ret < 0) {
                free(*tevs);
                *tevs = NULL;
@@ -1739,9 +1211,10 @@ out:
 }
 
 /* Find available variables at given probe point */
-int find_available_vars_at(int fd, struct perf_probe_event *pev,
-                          struct variable_list **vls, int max_vls,
-                          bool externs)
+int debuginfo__find_available_vars_at(struct debuginfo *self,
+                                     struct perf_probe_event *pev,
+                                     struct variable_list **vls,
+                                     int max_vls, bool externs)
 {
        struct available_var_finder af = {
                        .pf = {.pev = pev, .callback = add_available_vars},
@@ -1756,7 +1229,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
        af.vls = *vls;
        af.nvls = 0;
 
-       ret = find_probes(fd, &af.pf);
+       ret = debuginfo__find_probes(self, &af.pf);
        if (ret < 0) {
                /* Free vlist for error */
                while (af.nvls--) {
@@ -1774,28 +1247,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
 }
 
 /* Reverse search */
-int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
+int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
+                               struct perf_probe_point *ppt)
 {
        Dwarf_Die cudie, spdie, indie;
-       Dwarf *dbg = NULL;
-       Dwfl *dwfl = NULL;
-       Dwarf_Addr _addr, baseaddr, bias = 0;
+       Dwarf_Addr _addr, baseaddr;
        const char *fname = NULL, *func = NULL, *tmp;
        int baseline = 0, lineno = 0, ret = 0;
 
-       /* Open the live linux kernel */
-       dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
-       if (!dbg) {
-               pr_warning("No debug information found in the vmlinux - "
-                       "please rebuild with CONFIG_DEBUG_INFO=y.\n");
-               ret = -EINVAL;
-               goto end;
-       }
-
        /* Adjust address with bias */
-       addr += bias;
+       addr += self->bias;
+
        /* Find cu die */
-       if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
+       if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) {
                pr_warning("Failed to find debug information for address %lx\n",
                           addr);
                ret = -EINVAL;
@@ -1807,7 +1271,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
        /* Don't care whether it failed or not */
 
        /* Find a corresponding function (name, baseline and baseaddr) */
-       if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
+       if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
                /* Get function entry information */
                tmp = dwarf_diename(&spdie);
                if (!tmp ||
@@ -1871,8 +1335,6 @@ post:
                }
        }
 end:
-       if (dwfl)
-               dwfl_end(dwfl);
        if (ret == 0 && (fname || func))
                ret = 1;        /* Found a point */
        return ret;
@@ -1982,26 +1444,15 @@ static int find_line_range_by_func(struct line_finder *lf)
        return param.retval;
 }
 
-int find_line_range(int fd, struct line_range *lr)
+int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr)
 {
        struct line_finder lf = {.lr = lr, .found = 0};
        int ret = 0;
        Dwarf_Off off = 0, noff;
        size_t cuhl;
        Dwarf_Die *diep;
-       Dwarf *dbg = NULL;
-       Dwfl *dwfl;
-       Dwarf_Addr bias;        /* Currently ignored */
        const char *comp_dir;
 
-       dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
-       if (!dbg) {
-               pr_warning("No debug information found in the vmlinux - "
-                       "please rebuild with CONFIG_DEBUG_INFO=y.\n");
-               close(fd);      /* Without dwfl_end(), fd isn't closed. */
-               return -EBADF;
-       }
-
        /* Fastpath: lookup by function name from .debug_pubnames section */
        if (lr->function) {
                struct pubname_callback_param pubname_param = {
@@ -2010,7 +1461,8 @@ int find_line_range(int fd, struct line_range *lr)
                struct dwarf_callback_param line_range_param = {
                        .data = (void *)&lf, .retval = 0};
 
-               dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0);
+               dwarf_getpubnames(self->dbg, pubname_search_cb,
+                                 &pubname_param, 0);
                if (pubname_param.found) {
                        line_range_search_cb(&lf.sp_die, &line_range_param);
                        if (lf.found)
@@ -2020,11 +1472,12 @@ int find_line_range(int fd, struct line_range *lr)
 
        /* Loop on CUs (Compilation Unit) */
        while (!lf.found && ret >= 0) {
-               if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0)
+               if (dwarf_nextcu(self->dbg, off, &noff, &cuhl,
+                                NULL, NULL, NULL) != 0)
                        break;
 
                /* Get the DIE(Debugging Information Entry) of this CU */
-               diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die);
+               diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die);
                if (!diep)
                        continue;
 
@@ -2058,7 +1511,6 @@ found:
        }
 
        pr_debug("path: %s\n", lr->path);
-       dwfl_end(dwfl);
        return (ret < 0) ? ret : lf.found;
 }
 
index 605730a..c478b42 100644 (file)
@@ -16,27 +16,42 @@ static inline int is_c_varname(const char *name)
 }
 
 #ifdef DWARF_SUPPORT
+
+#include "dwarf-aux.h"
+
+/* TODO: export debuginfo data structure even if no dwarf support */
+
+/* debug information structure */
+struct debuginfo {
+       Dwarf           *dbg;
+       Dwfl            *dwfl;
+       Dwarf_Addr      bias;
+};
+
+extern struct debuginfo *debuginfo__new(const char *path);
+extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr);
+extern void debuginfo__delete(struct debuginfo *self);
+
 /* Find probe_trace_events specified by perf_probe_event from debuginfo */
-extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
-                                   struct probe_trace_event **tevs,
-                                   int max_tevs);
+extern int debuginfo__find_trace_events(struct debuginfo *self,
+                                       struct perf_probe_event *pev,
+                                       struct probe_trace_event **tevs,
+                                       int max_tevs);
 
 /* Find a perf_probe_point from debuginfo */
-extern int find_perf_probe_point(unsigned long addr,
-                                struct perf_probe_point *ppt);
+extern int debuginfo__find_probe_point(struct debuginfo *self,
+                                      unsigned long addr,
+                                      struct perf_probe_point *ppt);
 
 /* Find a line range */
-extern int find_line_range(int fd, struct line_range *lr);
+extern int debuginfo__find_line_range(struct debuginfo *self,
+                                     struct line_range *lr);
 
 /* Find available variables */
-extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
-                                 struct variable_list **vls, int max_points,
-                                 bool externs);
-
-#include <dwarf.h>
-#include <elfutils/libdw.h>
-#include <elfutils/libdwfl.h>
-#include <elfutils/version.h>
+extern int debuginfo__find_available_vars_at(struct debuginfo *self,
+                                            struct perf_probe_event *pev,
+                                            struct variable_list **vls,
+                                            int max_points, bool externs);
 
 struct probe_finder {
        struct perf_probe_event *pev;           /* Target probe event */
index b9a985d..d583638 100644 (file)
@@ -294,3 +294,22 @@ bool strlazymatch(const char *str, const char *pat)
 {
        return __match_glob(str, pat, true);
 }
+
+/**
+ * strtailcmp - Compare the tail of two strings
+ * @s1: 1st string to be compared
+ * @s2: 2nd string to be compared
+ *
+ * Return 0 if whole of either string is same as another's tail part.
+ */
+int strtailcmp(const char *s1, const char *s2)
+{
+       int i1 = strlen(s1);
+       int i2 = strlen(s2);
+       while (--i1 >= 0 && --i2 >= 0) {
+               if (s1[i1] != s2[i2])
+                       return s1[i1] - s2[i2];
+       }
+       return 0;
+}
+
index 35729f4..3403f81 100644 (file)
@@ -183,106 +183,59 @@ int bigendian(void)
        return *ptr == 0x01020304;
 }
 
-static unsigned long long copy_file_fd(int fd)
+/* unfortunately, you can not stat debugfs or proc files for size */
+static void record_file(const char *file, size_t hdr_sz)
 {
        unsigned long long size = 0;
-       char buf[BUFSIZ];
-       int r;
-
-       do {
-               r = read(fd, buf, BUFSIZ);
-               if (r > 0) {
-                       size += r;
-                       write_or_die(buf, r);
-               }
-       } while (r > 0);
-
-       return size;
-}
-
-static unsigned long long copy_file(const char *file)
-{
-       unsigned long long size = 0;
-       int fd;
+       char buf[BUFSIZ], *sizep;
+       off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
+       int r, fd;
 
        fd = open(file, O_RDONLY);
        if (fd < 0)
                die("Can't read '%s'", file);
-       size = copy_file_fd(fd);
-       close(fd);
 
-       return size;
-}
-
-static unsigned long get_size_fd(int fd)
-{
-       unsigned long long size = 0;
-       char buf[BUFSIZ];
-       int r;
+       /* put in zeros for file size, then fill true size later */
+       write_or_die(&size, hdr_sz);
 
        do {
                r = read(fd, buf, BUFSIZ);
-               if (r > 0)
+               if (r > 0) {
                        size += r;
+                       write_or_die(buf, r);
+               }
        } while (r > 0);
-
-       lseek(fd, 0, SEEK_SET);
-
-       return size;
-}
-
-static unsigned long get_size(const char *file)
-{
-       unsigned long long size = 0;
-       int fd;
-
-       fd = open(file, O_RDONLY);
-       if (fd < 0)
-               die("Can't read '%s'", file);
-       size = get_size_fd(fd);
        close(fd);
 
-       return size;
+       /* ugh, handle big-endian hdr_size == 4 */
+       sizep = (char*)&size;
+       if (bigendian())
+               sizep += sizeof(u64) - hdr_sz;
+
+       if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
+               die("writing to %s", output_file);
 }
 
 static void read_header_files(void)
 {
-       unsigned long long size, check_size;
        char *path;
-       int fd;
+       struct stat st;
 
        path = get_tracing_file("events/header_page");
-       fd = open(path, O_RDONLY);
-       if (fd < 0)
+       if (stat(path, &st) < 0)
                die("can't read '%s'", path);
 
-       /* unfortunately, you can not stat debugfs files for size */
-       size = get_size_fd(fd);
-
        write_or_die("header_page", 12);
-       write_or_die(&size, 8);
-       check_size = copy_file_fd(fd);
-       close(fd);
-
-       if (size != check_size)
-               die("wrong size for '%s' size=%lld read=%lld",
-                   path, size, check_size);
+       record_file(path, 8);
        put_tracing_file(path);
 
        path = get_tracing_file("events/header_event");
-       fd = open(path, O_RDONLY);
-       if (fd < 0)
+       if (stat(path, &st) < 0)
                die("can't read '%s'", path);
 
-       size = get_size_fd(fd);
-
        write_or_die("header_event", 13);
-       write_or_die(&size, 8);
-       check_size = copy_file_fd(fd);
-       if (size != check_size)
-               die("wrong size for '%s'", path);
+       record_file(path, 8);
        put_tracing_file(path);
-       close(fd);
 }
 
 static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
@@ -298,7 +251,6 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
 
 static void copy_event_system(const char *sys, struct tracepoint_path *tps)
 {
-       unsigned long long size, check_size;
        struct dirent *dent;
        struct stat st;
        char *format;
@@ -338,14 +290,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
                sprintf(format, "%s/%s/format", sys, dent->d_name);
                ret = stat(format, &st);
 
-               if (ret >= 0) {
-                       /* unfortunately, you can not stat debugfs files for size */
-                       size = get_size(format);
-                       write_or_die(&size, 8);
-                       check_size = copy_file(format);
-                       if (size != check_size)
-                               die("error in size of file '%s'", format);
-               }
+               if (ret >= 0)
+                       record_file(format, 8);
 
                free(format);
        }
@@ -426,7 +372,7 @@ static void read_event_files(struct tracepoint_path *tps)
 
 static void read_proc_kallsyms(void)
 {
-       unsigned int size, check_size;
+       unsigned int size;
        const char *path = "/proc/kallsyms";
        struct stat st;
        int ret;
@@ -438,17 +384,12 @@ static void read_proc_kallsyms(void)
                write_or_die(&size, 4);
                return;
        }
-       size = get_size(path);
-       write_or_die(&size, 4);
-       check_size = copy_file(path);
-       if (size != check_size)
-               die("error in size of file '%s'", path);
-
+       record_file(path, 4);
 }
 
 static void read_ftrace_printk(void)
 {
-       unsigned int size, check_size;
+       unsigned int size;
        char *path;
        struct stat st;
        int ret;
@@ -461,11 +402,8 @@ static void read_ftrace_printk(void)
                write_or_die(&size, 4);
                goto out;
        }
-       size = get_size(path);
-       write_or_die(&size, 4);
-       check_size = copy_file(path);
-       if (size != check_size)
-               die("error in size of file '%s'", path);
+       record_file(path, 4);
+
 out:
        put_tracing_file(path);
 }
index fc78428..0128906 100644 (file)
@@ -238,6 +238,7 @@ char **argv_split(const char *str, int *argcp);
 void argv_free(char **argv);
 bool strglobmatch(const char *str, const char *pat);
 bool strlazymatch(const char *str, const char *pat);
+int strtailcmp(const char *s1, const char *s2);
 unsigned long convert_unit(unsigned long value, char *unit);
 int readn(int fd, void *buf, size_t size);