Skip to content

Commit

Permalink
tracing: identify which executable object the userspace address belon…
Browse files Browse the repository at this point in the history
…gs to

Impact: modify+improve the userstacktrace tracing visualization feature

Store thread group leader id, and use it to lookup the address in the
process's map. We could have looked up the address on thread's map,
but the thread might not exist by the time we are called. The process
might not exist either, but if you are reading trace_pipe, that is
unlikely.

Example usage:

 mount -t debugfs nodev /sys/kernel/debug
 cd /sys/kernel/debug/tracing
 echo userstacktrace >iter_ctrl
 echo sym-userobj >iter_ctrl
 echo sched_switch >current_tracer
 echo 1 >tracing_enabled
 cat trace_pipe >/tmp/trace&
 .... run application ...
 echo 0 >tracing_enabled
 cat /tmp/trace

You'll see stack entries like:

   /lib/libpthread-2.7.so[+0xd370]

You can convert them to function/line using:

   addr2line -fie /lib/libpthread-2.7.so 0xd370

Or:

   addr2line -fie /usr/lib/debug/libpthread-2.7.so 0xd370

For non-PIC/PIE executables this won't work:

   a.out[+0x73b]

You need to run the following: addr2line -fie a.out 0x40073b
(where 0x400000 is the default load address of a.out)

Signed-off-by: Török Edwin <edwintorok@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
Török Edwin authored and Ingo Molnar committed Nov 23, 2008
1 parent 74e2f33 commit b54d3de
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 9 deletions.
13 changes: 12 additions & 1 deletion Documentation/ftrace.txt
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ output. To see what is available, simply cat the file:

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".

Expand Down Expand Up @@ -381,6 +381,17 @@ Here are the available options:
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??)


Expand Down
86 changes: 79 additions & 7 deletions kernel/trace/trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#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>
Expand Down Expand Up @@ -276,6 +277,7 @@ static const char *trace_options[] = {
"branch",
"annotate",
"userstacktrace",
"sym-userobj",
NULL
};

Expand Down Expand Up @@ -422,6 +424,28 @@ trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len)
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)
{
Expand Down Expand Up @@ -802,6 +826,7 @@ tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags,

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) |
Expand Down Expand Up @@ -1429,28 +1454,73 @@ seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
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;
}

Expand Down Expand Up @@ -1775,8 +1845,7 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
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:
Expand Down Expand Up @@ -3581,6 +3650,9 @@ void ftrace_dump(void)
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;
Expand Down
3 changes: 2 additions & 1 deletion kernel/trace/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,8 @@ enum trace_iterator_flags {
TRACE_ITER_PREEMPTONLY = 0x800,
TRACE_ITER_BRANCH = 0x1000,
TRACE_ITER_ANNOTATE = 0x2000,
TRACE_ITER_USERSTACKTRACE = 0x4000
TRACE_ITER_USERSTACKTRACE = 0x4000,
TRACE_ITER_SYM_USEROBJ = 0x8000
};

/*
Expand Down

0 comments on commit b54d3de

Please sign in to comment.