cat /debug/tracing/trace_options
   print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \
- noblock nostacktrace nosched-tree nouserstacktrace
+ noblock nostacktrace nosched-tree nouserstacktrace nosym-userobj
 
 To disable one of the options, echo in the option prepended with "no".
 
   userstacktrace - This option changes the trace.
                   It records a stacktrace of the current userspace thread.
 
+  sym-userobj - when user stacktrace are enabled, look up which object the
+               address belongs to, and print a relative address
+               This is especially useful when ASLR is on, otherwise you don't
+               get a chance to resolve the address to object/file/line after the app is no
+               longer running
+
+               The lookup is performed when you read trace,trace_pipe,latency_trace. Example:
+
+               a.out-1623  [000] 40874.465068: /root/a.out[+0x480] <-/root/a.out[+0
+x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6]
+
   sched-tree - TBD (any users??)
 
 
 
 #include <linux/gfp.h>
 #include <linux/fs.h>
 #include <linux/kprobes.h>
+#include <linux/seq_file.h>
 #include <linux/writeback.h>
 
 #include <linux/stacktrace.h>
        "branch",
        "annotate",
        "userstacktrace",
+       "sym-userobj",
        NULL
 };
 
        return trace_seq_putmem(s, hex, j);
 }
 
+static int
+trace_seq_path(struct trace_seq *s, struct path *path)
+{
+       unsigned char *p;
+
+       if (s->len >= (PAGE_SIZE - 1))
+               return 0;
+       p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
+       if (!IS_ERR(p)) {
+               p = mangle_path(s->buffer + s->len, p, "\n");
+               if (p) {
+                       s->len = p - s->buffer;
+                       return 1;
+               }
+       } else {
+               s->buffer[s->len++] = '?';
+               return 1;
+       }
+
+       return 0;
+}
+
 static void
 trace_seq_reset(struct trace_seq *s)
 {
 
        entry->preempt_count            = pc & 0xff;
        entry->pid                      = (tsk) ? tsk->pid : 0;
+       entry->tgid                     = (tsk) ? tsk->tgid : 0;
        entry->flags =
 #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
                (irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) |
        return ret;
 }
 
+static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
+                                   unsigned long ip, unsigned long sym_flags)
+{
+       struct file *file = NULL;
+       unsigned long vmstart = 0;
+       int ret = 1;
+
+       if (mm) {
+               const struct vm_area_struct *vma = find_vma(mm, ip);
+               if (vma) {
+                       file = vma->vm_file;
+                       vmstart = vma->vm_start;
+               }
+       }
+       if (file) {
+               ret = trace_seq_path(s, &file->f_path);
+               if (ret)
+                       ret = trace_seq_printf(s, "[+0x%lx]",
+                                       ip - vmstart);
+       }
+       if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file))
+               ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
+       return ret;
+}
+
 static int
 seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s,
-               unsigned long sym_flags)
+                     unsigned long sym_flags)
 {
+       struct mm_struct *mm = NULL;
        int ret = 1;
        unsigned i;
 
+       if (trace_flags & TRACE_ITER_SYM_USEROBJ) {
+               struct task_struct *task;
+               /*
+                * we do the lookup on the thread group leader,
+                * since individual threads might have already quit!
+                */
+               rcu_read_lock();
+               task = find_task_by_vpid(entry->ent.tgid);
+               rcu_read_unlock();
+
+               if (task)
+                       mm = get_task_mm(task);
+       }
+
        for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
                unsigned long ip = entry->caller[i];
 
                if (ip == ULONG_MAX || !ret)
                        break;
-               if (i)
+               if (i && ret)
                        ret = trace_seq_puts(s, " <- ");
                if (!ip) {
-                       ret = trace_seq_puts(s, "??");
+                       if (ret)
+                               ret = trace_seq_puts(s, "??");
                        continue;
                }
-               if (ret /*&& (sym_flags & TRACE_ITER_SYM_ADDR)*/)
-                       ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
+               if (!ret)
+                       break;
+               if (ret)
+                       ret = seq_print_user_ip(s, mm, ip, sym_flags);
        }
 
+       if (mm)
+               mmput(mm);
        return ret;
 }
 
                trace_assign_type(field, entry);
 
                seq_print_userip_objs(field, s, sym_flags);
-               if (entry->flags & TRACE_FLAG_CONT)
-                       trace_seq_print_cont(s, iter);
+               trace_seq_putc(s, '\n');
                break;
        }
        default:
                atomic_inc(&global_trace.data[cpu]->disabled);
        }
 
+       /* don't look at user memory in panic mode */
+       trace_flags &= ~TRACE_ITER_SYM_USEROBJ;
+
        printk(KERN_TRACE "Dumping ftrace buffer:\n");
 
        iter.tr = &global_trace;