diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2016-09-23 10:38:39 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-09-29 10:17:02 -0400 |
commit | 1b36c03e356936d62abbe2accaae1573d3b66f14 (patch) | |
tree | 6eb7cc8ca62751aba4661d07823a6cea3cb912e7 | |
parent | cd67f99fe90dcf515f1c70c474b84d56b6236cbb (diff) |
perf record: Add support for using symbols in address filters
Symbols come from either the DSO or /proc/kallsyms for the kernel.
Details of the functionality can be found in Documentation/perf-record.txt.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: http://lkml.kernel.org/r/1474641528-18776-8-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/Documentation/perf-record.txt | 55 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 14 | ||||
-rw-r--r-- | tools/perf/util/auxtrace.c | 737 | ||||
-rw-r--r-- | tools/perf/util/auxtrace.h | 54 |
4 files changed, 857 insertions, 3 deletions
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index babbb63e6d9d..92335193dc33 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -89,9 +89,62 @@ OPTIONS | |||
89 | 89 | ||
90 | --filter=<filter>:: | 90 | --filter=<filter>:: |
91 | Event filter. This option should follow a event selector (-e) which | 91 | Event filter. This option should follow a event selector (-e) which |
92 | selects tracepoint event(s). Multiple '--filter' options are combined | 92 | selects either tracepoint event(s) or a hardware trace PMU |
93 | (e.g. Intel PT or CoreSight). | ||
94 | |||
95 | - tracepoint filters | ||
96 | |||
97 | In the case of tracepoints, multiple '--filter' options are combined | ||
93 | using '&&'. | 98 | using '&&'. |
94 | 99 | ||
100 | - address filters | ||
101 | |||
102 | A hardware trace PMU advertises its ability to accept a number of | ||
103 | address filters by specifying a non-zero value in | ||
104 | /sys/bus/event_source/devices/<pmu>/nr_addr_filters. | ||
105 | |||
106 | Address filters have the format: | ||
107 | |||
108 | filter|start|stop|tracestop <start> [/ <size>] [@<file name>] | ||
109 | |||
110 | Where: | ||
111 | - 'filter': defines a region that will be traced. | ||
112 | - 'start': defines an address at which tracing will begin. | ||
113 | - 'stop': defines an address at which tracing will stop. | ||
114 | - 'tracestop': defines a region in which tracing will stop. | ||
115 | |||
116 | <file name> is the name of the object file, <start> is the offset to the | ||
117 | code to trace in that file, and <size> is the size of the region to | ||
118 | trace. 'start' and 'stop' filters need not specify a <size>. | ||
119 | |||
120 | If no object file is specified then the kernel is assumed, in which case | ||
121 | the start address must be a current kernel memory address. | ||
122 | |||
123 | <start> can also be specified by providing the name of a symbol. If the | ||
124 | symbol name is not unique, it can be disambiguated by inserting #n where | ||
125 | 'n' selects the n'th symbol in address order. Alternately #0, #g or #G | ||
126 | select only a global symbol. <size> can also be specified by providing | ||
127 | the name of a symbol, in which case the size is calculated to the end | ||
128 | of that symbol. For 'filter' and 'tracestop' filters, if <size> is | ||
129 | omitted and <start> is a symbol, then the size is calculated to the end | ||
130 | of that symbol. | ||
131 | |||
132 | If <size> is omitted and <start> is '*', then the start and size will | ||
133 | be calculated from the first and last symbols, i.e. to trace the whole | ||
134 | file. | ||
135 | |||
136 | If symbol names (or '*') are provided, they must be surrounded by white | ||
137 | space. | ||
138 | |||
139 | The filter passed to the kernel is not necessarily the same as entered. | ||
140 | To see the filter that is passed, use the -v option. | ||
141 | |||
142 | The kernel may not be able to configure a trace region if it is not | ||
143 | within a single mapping. MMAP events (or /proc/<pid>/maps) can be | ||
144 | examined to determine if that is a possibility. | ||
145 | |||
146 | Multiple filters can be separated with space or comma. | ||
147 | |||
95 | --exclude-perf:: | 148 | --exclude-perf:: |
96 | Don't record events issued by perf itself. This option should follow | 149 | Don't record events issued by perf itself. This option should follow |
97 | a event selector (-e) which selects tracepoint event(s). It adds a | 150 | a event selector (-e) which selects tracepoint event(s). It adds a |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 962adcfc43a5..67d2a9003294 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -1581,6 +1581,18 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1581 | if (err) | 1581 | if (err) |
1582 | goto out; | 1582 | goto out; |
1583 | 1583 | ||
1584 | /* | ||
1585 | * Allow aliases to facilitate the lookup of symbols for address | ||
1586 | * filters. Refer to auxtrace_parse_filters(). | ||
1587 | */ | ||
1588 | symbol_conf.allow_aliases = true; | ||
1589 | |||
1590 | symbol__init(NULL); | ||
1591 | |||
1592 | err = auxtrace_parse_filters(rec->evlist); | ||
1593 | if (err) | ||
1594 | goto out; | ||
1595 | |||
1584 | if (dry_run) | 1596 | if (dry_run) |
1585 | goto out; | 1597 | goto out; |
1586 | 1598 | ||
@@ -1594,8 +1606,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1594 | 1606 | ||
1595 | err = -ENOMEM; | 1607 | err = -ENOMEM; |
1596 | 1608 | ||
1597 | symbol__init(NULL); | ||
1598 | |||
1599 | if (symbol_conf.kptr_restrict) | 1609 | if (symbol_conf.kptr_restrict) |
1600 | pr_warning( | 1610 | pr_warning( |
1601 | "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" | 1611 | "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" |
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index c0aba8e839aa..c5a6e0b12452 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c | |||
@@ -16,6 +16,10 @@ | |||
16 | #include <sys/types.h> | 16 | #include <sys/types.h> |
17 | #include <sys/mman.h> | 17 | #include <sys/mman.h> |
18 | #include <stdbool.h> | 18 | #include <stdbool.h> |
19 | #include <ctype.h> | ||
20 | #include <string.h> | ||
21 | #include <limits.h> | ||
22 | #include <errno.h> | ||
19 | 23 | ||
20 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
21 | #include <linux/perf_event.h> | 25 | #include <linux/perf_event.h> |
@@ -35,9 +39,14 @@ | |||
35 | #include "../perf.h" | 39 | #include "../perf.h" |
36 | #include "util.h" | 40 | #include "util.h" |
37 | #include "evlist.h" | 41 | #include "evlist.h" |
42 | #include "dso.h" | ||
43 | #include "map.h" | ||
44 | #include "pmu.h" | ||
45 | #include "evsel.h" | ||
38 | #include "cpumap.h" | 46 | #include "cpumap.h" |
39 | #include "thread_map.h" | 47 | #include "thread_map.h" |
40 | #include "asm/bug.h" | 48 | #include "asm/bug.h" |
49 | #include "symbol/kallsyms.h" | ||
41 | #include "auxtrace.h" | 50 | #include "auxtrace.h" |
42 | 51 | ||
43 | #include <linux/hash.h> | 52 | #include <linux/hash.h> |
@@ -1399,3 +1408,731 @@ void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key) | |||
1399 | 1408 | ||
1400 | return NULL; | 1409 | return NULL; |
1401 | } | 1410 | } |
1411 | |||
1412 | static void addr_filter__free_str(struct addr_filter *filt) | ||
1413 | { | ||
1414 | free(filt->str); | ||
1415 | filt->action = NULL; | ||
1416 | filt->sym_from = NULL; | ||
1417 | filt->sym_to = NULL; | ||
1418 | filt->filename = NULL; | ||
1419 | filt->str = NULL; | ||
1420 | } | ||
1421 | |||
1422 | static struct addr_filter *addr_filter__new(void) | ||
1423 | { | ||
1424 | struct addr_filter *filt = zalloc(sizeof(*filt)); | ||
1425 | |||
1426 | if (filt) | ||
1427 | INIT_LIST_HEAD(&filt->list); | ||
1428 | |||
1429 | return filt; | ||
1430 | } | ||
1431 | |||
1432 | static void addr_filter__free(struct addr_filter *filt) | ||
1433 | { | ||
1434 | if (filt) | ||
1435 | addr_filter__free_str(filt); | ||
1436 | free(filt); | ||
1437 | } | ||
1438 | |||
1439 | static void addr_filters__add(struct addr_filters *filts, | ||
1440 | struct addr_filter *filt) | ||
1441 | { | ||
1442 | list_add_tail(&filt->list, &filts->head); | ||
1443 | filts->cnt += 1; | ||
1444 | } | ||
1445 | |||
1446 | static void addr_filters__del(struct addr_filters *filts, | ||
1447 | struct addr_filter *filt) | ||
1448 | { | ||
1449 | list_del_init(&filt->list); | ||
1450 | filts->cnt -= 1; | ||
1451 | } | ||
1452 | |||
1453 | void addr_filters__init(struct addr_filters *filts) | ||
1454 | { | ||
1455 | INIT_LIST_HEAD(&filts->head); | ||
1456 | filts->cnt = 0; | ||
1457 | } | ||
1458 | |||
1459 | void addr_filters__exit(struct addr_filters *filts) | ||
1460 | { | ||
1461 | struct addr_filter *filt, *n; | ||
1462 | |||
1463 | list_for_each_entry_safe(filt, n, &filts->head, list) { | ||
1464 | addr_filters__del(filts, filt); | ||
1465 | addr_filter__free(filt); | ||
1466 | } | ||
1467 | } | ||
1468 | |||
1469 | static int parse_num_or_str(char **inp, u64 *num, const char **str, | ||
1470 | const char *str_delim) | ||
1471 | { | ||
1472 | *inp += strspn(*inp, " "); | ||
1473 | |||
1474 | if (isdigit(**inp)) { | ||
1475 | char *endptr; | ||
1476 | |||
1477 | if (!num) | ||
1478 | return -EINVAL; | ||
1479 | errno = 0; | ||
1480 | *num = strtoull(*inp, &endptr, 0); | ||
1481 | if (errno) | ||
1482 | return -errno; | ||
1483 | if (endptr == *inp) | ||
1484 | return -EINVAL; | ||
1485 | *inp = endptr; | ||
1486 | } else { | ||
1487 | size_t n; | ||
1488 | |||
1489 | if (!str) | ||
1490 | return -EINVAL; | ||
1491 | *inp += strspn(*inp, " "); | ||
1492 | *str = *inp; | ||
1493 | n = strcspn(*inp, str_delim); | ||
1494 | if (!n) | ||
1495 | return -EINVAL; | ||
1496 | *inp += n; | ||
1497 | if (**inp) { | ||
1498 | **inp = '\0'; | ||
1499 | *inp += 1; | ||
1500 | } | ||
1501 | } | ||
1502 | return 0; | ||
1503 | } | ||
1504 | |||
1505 | static int parse_action(struct addr_filter *filt) | ||
1506 | { | ||
1507 | if (!strcmp(filt->action, "filter")) { | ||
1508 | filt->start = true; | ||
1509 | filt->range = true; | ||
1510 | } else if (!strcmp(filt->action, "start")) { | ||
1511 | filt->start = true; | ||
1512 | } else if (!strcmp(filt->action, "stop")) { | ||
1513 | filt->start = false; | ||
1514 | } else if (!strcmp(filt->action, "tracestop")) { | ||
1515 | filt->start = false; | ||
1516 | filt->range = true; | ||
1517 | filt->action += 5; /* Change 'tracestop' to 'stop' */ | ||
1518 | } else { | ||
1519 | return -EINVAL; | ||
1520 | } | ||
1521 | return 0; | ||
1522 | } | ||
1523 | |||
1524 | static int parse_sym_idx(char **inp, int *idx) | ||
1525 | { | ||
1526 | *idx = -1; | ||
1527 | |||
1528 | *inp += strspn(*inp, " "); | ||
1529 | |||
1530 | if (**inp != '#') | ||
1531 | return 0; | ||
1532 | |||
1533 | *inp += 1; | ||
1534 | |||
1535 | if (**inp == 'g' || **inp == 'G') { | ||
1536 | *inp += 1; | ||
1537 | *idx = 0; | ||
1538 | } else { | ||
1539 | unsigned long num; | ||
1540 | char *endptr; | ||
1541 | |||
1542 | errno = 0; | ||
1543 | num = strtoul(*inp, &endptr, 0); | ||
1544 | if (errno) | ||
1545 | return -errno; | ||
1546 | if (endptr == *inp || num > INT_MAX) | ||
1547 | return -EINVAL; | ||
1548 | *inp = endptr; | ||
1549 | *idx = num; | ||
1550 | } | ||
1551 | |||
1552 | return 0; | ||
1553 | } | ||
1554 | |||
1555 | static int parse_addr_size(char **inp, u64 *num, const char **str, int *idx) | ||
1556 | { | ||
1557 | int err = parse_num_or_str(inp, num, str, " "); | ||
1558 | |||
1559 | if (!err && *str) | ||
1560 | err = parse_sym_idx(inp, idx); | ||
1561 | |||
1562 | return err; | ||
1563 | } | ||
1564 | |||
1565 | static int parse_one_filter(struct addr_filter *filt, const char **filter_inp) | ||
1566 | { | ||
1567 | char *fstr; | ||
1568 | int err; | ||
1569 | |||
1570 | filt->str = fstr = strdup(*filter_inp); | ||
1571 | if (!fstr) | ||
1572 | return -ENOMEM; | ||
1573 | |||
1574 | err = parse_num_or_str(&fstr, NULL, &filt->action, " "); | ||
1575 | if (err) | ||
1576 | goto out_err; | ||
1577 | |||
1578 | err = parse_action(filt); | ||
1579 | if (err) | ||
1580 | goto out_err; | ||
1581 | |||
1582 | err = parse_addr_size(&fstr, &filt->addr, &filt->sym_from, | ||
1583 | &filt->sym_from_idx); | ||
1584 | if (err) | ||
1585 | goto out_err; | ||
1586 | |||
1587 | fstr += strspn(fstr, " "); | ||
1588 | |||
1589 | if (*fstr == '/') { | ||
1590 | fstr += 1; | ||
1591 | err = parse_addr_size(&fstr, &filt->size, &filt->sym_to, | ||
1592 | &filt->sym_to_idx); | ||
1593 | if (err) | ||
1594 | goto out_err; | ||
1595 | filt->range = true; | ||
1596 | } | ||
1597 | |||
1598 | fstr += strspn(fstr, " "); | ||
1599 | |||
1600 | if (*fstr == '@') { | ||
1601 | fstr += 1; | ||
1602 | err = parse_num_or_str(&fstr, NULL, &filt->filename, " ,"); | ||
1603 | if (err) | ||
1604 | goto out_err; | ||
1605 | } | ||
1606 | |||
1607 | fstr += strspn(fstr, " ,"); | ||
1608 | |||
1609 | *filter_inp += fstr - filt->str; | ||
1610 | |||
1611 | return 0; | ||
1612 | |||
1613 | out_err: | ||
1614 | addr_filter__free_str(filt); | ||
1615 | |||
1616 | return err; | ||
1617 | } | ||
1618 | |||
1619 | int addr_filters__parse_bare_filter(struct addr_filters *filts, | ||
1620 | const char *filter) | ||
1621 | { | ||
1622 | struct addr_filter *filt; | ||
1623 | const char *fstr = filter; | ||
1624 | int err; | ||
1625 | |||
1626 | while (*fstr) { | ||
1627 | filt = addr_filter__new(); | ||
1628 | err = parse_one_filter(filt, &fstr); | ||
1629 | if (err) { | ||
1630 | addr_filter__free(filt); | ||
1631 | addr_filters__exit(filts); | ||
1632 | return err; | ||
1633 | } | ||
1634 | addr_filters__add(filts, filt); | ||
1635 | } | ||
1636 | |||
1637 | return 0; | ||
1638 | } | ||
1639 | |||
1640 | struct sym_args { | ||
1641 | const char *name; | ||
1642 | u64 start; | ||
1643 | u64 size; | ||
1644 | int idx; | ||
1645 | int cnt; | ||
1646 | bool started; | ||
1647 | bool global; | ||
1648 | bool selected; | ||
1649 | bool duplicate; | ||
1650 | bool near; | ||
1651 | }; | ||
1652 | |||
1653 | static bool kern_sym_match(struct sym_args *args, const char *name, char type) | ||
1654 | { | ||
1655 | /* A function with the same name, and global or the n'th found or any */ | ||
1656 | return symbol_type__is_a(type, MAP__FUNCTION) && | ||
1657 | !strcmp(name, args->name) && | ||
1658 | ((args->global && isupper(type)) || | ||
1659 | (args->selected && ++(args->cnt) == args->idx) || | ||
1660 | (!args->global && !args->selected)); | ||
1661 | } | ||
1662 | |||
1663 | static int find_kern_sym_cb(void *arg, const char *name, char type, u64 start) | ||
1664 | { | ||
1665 | struct sym_args *args = arg; | ||
1666 | |||
1667 | if (args->started) { | ||
1668 | if (!args->size) | ||
1669 | args->size = start - args->start; | ||
1670 | if (args->selected) { | ||
1671 | if (args->size) | ||
1672 | return 1; | ||
1673 | } else if (kern_sym_match(args, name, type)) { | ||
1674 | args->duplicate = true; | ||
1675 | return 1; | ||
1676 | } | ||
1677 | } else if (kern_sym_match(args, name, type)) { | ||
1678 | args->started = true; | ||
1679 | args->start = start; | ||
1680 | } | ||
1681 | |||
1682 | return 0; | ||
1683 | } | ||
1684 | |||
1685 | static int print_kern_sym_cb(void *arg, const char *name, char type, u64 start) | ||
1686 | { | ||
1687 | struct sym_args *args = arg; | ||
1688 | |||
1689 | if (kern_sym_match(args, name, type)) { | ||
1690 | pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", | ||
1691 | ++args->cnt, start, type, name); | ||
1692 | args->near = true; | ||
1693 | } else if (args->near) { | ||
1694 | args->near = false; | ||
1695 | pr_err("\t\twhich is near\t\t%s\n", name); | ||
1696 | } | ||
1697 | |||
1698 | return 0; | ||
1699 | } | ||
1700 | |||
1701 | static int sym_not_found_error(const char *sym_name, int idx) | ||
1702 | { | ||
1703 | if (idx > 0) { | ||
1704 | pr_err("N'th occurrence (N=%d) of symbol '%s' not found.\n", | ||
1705 | idx, sym_name); | ||
1706 | } else if (!idx) { | ||
1707 | pr_err("Global symbol '%s' not found.\n", sym_name); | ||
1708 | } else { | ||
1709 | pr_err("Symbol '%s' not found.\n", sym_name); | ||
1710 | } | ||
1711 | pr_err("Note that symbols must be functions.\n"); | ||
1712 | |||
1713 | return -EINVAL; | ||
1714 | } | ||
1715 | |||
1716 | static int find_kern_sym(const char *sym_name, u64 *start, u64 *size, int idx) | ||
1717 | { | ||
1718 | struct sym_args args = { | ||
1719 | .name = sym_name, | ||
1720 | .idx = idx, | ||
1721 | .global = !idx, | ||
1722 | .selected = idx > 0, | ||
1723 | }; | ||
1724 | int err; | ||
1725 | |||
1726 | *start = 0; | ||
1727 | *size = 0; | ||
1728 | |||
1729 | err = kallsyms__parse("/proc/kallsyms", &args, find_kern_sym_cb); | ||
1730 | if (err < 0) { | ||
1731 | pr_err("Failed to parse /proc/kallsyms\n"); | ||
1732 | return err; | ||
1733 | } | ||
1734 | |||
1735 | if (args.duplicate) { | ||
1736 | pr_err("Multiple kernel symbols with name '%s'\n", sym_name); | ||
1737 | args.cnt = 0; | ||
1738 | kallsyms__parse("/proc/kallsyms", &args, print_kern_sym_cb); | ||
1739 | pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n", | ||
1740 | sym_name); | ||
1741 | pr_err("Or select a global symbol by inserting #0 or #g or #G\n"); | ||
1742 | return -EINVAL; | ||
1743 | } | ||
1744 | |||
1745 | if (!args.started) { | ||
1746 | pr_err("Kernel symbol lookup: "); | ||
1747 | return sym_not_found_error(sym_name, idx); | ||
1748 | } | ||
1749 | |||
1750 | *start = args.start; | ||
1751 | *size = args.size; | ||
1752 | |||
1753 | return 0; | ||
1754 | } | ||
1755 | |||
1756 | static int find_entire_kern_cb(void *arg, const char *name __maybe_unused, | ||
1757 | char type, u64 start) | ||
1758 | { | ||
1759 | struct sym_args *args = arg; | ||
1760 | |||
1761 | if (!symbol_type__is_a(type, MAP__FUNCTION)) | ||
1762 | return 0; | ||
1763 | |||
1764 | if (!args->started) { | ||
1765 | args->started = true; | ||
1766 | args->start = start; | ||
1767 | } | ||
1768 | /* Don't know exactly where the kernel ends, so we add a page */ | ||
1769 | args->size = round_up(start, page_size) + page_size - args->start; | ||
1770 | |||
1771 | return 0; | ||
1772 | } | ||
1773 | |||
1774 | static int addr_filter__entire_kernel(struct addr_filter *filt) | ||
1775 | { | ||
1776 | struct sym_args args = { .started = false }; | ||
1777 | int err; | ||
1778 | |||
1779 | err = kallsyms__parse("/proc/kallsyms", &args, find_entire_kern_cb); | ||
1780 | if (err < 0 || !args.started) { | ||
1781 | pr_err("Failed to parse /proc/kallsyms\n"); | ||
1782 | return err; | ||
1783 | } | ||
1784 | |||
1785 | filt->addr = args.start; | ||
1786 | filt->size = args.size; | ||
1787 | |||
1788 | return 0; | ||
1789 | } | ||
1790 | |||
1791 | static int check_end_after_start(struct addr_filter *filt, u64 start, u64 size) | ||
1792 | { | ||
1793 | if (start + size >= filt->addr) | ||
1794 | return 0; | ||
1795 | |||
1796 | if (filt->sym_from) { | ||
1797 | pr_err("Symbol '%s' (0x%"PRIx64") comes before '%s' (0x%"PRIx64")\n", | ||
1798 | filt->sym_to, start, filt->sym_from, filt->addr); | ||
1799 | } else { | ||
1800 | pr_err("Symbol '%s' (0x%"PRIx64") comes before address 0x%"PRIx64")\n", | ||
1801 | filt->sym_to, start, filt->addr); | ||
1802 | } | ||
1803 | |||
1804 | return -EINVAL; | ||
1805 | } | ||
1806 | |||
1807 | static int addr_filter__resolve_kernel_syms(struct addr_filter *filt) | ||
1808 | { | ||
1809 | bool no_size = false; | ||
1810 | u64 start, size; | ||
1811 | int err; | ||
1812 | |||
1813 | if (symbol_conf.kptr_restrict) { | ||
1814 | pr_err("Kernel addresses are restricted. Unable to resolve kernel symbols.\n"); | ||
1815 | return -EINVAL; | ||
1816 | } | ||
1817 | |||
1818 | if (filt->sym_from && !strcmp(filt->sym_from, "*")) | ||
1819 | return addr_filter__entire_kernel(filt); | ||
1820 | |||
1821 | if (filt->sym_from) { | ||
1822 | err = find_kern_sym(filt->sym_from, &start, &size, | ||
1823 | filt->sym_from_idx); | ||
1824 | if (err) | ||
1825 | return err; | ||
1826 | filt->addr = start; | ||
1827 | if (filt->range && !filt->size && !filt->sym_to) { | ||
1828 | filt->size = size; | ||
1829 | no_size = !!size; | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | if (filt->sym_to) { | ||
1834 | err = find_kern_sym(filt->sym_to, &start, &size, | ||
1835 | filt->sym_to_idx); | ||
1836 | if (err) | ||
1837 | return err; | ||
1838 | |||
1839 | err = check_end_after_start(filt, start, size); | ||
1840 | if (err) | ||
1841 | return err; | ||
1842 | filt->size = start + size - filt->addr; | ||
1843 | no_size = !!size; | ||
1844 | } | ||
1845 | |||
1846 | /* The very last symbol in kallsyms does not imply a particular size */ | ||
1847 | if (no_size) { | ||
1848 | pr_err("Cannot determine size of symbol '%s'\n", | ||
1849 | filt->sym_to ? filt->sym_to : filt->sym_from); | ||
1850 | return -EINVAL; | ||
1851 | } | ||
1852 | |||
1853 | return 0; | ||
1854 | } | ||
1855 | |||
1856 | static struct dso *load_dso(const char *name) | ||
1857 | { | ||
1858 | struct map *map; | ||
1859 | struct dso *dso; | ||
1860 | |||
1861 | map = dso__new_map(name); | ||
1862 | if (!map) | ||
1863 | return NULL; | ||
1864 | |||
1865 | map__load(map); | ||
1866 | |||
1867 | dso = dso__get(map->dso); | ||
1868 | |||
1869 | map__put(map); | ||
1870 | |||
1871 | return dso; | ||
1872 | } | ||
1873 | |||
1874 | static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt, | ||
1875 | int idx) | ||
1876 | { | ||
1877 | /* Same name, and global or the n'th found or any */ | ||
1878 | return !arch__compare_symbol_names(name, sym->name) && | ||
1879 | ((!idx && sym->binding == STB_GLOBAL) || | ||
1880 | (idx > 0 && ++*cnt == idx) || | ||
1881 | idx < 0); | ||
1882 | } | ||
1883 | |||
1884 | static void print_duplicate_syms(struct dso *dso, const char *sym_name) | ||
1885 | { | ||
1886 | struct symbol *sym; | ||
1887 | bool near = false; | ||
1888 | int cnt = 0; | ||
1889 | |||
1890 | pr_err("Multiple symbols with name '%s'\n", sym_name); | ||
1891 | |||
1892 | sym = dso__first_symbol(dso, MAP__FUNCTION); | ||
1893 | while (sym) { | ||
1894 | if (dso_sym_match(sym, sym_name, &cnt, -1)) { | ||
1895 | pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", | ||
1896 | ++cnt, sym->start, | ||
1897 | sym->binding == STB_GLOBAL ? 'g' : | ||
1898 | sym->binding == STB_LOCAL ? 'l' : 'w', | ||
1899 | sym->name); | ||
1900 | near = true; | ||
1901 | } else if (near) { | ||
1902 | near = false; | ||
1903 | pr_err("\t\twhich is near\t\t%s\n", sym->name); | ||
1904 | } | ||
1905 | sym = dso__next_symbol(sym); | ||
1906 | } | ||
1907 | |||
1908 | pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n", | ||
1909 | sym_name); | ||
1910 | pr_err("Or select a global symbol by inserting #0 or #g or #G\n"); | ||
1911 | } | ||
1912 | |||
1913 | static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start, | ||
1914 | u64 *size, int idx) | ||
1915 | { | ||
1916 | struct symbol *sym; | ||
1917 | int cnt = 0; | ||
1918 | |||
1919 | *start = 0; | ||
1920 | *size = 0; | ||
1921 | |||
1922 | sym = dso__first_symbol(dso, MAP__FUNCTION); | ||
1923 | while (sym) { | ||
1924 | if (*start) { | ||
1925 | if (!*size) | ||
1926 | *size = sym->start - *start; | ||
1927 | if (idx > 0) { | ||
1928 | if (*size) | ||
1929 | return 1; | ||
1930 | } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { | ||
1931 | print_duplicate_syms(dso, sym_name); | ||
1932 | return -EINVAL; | ||
1933 | } | ||
1934 | } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { | ||
1935 | *start = sym->start; | ||
1936 | *size = sym->end - sym->start; | ||
1937 | } | ||
1938 | sym = dso__next_symbol(sym); | ||
1939 | } | ||
1940 | |||
1941 | if (!*start) | ||
1942 | return sym_not_found_error(sym_name, idx); | ||
1943 | |||
1944 | return 0; | ||
1945 | } | ||
1946 | |||
1947 | static int addr_filter__entire_dso(struct addr_filter *filt, struct dso *dso) | ||
1948 | { | ||
1949 | struct symbol *first_sym = dso__first_symbol(dso, MAP__FUNCTION); | ||
1950 | struct symbol *last_sym = dso__last_symbol(dso, MAP__FUNCTION); | ||
1951 | |||
1952 | if (!first_sym || !last_sym) { | ||
1953 | pr_err("Failed to determine filter for %s\nNo symbols found.\n", | ||
1954 | filt->filename); | ||
1955 | return -EINVAL; | ||
1956 | } | ||
1957 | |||
1958 | filt->addr = first_sym->start; | ||
1959 | filt->size = last_sym->end - first_sym->start; | ||
1960 | |||
1961 | return 0; | ||
1962 | } | ||
1963 | |||
1964 | static int addr_filter__resolve_syms(struct addr_filter *filt) | ||
1965 | { | ||
1966 | u64 start, size; | ||
1967 | struct dso *dso; | ||
1968 | int err = 0; | ||
1969 | |||
1970 | if (!filt->sym_from && !filt->sym_to) | ||
1971 | return 0; | ||
1972 | |||
1973 | if (!filt->filename) | ||
1974 | return addr_filter__resolve_kernel_syms(filt); | ||
1975 | |||
1976 | dso = load_dso(filt->filename); | ||
1977 | if (!dso) { | ||
1978 | pr_err("Failed to load symbols from: %s\n", filt->filename); | ||
1979 | return -EINVAL; | ||
1980 | } | ||
1981 | |||
1982 | if (filt->sym_from && !strcmp(filt->sym_from, "*")) { | ||
1983 | err = addr_filter__entire_dso(filt, dso); | ||
1984 | goto put_dso; | ||
1985 | } | ||
1986 | |||
1987 | if (filt->sym_from) { | ||
1988 | err = find_dso_sym(dso, filt->sym_from, &start, &size, | ||
1989 | filt->sym_from_idx); | ||
1990 | if (err) | ||
1991 | goto put_dso; | ||
1992 | filt->addr = start; | ||
1993 | if (filt->range && !filt->size && !filt->sym_to) | ||
1994 | filt->size = size; | ||
1995 | } | ||
1996 | |||
1997 | if (filt->sym_to) { | ||
1998 | err = find_dso_sym(dso, filt->sym_to, &start, &size, | ||
1999 | filt->sym_to_idx); | ||
2000 | if (err) | ||
2001 | goto put_dso; | ||
2002 | |||
2003 | err = check_end_after_start(filt, start, size); | ||
2004 | if (err) | ||
2005 | return err; | ||
2006 | |||
2007 | filt->size = start + size - filt->addr; | ||
2008 | } | ||
2009 | |||
2010 | put_dso: | ||
2011 | dso__put(dso); | ||
2012 | |||
2013 | return err; | ||
2014 | } | ||
2015 | |||
2016 | static char *addr_filter__to_str(struct addr_filter *filt) | ||
2017 | { | ||
2018 | char filename_buf[PATH_MAX]; | ||
2019 | const char *at = ""; | ||
2020 | const char *fn = ""; | ||
2021 | char *filter; | ||
2022 | int err; | ||
2023 | |||
2024 | if (filt->filename) { | ||
2025 | at = "@"; | ||
2026 | fn = realpath(filt->filename, filename_buf); | ||
2027 | if (!fn) | ||
2028 | return NULL; | ||
2029 | } | ||
2030 | |||
2031 | if (filt->range) { | ||
2032 | err = asprintf(&filter, "%s 0x%"PRIx64"/0x%"PRIx64"%s%s", | ||
2033 | filt->action, filt->addr, filt->size, at, fn); | ||
2034 | } else { | ||
2035 | err = asprintf(&filter, "%s 0x%"PRIx64"%s%s", | ||
2036 | filt->action, filt->addr, at, fn); | ||
2037 | } | ||
2038 | |||
2039 | return err < 0 ? NULL : filter; | ||
2040 | } | ||
2041 | |||
2042 | static int parse_addr_filter(struct perf_evsel *evsel, const char *filter, | ||
2043 | int max_nr) | ||
2044 | { | ||
2045 | struct addr_filters filts; | ||
2046 | struct addr_filter *filt; | ||
2047 | int err; | ||
2048 | |||
2049 | addr_filters__init(&filts); | ||
2050 | |||
2051 | err = addr_filters__parse_bare_filter(&filts, filter); | ||
2052 | if (err) | ||
2053 | goto out_exit; | ||
2054 | |||
2055 | if (filts.cnt > max_nr) { | ||
2056 | pr_err("Error: number of address filters (%d) exceeds maximum (%d)\n", | ||
2057 | filts.cnt, max_nr); | ||
2058 | err = -EINVAL; | ||
2059 | goto out_exit; | ||
2060 | } | ||
2061 | |||
2062 | list_for_each_entry(filt, &filts.head, list) { | ||
2063 | char *new_filter; | ||
2064 | |||
2065 | err = addr_filter__resolve_syms(filt); | ||
2066 | if (err) | ||
2067 | goto out_exit; | ||
2068 | |||
2069 | new_filter = addr_filter__to_str(filt); | ||
2070 | if (!new_filter) { | ||
2071 | err = -ENOMEM; | ||
2072 | goto out_exit; | ||
2073 | } | ||
2074 | |||
2075 | if (perf_evsel__append_addr_filter(evsel, new_filter)) { | ||
2076 | err = -ENOMEM; | ||
2077 | goto out_exit; | ||
2078 | } | ||
2079 | } | ||
2080 | |||
2081 | out_exit: | ||
2082 | addr_filters__exit(&filts); | ||
2083 | |||
2084 | if (err) { | ||
2085 | pr_err("Failed to parse address filter: '%s'\n", filter); | ||
2086 | pr_err("Filter format is: filter|start|stop|tracestop <start symbol or address> [/ <end symbol or size>] [@<file name>]\n"); | ||
2087 | pr_err("Where multiple filters are separated by space or comma.\n"); | ||
2088 | } | ||
2089 | |||
2090 | return err; | ||
2091 | } | ||
2092 | |||
2093 | static struct perf_pmu *perf_evsel__find_pmu(struct perf_evsel *evsel) | ||
2094 | { | ||
2095 | struct perf_pmu *pmu = NULL; | ||
2096 | |||
2097 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { | ||
2098 | if (pmu->type == evsel->attr.type) | ||
2099 | break; | ||
2100 | } | ||
2101 | |||
2102 | return pmu; | ||
2103 | } | ||
2104 | |||
2105 | static int perf_evsel__nr_addr_filter(struct perf_evsel *evsel) | ||
2106 | { | ||
2107 | struct perf_pmu *pmu = perf_evsel__find_pmu(evsel); | ||
2108 | int nr_addr_filters = 0; | ||
2109 | |||
2110 | if (!pmu) | ||
2111 | return 0; | ||
2112 | |||
2113 | perf_pmu__scan_file(pmu, "nr_addr_filters", "%d", &nr_addr_filters); | ||
2114 | |||
2115 | return nr_addr_filters; | ||
2116 | } | ||
2117 | |||
2118 | int auxtrace_parse_filters(struct perf_evlist *evlist) | ||
2119 | { | ||
2120 | struct perf_evsel *evsel; | ||
2121 | char *filter; | ||
2122 | int err, max_nr; | ||
2123 | |||
2124 | evlist__for_each_entry(evlist, evsel) { | ||
2125 | filter = evsel->filter; | ||
2126 | max_nr = perf_evsel__nr_addr_filter(evsel); | ||
2127 | if (!filter || !max_nr) | ||
2128 | continue; | ||
2129 | evsel->filter = NULL; | ||
2130 | err = parse_addr_filter(evsel, filter, max_nr); | ||
2131 | free(filter); | ||
2132 | if (err) | ||
2133 | return err; | ||
2134 | pr_debug("Address filter: %s\n", evsel->filter); | ||
2135 | } | ||
2136 | |||
2137 | return 0; | ||
2138 | } | ||
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 09286f193532..26fb1ee5746a 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h | |||
@@ -318,6 +318,48 @@ struct auxtrace_record { | |||
318 | unsigned int alignment; | 318 | unsigned int alignment; |
319 | }; | 319 | }; |
320 | 320 | ||
321 | /** | ||
322 | * struct addr_filter - address filter. | ||
323 | * @list: list node | ||
324 | * @range: true if it is a range filter | ||
325 | * @start: true if action is 'filter' or 'start' | ||
326 | * @action: 'filter', 'start' or 'stop' ('tracestop' is accepted but converted | ||
327 | * to 'stop') | ||
328 | * @sym_from: symbol name for the filter address | ||
329 | * @sym_to: symbol name that determines the filter size | ||
330 | * @sym_from_idx: selects n'th from symbols with the same name (0 means global | ||
331 | * and less than 0 means symbol must be unique) | ||
332 | * @sym_to_idx: same as @sym_from_idx but for @sym_to | ||
333 | * @addr: filter address | ||
334 | * @size: filter region size (for range filters) | ||
335 | * @filename: DSO file name or NULL for the kernel | ||
336 | * @str: allocated string that contains the other string members | ||
337 | */ | ||
338 | struct addr_filter { | ||
339 | struct list_head list; | ||
340 | bool range; | ||
341 | bool start; | ||
342 | const char *action; | ||
343 | const char *sym_from; | ||
344 | const char *sym_to; | ||
345 | int sym_from_idx; | ||
346 | int sym_to_idx; | ||
347 | u64 addr; | ||
348 | u64 size; | ||
349 | const char *filename; | ||
350 | char *str; | ||
351 | }; | ||
352 | |||
353 | /** | ||
354 | * struct addr_filters - list of address filters. | ||
355 | * @head: list of address filters | ||
356 | * @cnt: number of address filters | ||
357 | */ | ||
358 | struct addr_filters { | ||
359 | struct list_head head; | ||
360 | int cnt; | ||
361 | }; | ||
362 | |||
321 | #ifdef HAVE_AUXTRACE_SUPPORT | 363 | #ifdef HAVE_AUXTRACE_SUPPORT |
322 | 364 | ||
323 | /* | 365 | /* |
@@ -482,6 +524,12 @@ void perf_session__auxtrace_error_inc(struct perf_session *session, | |||
482 | union perf_event *event); | 524 | union perf_event *event); |
483 | void events_stats__auxtrace_error_warn(const struct events_stats *stats); | 525 | void events_stats__auxtrace_error_warn(const struct events_stats *stats); |
484 | 526 | ||
527 | void addr_filters__init(struct addr_filters *filts); | ||
528 | void addr_filters__exit(struct addr_filters *filts); | ||
529 | int addr_filters__parse_bare_filter(struct addr_filters *filts, | ||
530 | const char *filter); | ||
531 | int auxtrace_parse_filters(struct perf_evlist *evlist); | ||
532 | |||
485 | static inline int auxtrace__process_event(struct perf_session *session, | 533 | static inline int auxtrace__process_event(struct perf_session *session, |
486 | union perf_event *event, | 534 | union perf_event *event, |
487 | struct perf_sample *sample, | 535 | struct perf_sample *sample, |
@@ -640,6 +688,12 @@ void auxtrace_index__free(struct list_head *head __maybe_unused) | |||
640 | { | 688 | { |
641 | } | 689 | } |
642 | 690 | ||
691 | static inline | ||
692 | int auxtrace_parse_filters(struct perf_evlist *evlist __maybe_unused) | ||
693 | { | ||
694 | return 0; | ||
695 | } | ||
696 | |||
643 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, | 697 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, |
644 | struct auxtrace_mmap_params *mp, | 698 | struct auxtrace_mmap_params *mp, |
645 | void *userpg, int fd); | 699 | void *userpg, int fd); |