diff options
Diffstat (limited to 'kernel/trace/trace.c')
-rw-r--r-- | kernel/trace/trace.c | 275 |
1 files changed, 1 insertions, 274 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index fca0233f1d73..90ce0c1d437b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/irqflags.h> | 38 | #include <linux/irqflags.h> |
39 | 39 | ||
40 | #include "trace.h" | 40 | #include "trace.h" |
41 | #include "trace_output.h" | ||
41 | 42 | ||
42 | #define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE) | 43 | #define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE) |
43 | 44 | ||
@@ -330,132 +331,6 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) | |||
330 | tracing_record_cmdline(current); | 331 | tracing_record_cmdline(current); |
331 | } | 332 | } |
332 | 333 | ||
333 | /** | ||
334 | * trace_seq_printf - sequence printing of trace information | ||
335 | * @s: trace sequence descriptor | ||
336 | * @fmt: printf format string | ||
337 | * | ||
338 | * The tracer may use either sequence operations or its own | ||
339 | * copy to user routines. To simplify formating of a trace | ||
340 | * trace_seq_printf is used to store strings into a special | ||
341 | * buffer (@s). Then the output may be either used by | ||
342 | * the sequencer or pulled into another buffer. | ||
343 | */ | ||
344 | int | ||
345 | trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | ||
346 | { | ||
347 | int len = (PAGE_SIZE - 1) - s->len; | ||
348 | va_list ap; | ||
349 | int ret; | ||
350 | |||
351 | if (!len) | ||
352 | return 0; | ||
353 | |||
354 | va_start(ap, fmt); | ||
355 | ret = vsnprintf(s->buffer + s->len, len, fmt, ap); | ||
356 | va_end(ap); | ||
357 | |||
358 | /* If we can't write it all, don't bother writing anything */ | ||
359 | if (ret >= len) | ||
360 | return 0; | ||
361 | |||
362 | s->len += ret; | ||
363 | |||
364 | return len; | ||
365 | } | ||
366 | |||
367 | /** | ||
368 | * trace_seq_puts - trace sequence printing of simple string | ||
369 | * @s: trace sequence descriptor | ||
370 | * @str: simple string to record | ||
371 | * | ||
372 | * The tracer may use either the sequence operations or its own | ||
373 | * copy to user routines. This function records a simple string | ||
374 | * into a special buffer (@s) for later retrieval by a sequencer | ||
375 | * or other mechanism. | ||
376 | */ | ||
377 | static int | ||
378 | trace_seq_puts(struct trace_seq *s, const char *str) | ||
379 | { | ||
380 | int len = strlen(str); | ||
381 | |||
382 | if (len > ((PAGE_SIZE - 1) - s->len)) | ||
383 | return 0; | ||
384 | |||
385 | memcpy(s->buffer + s->len, str, len); | ||
386 | s->len += len; | ||
387 | |||
388 | return len; | ||
389 | } | ||
390 | |||
391 | static int | ||
392 | trace_seq_putc(struct trace_seq *s, unsigned char c) | ||
393 | { | ||
394 | if (s->len >= (PAGE_SIZE - 1)) | ||
395 | return 0; | ||
396 | |||
397 | s->buffer[s->len++] = c; | ||
398 | |||
399 | return 1; | ||
400 | } | ||
401 | |||
402 | static int | ||
403 | trace_seq_putmem(struct trace_seq *s, void *mem, size_t len) | ||
404 | { | ||
405 | if (len > ((PAGE_SIZE - 1) - s->len)) | ||
406 | return 0; | ||
407 | |||
408 | memcpy(s->buffer + s->len, mem, len); | ||
409 | s->len += len; | ||
410 | |||
411 | return len; | ||
412 | } | ||
413 | |||
414 | #define MAX_MEMHEX_BYTES 8 | ||
415 | #define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) | ||
416 | |||
417 | static int | ||
418 | trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len) | ||
419 | { | ||
420 | unsigned char hex[HEX_CHARS]; | ||
421 | unsigned char *data = mem; | ||
422 | int i, j; | ||
423 | |||
424 | #ifdef __BIG_ENDIAN | ||
425 | for (i = 0, j = 0; i < len; i++) { | ||
426 | #else | ||
427 | for (i = len-1, j = 0; i >= 0; i--) { | ||
428 | #endif | ||
429 | hex[j++] = hex_asc_hi(data[i]); | ||
430 | hex[j++] = hex_asc_lo(data[i]); | ||
431 | } | ||
432 | hex[j++] = ' '; | ||
433 | |||
434 | return trace_seq_putmem(s, hex, j); | ||
435 | } | ||
436 | |||
437 | static int | ||
438 | trace_seq_path(struct trace_seq *s, struct path *path) | ||
439 | { | ||
440 | unsigned char *p; | ||
441 | |||
442 | if (s->len >= (PAGE_SIZE - 1)) | ||
443 | return 0; | ||
444 | p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); | ||
445 | if (!IS_ERR(p)) { | ||
446 | p = mangle_path(s->buffer + s->len, p, "\n"); | ||
447 | if (p) { | ||
448 | s->len = p - s->buffer; | ||
449 | return 1; | ||
450 | } | ||
451 | } else { | ||
452 | s->buffer[s->len++] = '?'; | ||
453 | return 1; | ||
454 | } | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | static void | 334 | static void |
460 | trace_seq_reset(struct trace_seq *s) | 335 | trace_seq_reset(struct trace_seq *s) |
461 | { | 336 | { |
@@ -1473,154 +1348,6 @@ static void s_stop(struct seq_file *m, void *p) | |||
1473 | mutex_unlock(&trace_types_lock); | 1348 | mutex_unlock(&trace_types_lock); |
1474 | } | 1349 | } |
1475 | 1350 | ||
1476 | #ifdef CONFIG_KRETPROBES | ||
1477 | static inline const char *kretprobed(const char *name) | ||
1478 | { | ||
1479 | static const char tramp_name[] = "kretprobe_trampoline"; | ||
1480 | int size = sizeof(tramp_name); | ||
1481 | |||
1482 | if (strncmp(tramp_name, name, size) == 0) | ||
1483 | return "[unknown/kretprobe'd]"; | ||
1484 | return name; | ||
1485 | } | ||
1486 | #else | ||
1487 | static inline const char *kretprobed(const char *name) | ||
1488 | { | ||
1489 | return name; | ||
1490 | } | ||
1491 | #endif /* CONFIG_KRETPROBES */ | ||
1492 | |||
1493 | static int | ||
1494 | seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) | ||
1495 | { | ||
1496 | #ifdef CONFIG_KALLSYMS | ||
1497 | char str[KSYM_SYMBOL_LEN]; | ||
1498 | const char *name; | ||
1499 | |||
1500 | kallsyms_lookup(address, NULL, NULL, NULL, str); | ||
1501 | |||
1502 | name = kretprobed(str); | ||
1503 | |||
1504 | return trace_seq_printf(s, fmt, name); | ||
1505 | #endif | ||
1506 | return 1; | ||
1507 | } | ||
1508 | |||
1509 | static int | ||
1510 | seq_print_sym_offset(struct trace_seq *s, const char *fmt, | ||
1511 | unsigned long address) | ||
1512 | { | ||
1513 | #ifdef CONFIG_KALLSYMS | ||
1514 | char str[KSYM_SYMBOL_LEN]; | ||
1515 | const char *name; | ||
1516 | |||
1517 | sprint_symbol(str, address); | ||
1518 | name = kretprobed(str); | ||
1519 | |||
1520 | return trace_seq_printf(s, fmt, name); | ||
1521 | #endif | ||
1522 | return 1; | ||
1523 | } | ||
1524 | |||
1525 | #ifndef CONFIG_64BIT | ||
1526 | # define IP_FMT "%08lx" | ||
1527 | #else | ||
1528 | # define IP_FMT "%016lx" | ||
1529 | #endif | ||
1530 | |||
1531 | int | ||
1532 | seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags) | ||
1533 | { | ||
1534 | int ret; | ||
1535 | |||
1536 | if (!ip) | ||
1537 | return trace_seq_printf(s, "0"); | ||
1538 | |||
1539 | if (sym_flags & TRACE_ITER_SYM_OFFSET) | ||
1540 | ret = seq_print_sym_offset(s, "%s", ip); | ||
1541 | else | ||
1542 | ret = seq_print_sym_short(s, "%s", ip); | ||
1543 | |||
1544 | if (!ret) | ||
1545 | return 0; | ||
1546 | |||
1547 | if (sym_flags & TRACE_ITER_SYM_ADDR) | ||
1548 | ret = trace_seq_printf(s, " <" IP_FMT ">", ip); | ||
1549 | return ret; | ||
1550 | } | ||
1551 | |||
1552 | static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm, | ||
1553 | unsigned long ip, unsigned long sym_flags) | ||
1554 | { | ||
1555 | struct file *file = NULL; | ||
1556 | unsigned long vmstart = 0; | ||
1557 | int ret = 1; | ||
1558 | |||
1559 | if (mm) { | ||
1560 | const struct vm_area_struct *vma; | ||
1561 | |||
1562 | down_read(&mm->mmap_sem); | ||
1563 | vma = find_vma(mm, ip); | ||
1564 | if (vma) { | ||
1565 | file = vma->vm_file; | ||
1566 | vmstart = vma->vm_start; | ||
1567 | } | ||
1568 | if (file) { | ||
1569 | ret = trace_seq_path(s, &file->f_path); | ||
1570 | if (ret) | ||
1571 | ret = trace_seq_printf(s, "[+0x%lx]", ip - vmstart); | ||
1572 | } | ||
1573 | up_read(&mm->mmap_sem); | ||
1574 | } | ||
1575 | if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file)) | ||
1576 | ret = trace_seq_printf(s, " <" IP_FMT ">", ip); | ||
1577 | return ret; | ||
1578 | } | ||
1579 | |||
1580 | static int | ||
1581 | seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, | ||
1582 | unsigned long sym_flags) | ||
1583 | { | ||
1584 | struct mm_struct *mm = NULL; | ||
1585 | int ret = 1; | ||
1586 | unsigned int i; | ||
1587 | |||
1588 | if (trace_flags & TRACE_ITER_SYM_USEROBJ) { | ||
1589 | struct task_struct *task; | ||
1590 | /* | ||
1591 | * we do the lookup on the thread group leader, | ||
1592 | * since individual threads might have already quit! | ||
1593 | */ | ||
1594 | rcu_read_lock(); | ||
1595 | task = find_task_by_vpid(entry->ent.tgid); | ||
1596 | if (task) | ||
1597 | mm = get_task_mm(task); | ||
1598 | rcu_read_unlock(); | ||
1599 | } | ||
1600 | |||
1601 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { | ||
1602 | unsigned long ip = entry->caller[i]; | ||
1603 | |||
1604 | if (ip == ULONG_MAX || !ret) | ||
1605 | break; | ||
1606 | if (i && ret) | ||
1607 | ret = trace_seq_puts(s, " <- "); | ||
1608 | if (!ip) { | ||
1609 | if (ret) | ||
1610 | ret = trace_seq_puts(s, "??"); | ||
1611 | continue; | ||
1612 | } | ||
1613 | if (!ret) | ||
1614 | break; | ||
1615 | if (ret) | ||
1616 | ret = seq_print_user_ip(s, mm, ip, sym_flags); | ||
1617 | } | ||
1618 | |||
1619 | if (mm) | ||
1620 | mmput(mm); | ||
1621 | return ret; | ||
1622 | } | ||
1623 | |||
1624 | static void print_lat_help_header(struct seq_file *m) | 1351 | static void print_lat_help_header(struct seq_file *m) |
1625 | { | 1352 | { |
1626 | seq_puts(m, "# _------=> CPU# \n"); | 1353 | seq_puts(m, "# _------=> CPU# \n"); |