perf: Have arches handle the backtrace
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 19 Jul 2016 23:26:24 +0000 (19:26 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 27 Jul 2016 16:52:43 +0000 (12:52 -0400)
Instead of having arch-independent perf do the backtraces, let the arch
generate the BT and pass it to the arch-independent part.

This will matter for NMI tracing, where the actual pc_list will be saved
somewhere temporarily and then emitted.  Only x86 needs to know about that.

This also removes the add_trace() option.  It's a bad thing to do.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/perfmon.c
kern/drivers/dev/kprof.c
kern/include/profiler.h
kern/src/profiler.c

index c313c91..0038146 100644 (file)
@@ -442,6 +442,23 @@ static uint64_t perfmon_make_sample_event(const struct perfmon_event *pev)
        return pev->user_data;
 }
 
+static void profiler_add_hw_sample(struct hw_trapframe *hw_tf, uint64_t info)
+{
+       #define PROFILER_BT_DEPTH 16
+       uintptr_t pc_list[PROFILER_BT_DEPTH];
+       size_t n;
+       uintptr_t pc = get_hwtf_pc(hw_tf);
+       uintptr_t fp = get_hwtf_fp(hw_tf);
+
+       if (in_kernel(hw_tf)) {
+               n = backtrace_list(pc, fp, pc_list, PROFILER_BT_DEPTH);
+               profiler_push_kernel_backtrace(pc_list, n, info);
+       } else {
+               n = backtrace_user_list(pc, fp, pc_list, PROFILER_BT_DEPTH);
+               profiler_push_user_backtrace(pc_list, n, info);
+       }
+}
+
 void perfmon_interrupt(struct hw_trapframe *hw_tf, void *data)
 {
        int i;
index e0ab229..c2b90d2 100644 (file)
@@ -374,9 +374,6 @@ static long kprof_write(struct chan *c, void *a, long n, int64_t unused)
                        error(EFAIL, kprof_control_usage);
                }
                break;
-       case Kprofdataqid:
-               profiler_add_trace((uintptr_t) strtoul(a, 0, 0), 0);
-               break;
        case Kptraceqid:
                if (a && (n > 0)) {
                        char *uptr = user_strdup_errno(current, a, n);
index 7fa9504..8a40c62 100644 (file)
@@ -20,11 +20,11 @@ void profiler_setup(void);
 void profiler_cleanup(void);
 void profiler_start(void);
 void profiler_stop(void);
-void profiler_add_kernel_backtrace(uintptr_t pc, uintptr_t fp, uint64_t info);
-void profiler_add_user_backtrace(uintptr_t pc, uintptr_t fp, uint64_t info);
-void profiler_add_trace(uintptr_t pc, uint64_t info);
+void profiler_push_kernel_backtrace(uintptr_t *pc_list, size_t nr_pcs,
+                                    uint64_t info);
+void profiler_push_user_backtrace(uintptr_t *pc_list, size_t nr_pcs,
+                                  uint64_t info);
 void profiler_trace_data_flush(void);
-void profiler_add_hw_sample(struct hw_trapframe *hw_tf, uint64_t info);
 int profiler_size(void);
 int profiler_read(void *va, int n);
 void profiler_notify_mmap(struct proc *p, uintptr_t addr, size_t size, int prot,
index c85f45b..c94caed 100644 (file)
@@ -7,10 +7,9 @@
  * creation.
  *
  * Events are collected in a central qio queue.  High-frequency events (e.g.
- * profiler_add_hw_sample()) are collected in per-core buffers, which are
- * flushed to the central queue when they fill up or on command.
- * Lower-frequency events (e.g. profiler_notify_mmap()) just go straight to the
- * central queue.
+ * IRQ backtraces()) are collected in per-core buffers, which are flushed to the
+ * central queue when they fill up or on command.  Lower-frequency events (e.g.
+ * profiler_notify_mmap()) just go straight to the central queue.
  *
  * Currently there is one global profiler.  Kprof is careful to only have one
  * open profiler at a time.  We assert that this is true.  TODO: stop using the
@@ -45,7 +44,6 @@
 #include "profiler.h"
 
 #define PROFILER_MAX_PRG_PATH  256
-#define PROFILER_BT_DEPTH 16
 
 #define VBE_MAX_SIZE(t) ((8 * sizeof(t) + 6) / 7)
 
@@ -498,57 +496,31 @@ void profiler_trace_data_flush(void)
        smp_do_in_cores(&cset, profiler_core_flush, NULL);
 }
 
-void profiler_add_trace(uintptr_t pc, uint64_t info)
-{
-       if (is_user_raddr((void *) pc, 1))
-               profiler_add_user_backtrace(pc, 0, info);
-       else
-               profiler_add_kernel_backtrace(pc, 0, info);
-}
-
-void profiler_add_kernel_backtrace(uintptr_t pc, uintptr_t fp, uint64_t info)
+void profiler_push_kernel_backtrace(uintptr_t *pc_list, size_t nr_pcs,
+                                    uint64_t info)
 {
        if (kref_get_not_zero(&profiler_kref, 1)) {
                struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(core_id());
 
-               if (profiler_percpu_ctx && cpu_buf->tracing) {
-                       uintptr_t trace[PROFILER_BT_DEPTH];
-                       size_t n;
-
-                       n = backtrace_list(pc, fp, trace, PROFILER_BT_DEPTH);
-                       profiler_push_kernel_trace64(cpu_buf, trace, n, info);
-               }
+               if (profiler_percpu_ctx && cpu_buf->tracing)
+                       profiler_push_kernel_trace64(cpu_buf, pc_list, nr_pcs, info);
                kref_put(&profiler_kref);
        }
 }
 
-void profiler_add_user_backtrace(uintptr_t pc, uintptr_t fp, uint64_t info)
+void profiler_push_user_backtrace(uintptr_t *pc_list, size_t nr_pcs,
+                                  uint64_t info)
 {
        if (kref_get_not_zero(&profiler_kref, 1)) {
                struct proc *p = current;
                struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(core_id());
 
-               if (p && profiler_percpu_ctx && cpu_buf->tracing) {
-                       uintptr_t trace[PROFILER_BT_DEPTH];
-                       size_t n;
-
-                       n = backtrace_user_list(pc, fp, trace, PROFILER_BT_DEPTH);
-                       profiler_push_user_trace64(cpu_buf, p, trace, n, info);
-               }
+               if (profiler_percpu_ctx && cpu_buf->tracing)
+                       profiler_push_user_trace64(cpu_buf, p, pc_list, nr_pcs, info);
                kref_put(&profiler_kref);
        }
 }
 
-void profiler_add_hw_sample(struct hw_trapframe *hw_tf, uint64_t info)
-{
-       if (in_kernel(hw_tf))
-               profiler_add_kernel_backtrace(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf),
-                                             info);
-       else
-               profiler_add_user_backtrace(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf),
-                                           info);
-}
-
 int profiler_size(void)
 {
        return profiler_queue ? qlen(profiler_queue) : 0;