diff options
-rw-r--r-- | Documentation/ftrace.txt | 13 | ||||
-rw-r--r-- | kernel/trace/trace.c | 86 | ||||
-rw-r--r-- | kernel/trace/trace.h | 3 |
3 files changed, 93 insertions, 9 deletions
diff --git a/Documentation/ftrace.txt b/Documentation/ftrace.txt index 79a80f79c062..35a78bc6651d 100644 --- a/Documentation/ftrace.txt +++ b/Documentation/ftrace.txt | |||
@@ -324,7 +324,7 @@ output. To see what is available, simply cat the file: | |||
324 | 324 | ||
325 | cat /debug/tracing/trace_options | 325 | cat /debug/tracing/trace_options |
326 | print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \ | 326 | print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \ |
327 | noblock nostacktrace nosched-tree nouserstacktrace | 327 | noblock nostacktrace nosched-tree nouserstacktrace nosym-userobj |
328 | 328 | ||
329 | To disable one of the options, echo in the option prepended with "no". | 329 | To disable one of the options, echo in the option prepended with "no". |
330 | 330 | ||
@@ -381,6 +381,17 @@ Here are the available options: | |||
381 | userstacktrace - This option changes the trace. | 381 | userstacktrace - This option changes the trace. |
382 | It records a stacktrace of the current userspace thread. | 382 | It records a stacktrace of the current userspace thread. |
383 | 383 | ||
384 | sym-userobj - when user stacktrace are enabled, look up which object the | ||
385 | address belongs to, and print a relative address | ||
386 | This is especially useful when ASLR is on, otherwise you don't | ||
387 | get a chance to resolve the address to object/file/line after the app is no | ||
388 | longer running | ||
389 | |||
390 | The lookup is performed when you read trace,trace_pipe,latency_trace. Example: | ||
391 | |||
392 | a.out-1623 [000] 40874.465068: /root/a.out[+0x480] <-/root/a.out[+0 | ||
393 | x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6] | ||
394 | |||
384 | sched-tree - TBD (any users??) | 395 | sched-tree - TBD (any users??) |
385 | 396 | ||
386 | 397 | ||
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; |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 17bb4c830b01..28c15c2ebc22 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -508,7 +508,8 @@ enum trace_iterator_flags { | |||
508 | TRACE_ITER_PREEMPTONLY = 0x800, | 508 | TRACE_ITER_PREEMPTONLY = 0x800, |
509 | TRACE_ITER_BRANCH = 0x1000, | 509 | TRACE_ITER_BRANCH = 0x1000, |
510 | TRACE_ITER_ANNOTATE = 0x2000, | 510 | TRACE_ITER_ANNOTATE = 0x2000, |
511 | TRACE_ITER_USERSTACKTRACE = 0x4000 | 511 | TRACE_ITER_USERSTACKTRACE = 0x4000, |
512 | TRACE_ITER_SYM_USEROBJ = 0x8000 | ||
512 | }; | 513 | }; |
513 | 514 | ||
514 | /* | 515 | /* |