diff options
author | Török Edwin <edwintorok@gmail.com> | 2008-11-22 06:28:48 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-23 03:45:42 -0500 |
commit | b54d3de9f3b8956653b06f1a32e9f9321c6d9027 (patch) | |
tree | fef98e07a9327371304811191cafebdd65311502 /kernel/trace/trace.c | |
parent | 74e2f334f4440cbcb63e9ebbcdcea430d41bdfa3 (diff) |
tracing: identify which executable object the userspace address belongs 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>
Diffstat (limited to 'kernel/trace/trace.c')
-rw-r--r-- | kernel/trace/trace.c | 86 |
1 files changed, 79 insertions, 7 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ced8b4fa9f51..62776b71b1c5 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/gfp.h> | 30 | #include <linux/gfp.h> |
31 | #include <linux/fs.h> | 31 | #include <linux/fs.h> |
32 | #include <linux/kprobes.h> | 32 | #include <linux/kprobes.h> |
33 | #include <linux/seq_file.h> | ||
33 | #include <linux/writeback.h> | 34 | #include <linux/writeback.h> |
34 | 35 | ||
35 | #include <linux/stacktrace.h> | 36 | #include <linux/stacktrace.h> |
@@ -276,6 +277,7 @@ static const char *trace_options[] = { | |||
276 | "branch", | 277 | "branch", |
277 | "annotate", | 278 | "annotate", |
278 | "userstacktrace", | 279 | "userstacktrace", |
280 | "sym-userobj", | ||
279 | NULL | 281 | NULL |
280 | }; | 282 | }; |
281 | 283 | ||
@@ -422,6 +424,28 @@ trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len) | |||
422 | return trace_seq_putmem(s, hex, j); | 424 | return trace_seq_putmem(s, hex, j); |
423 | } | 425 | } |
424 | 426 | ||
427 | static int | ||
428 | trace_seq_path(struct trace_seq *s, struct path *path) | ||
429 | { | ||
430 | unsigned char *p; | ||
431 | |||
432 | if (s->len >= (PAGE_SIZE - 1)) | ||
433 | return 0; | ||
434 | p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); | ||
435 | if (!IS_ERR(p)) { | ||
436 | p = mangle_path(s->buffer + s->len, p, "\n"); | ||
437 | if (p) { | ||
438 | s->len = p - s->buffer; | ||
439 | return 1; | ||
440 | } | ||
441 | } else { | ||
442 | s->buffer[s->len++] = '?'; | ||
443 | return 1; | ||
444 | } | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
425 | static void | 449 | static void |
426 | trace_seq_reset(struct trace_seq *s) | 450 | trace_seq_reset(struct trace_seq *s) |
427 | { | 451 | { |
@@ -802,6 +826,7 @@ tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags, | |||
802 | 826 | ||
803 | entry->preempt_count = pc & 0xff; | 827 | entry->preempt_count = pc & 0xff; |
804 | entry->pid = (tsk) ? tsk->pid : 0; | 828 | entry->pid = (tsk) ? tsk->pid : 0; |
829 | entry->tgid = (tsk) ? tsk->tgid : 0; | ||
805 | entry->flags = | 830 | entry->flags = |
806 | #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT | 831 | #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT |
807 | (irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) | | 832 | (irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) | |
@@ -1429,28 +1454,73 @@ seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags) | |||
1429 | return ret; | 1454 | return ret; |
1430 | } | 1455 | } |
1431 | 1456 | ||
1457 | static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm, | ||
1458 | unsigned long ip, unsigned long sym_flags) | ||
1459 | { | ||
1460 | struct file *file = NULL; | ||
1461 | unsigned long vmstart = 0; | ||
1462 | int ret = 1; | ||
1463 | |||
1464 | if (mm) { | ||
1465 | const struct vm_area_struct *vma = find_vma(mm, ip); | ||
1466 | if (vma) { | ||
1467 | file = vma->vm_file; | ||
1468 | vmstart = vma->vm_start; | ||
1469 | } | ||
1470 | } | ||
1471 | if (file) { | ||
1472 | ret = trace_seq_path(s, &file->f_path); | ||
1473 | if (ret) | ||
1474 | ret = trace_seq_printf(s, "[+0x%lx]", | ||
1475 | ip - vmstart); | ||
1476 | } | ||
1477 | if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file)) | ||
1478 | ret = trace_seq_printf(s, " <" IP_FMT ">", ip); | ||
1479 | return ret; | ||
1480 | } | ||
1481 | |||
1432 | static int | 1482 | static int |
1433 | seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, | 1483 | seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, |
1434 | unsigned long sym_flags) | 1484 | unsigned long sym_flags) |
1435 | { | 1485 | { |
1486 | struct mm_struct *mm = NULL; | ||
1436 | int ret = 1; | 1487 | int ret = 1; |
1437 | unsigned i; | 1488 | unsigned i; |
1438 | 1489 | ||
1490 | if (trace_flags & TRACE_ITER_SYM_USEROBJ) { | ||
1491 | struct task_struct *task; | ||
1492 | /* | ||
1493 | * we do the lookup on the thread group leader, | ||
1494 | * since individual threads might have already quit! | ||
1495 | */ | ||
1496 | rcu_read_lock(); | ||
1497 | task = find_task_by_vpid(entry->ent.tgid); | ||
1498 | rcu_read_unlock(); | ||
1499 | |||
1500 | if (task) | ||
1501 | mm = get_task_mm(task); | ||
1502 | } | ||
1503 | |||
1439 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { | 1504 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { |
1440 | unsigned long ip = entry->caller[i]; | 1505 | unsigned long ip = entry->caller[i]; |
1441 | 1506 | ||
1442 | if (ip == ULONG_MAX || !ret) | 1507 | if (ip == ULONG_MAX || !ret) |
1443 | break; | 1508 | break; |
1444 | if (i) | 1509 | if (i && ret) |
1445 | ret = trace_seq_puts(s, " <- "); | 1510 | ret = trace_seq_puts(s, " <- "); |
1446 | if (!ip) { | 1511 | if (!ip) { |
1447 | ret = trace_seq_puts(s, "??"); | 1512 | if (ret) |
1513 | ret = trace_seq_puts(s, "??"); | ||
1448 | continue; | 1514 | continue; |
1449 | } | 1515 | } |
1450 | if (ret /*&& (sym_flags & TRACE_ITER_SYM_ADDR)*/) | 1516 | if (!ret) |
1451 | ret = trace_seq_printf(s, " <" IP_FMT ">", ip); | 1517 | break; |
1518 | if (ret) | ||
1519 | ret = seq_print_user_ip(s, mm, ip, sym_flags); | ||
1452 | } | 1520 | } |
1453 | 1521 | ||
1522 | if (mm) | ||
1523 | mmput(mm); | ||
1454 | return ret; | 1524 | return ret; |
1455 | } | 1525 | } |
1456 | 1526 | ||
@@ -1775,8 +1845,7 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | |||
1775 | trace_assign_type(field, entry); | 1845 | trace_assign_type(field, entry); |
1776 | 1846 | ||
1777 | seq_print_userip_objs(field, s, sym_flags); | 1847 | seq_print_userip_objs(field, s, sym_flags); |
1778 | if (entry->flags & TRACE_FLAG_CONT) | 1848 | trace_seq_putc(s, '\n'); |
1779 | trace_seq_print_cont(s, iter); | ||
1780 | break; | 1849 | break; |
1781 | } | 1850 | } |
1782 | default: | 1851 | default: |
@@ -3581,6 +3650,9 @@ void ftrace_dump(void) | |||
3581 | atomic_inc(&global_trace.data[cpu]->disabled); | 3650 | atomic_inc(&global_trace.data[cpu]->disabled); |
3582 | } | 3651 | } |
3583 | 3652 | ||
3653 | /* don't look at user memory in panic mode */ | ||
3654 | trace_flags &= ~TRACE_ITER_SYM_USEROBJ; | ||
3655 | |||
3584 | printk(KERN_TRACE "Dumping ftrace buffer:\n"); | 3656 | printk(KERN_TRACE "Dumping ftrace buffer:\n"); |
3585 | 3657 | ||
3586 | iter.tr = &global_trace; | 3658 | iter.tr = &global_trace; |