diff options
Diffstat (limited to 'tools/perf/util/sort.c')
-rw-r--r-- | tools/perf/util/sort.c | 601 |
1 files changed, 590 insertions, 11 deletions
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2d8ccd4d9e1b..ec722346e6ff 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -4,6 +4,8 @@ | |||
4 | #include "comm.h" | 4 | #include "comm.h" |
5 | #include "symbol.h" | 5 | #include "symbol.h" |
6 | #include "evsel.h" | 6 | #include "evsel.h" |
7 | #include "evlist.h" | ||
8 | #include <traceevent/event-parse.h> | ||
7 | 9 | ||
8 | regex_t parent_regex; | 10 | regex_t parent_regex; |
9 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; | 11 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; |
@@ -13,6 +15,7 @@ const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cy | |||
13 | const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; | 15 | const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; |
14 | const char default_top_sort_order[] = "dso,symbol"; | 16 | const char default_top_sort_order[] = "dso,symbol"; |
15 | const char default_diff_sort_order[] = "dso,symbol"; | 17 | const char default_diff_sort_order[] = "dso,symbol"; |
18 | const char default_tracepoint_sort_order[] = "trace"; | ||
16 | const char *sort_order; | 19 | const char *sort_order; |
17 | const char *field_order; | 20 | const char *field_order; |
18 | regex_t ignore_callees_regex; | 21 | regex_t ignore_callees_regex; |
@@ -443,6 +446,70 @@ struct sort_entry sort_socket = { | |||
443 | .se_width_idx = HISTC_SOCKET, | 446 | .se_width_idx = HISTC_SOCKET, |
444 | }; | 447 | }; |
445 | 448 | ||
449 | /* --sort trace */ | ||
450 | |||
451 | static char *get_trace_output(struct hist_entry *he) | ||
452 | { | ||
453 | struct trace_seq seq; | ||
454 | struct perf_evsel *evsel; | ||
455 | struct pevent_record rec = { | ||
456 | .data = he->raw_data, | ||
457 | .size = he->raw_size, | ||
458 | }; | ||
459 | |||
460 | evsel = hists_to_evsel(he->hists); | ||
461 | |||
462 | trace_seq_init(&seq); | ||
463 | if (symbol_conf.raw_trace) { | ||
464 | pevent_print_fields(&seq, he->raw_data, he->raw_size, | ||
465 | evsel->tp_format); | ||
466 | } else { | ||
467 | pevent_event_info(&seq, evsel->tp_format, &rec); | ||
468 | } | ||
469 | return seq.buffer; | ||
470 | } | ||
471 | |||
472 | static int64_t | ||
473 | sort__trace_cmp(struct hist_entry *left, struct hist_entry *right) | ||
474 | { | ||
475 | struct perf_evsel *evsel; | ||
476 | |||
477 | evsel = hists_to_evsel(left->hists); | ||
478 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | ||
479 | return 0; | ||
480 | |||
481 | if (left->trace_output == NULL) | ||
482 | left->trace_output = get_trace_output(left); | ||
483 | if (right->trace_output == NULL) | ||
484 | right->trace_output = get_trace_output(right); | ||
485 | |||
486 | hists__new_col_len(left->hists, HISTC_TRACE, strlen(left->trace_output)); | ||
487 | hists__new_col_len(right->hists, HISTC_TRACE, strlen(right->trace_output)); | ||
488 | |||
489 | return strcmp(right->trace_output, left->trace_output); | ||
490 | } | ||
491 | |||
492 | static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, | ||
493 | size_t size, unsigned int width) | ||
494 | { | ||
495 | struct perf_evsel *evsel; | ||
496 | |||
497 | evsel = hists_to_evsel(he->hists); | ||
498 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | ||
499 | return scnprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
500 | |||
501 | if (he->trace_output == NULL) | ||
502 | he->trace_output = get_trace_output(he); | ||
503 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->trace_output); | ||
504 | } | ||
505 | |||
506 | struct sort_entry sort_trace = { | ||
507 | .se_header = "Trace output", | ||
508 | .se_cmp = sort__trace_cmp, | ||
509 | .se_snprintf = hist_entry__trace_snprintf, | ||
510 | .se_width_idx = HISTC_TRACE, | ||
511 | }; | ||
512 | |||
446 | /* sort keys for branch stacks */ | 513 | /* sort keys for branch stacks */ |
447 | 514 | ||
448 | static int64_t | 515 | static int64_t |
@@ -1312,6 +1379,7 @@ static struct sort_dimension common_sort_dimensions[] = { | |||
1312 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), | 1379 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), |
1313 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | 1380 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), |
1314 | DIM(SORT_TRANSACTION, "transaction", sort_transaction), | 1381 | DIM(SORT_TRANSACTION, "transaction", sort_transaction), |
1382 | DIM(SORT_TRACE, "trace", sort_trace), | ||
1315 | }; | 1383 | }; |
1316 | 1384 | ||
1317 | #undef DIM | 1385 | #undef DIM |
@@ -1529,6 +1597,455 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) | |||
1529 | return 0; | 1597 | return 0; |
1530 | } | 1598 | } |
1531 | 1599 | ||
1600 | struct hpp_dynamic_entry { | ||
1601 | struct perf_hpp_fmt hpp; | ||
1602 | struct perf_evsel *evsel; | ||
1603 | struct format_field *field; | ||
1604 | unsigned dynamic_len; | ||
1605 | bool raw_trace; | ||
1606 | }; | ||
1607 | |||
1608 | static int hde_width(struct hpp_dynamic_entry *hde) | ||
1609 | { | ||
1610 | if (!hde->hpp.len) { | ||
1611 | int len = hde->dynamic_len; | ||
1612 | int namelen = strlen(hde->field->name); | ||
1613 | int fieldlen = hde->field->size; | ||
1614 | |||
1615 | if (namelen > len) | ||
1616 | len = namelen; | ||
1617 | |||
1618 | if (!(hde->field->flags & FIELD_IS_STRING)) { | ||
1619 | /* length for print hex numbers */ | ||
1620 | fieldlen = hde->field->size * 2 + 2; | ||
1621 | } | ||
1622 | if (fieldlen > len) | ||
1623 | len = fieldlen; | ||
1624 | |||
1625 | hde->hpp.len = len; | ||
1626 | } | ||
1627 | return hde->hpp.len; | ||
1628 | } | ||
1629 | |||
1630 | static void update_dynamic_len(struct hpp_dynamic_entry *hde, | ||
1631 | struct hist_entry *he) | ||
1632 | { | ||
1633 | char *str, *pos; | ||
1634 | struct format_field *field = hde->field; | ||
1635 | size_t namelen; | ||
1636 | bool last = false; | ||
1637 | |||
1638 | if (hde->raw_trace) | ||
1639 | return; | ||
1640 | |||
1641 | /* parse pretty print result and update max length */ | ||
1642 | if (!he->trace_output) | ||
1643 | he->trace_output = get_trace_output(he); | ||
1644 | |||
1645 | namelen = strlen(field->name); | ||
1646 | str = he->trace_output; | ||
1647 | |||
1648 | while (str) { | ||
1649 | pos = strchr(str, ' '); | ||
1650 | if (pos == NULL) { | ||
1651 | last = true; | ||
1652 | pos = str + strlen(str); | ||
1653 | } | ||
1654 | |||
1655 | if (!strncmp(str, field->name, namelen)) { | ||
1656 | size_t len; | ||
1657 | |||
1658 | str += namelen + 1; | ||
1659 | len = pos - str; | ||
1660 | |||
1661 | if (len > hde->dynamic_len) | ||
1662 | hde->dynamic_len = len; | ||
1663 | break; | ||
1664 | } | ||
1665 | |||
1666 | if (last) | ||
1667 | str = NULL; | ||
1668 | else | ||
1669 | str = pos + 1; | ||
1670 | } | ||
1671 | } | ||
1672 | |||
1673 | static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
1674 | struct perf_evsel *evsel __maybe_unused) | ||
1675 | { | ||
1676 | struct hpp_dynamic_entry *hde; | ||
1677 | size_t len = fmt->user_len; | ||
1678 | |||
1679 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1680 | |||
1681 | if (!len) | ||
1682 | len = hde_width(hde); | ||
1683 | |||
1684 | return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name); | ||
1685 | } | ||
1686 | |||
1687 | static int __sort__hde_width(struct perf_hpp_fmt *fmt, | ||
1688 | struct perf_hpp *hpp __maybe_unused, | ||
1689 | struct perf_evsel *evsel __maybe_unused) | ||
1690 | { | ||
1691 | struct hpp_dynamic_entry *hde; | ||
1692 | size_t len = fmt->user_len; | ||
1693 | |||
1694 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1695 | |||
1696 | if (!len) | ||
1697 | len = hde_width(hde); | ||
1698 | |||
1699 | return len; | ||
1700 | } | ||
1701 | |||
1702 | bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists) | ||
1703 | { | ||
1704 | struct hpp_dynamic_entry *hde; | ||
1705 | |||
1706 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1707 | |||
1708 | return hists_to_evsel(hists) == hde->evsel; | ||
1709 | } | ||
1710 | |||
1711 | static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
1712 | struct hist_entry *he) | ||
1713 | { | ||
1714 | struct hpp_dynamic_entry *hde; | ||
1715 | size_t len = fmt->user_len; | ||
1716 | char *str, *pos; | ||
1717 | struct format_field *field; | ||
1718 | size_t namelen; | ||
1719 | bool last = false; | ||
1720 | int ret; | ||
1721 | |||
1722 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1723 | |||
1724 | if (!len) | ||
1725 | len = hde_width(hde); | ||
1726 | |||
1727 | if (hde->raw_trace) | ||
1728 | goto raw_field; | ||
1729 | |||
1730 | field = hde->field; | ||
1731 | namelen = strlen(field->name); | ||
1732 | str = he->trace_output; | ||
1733 | |||
1734 | while (str) { | ||
1735 | pos = strchr(str, ' '); | ||
1736 | if (pos == NULL) { | ||
1737 | last = true; | ||
1738 | pos = str + strlen(str); | ||
1739 | } | ||
1740 | |||
1741 | if (!strncmp(str, field->name, namelen)) { | ||
1742 | str += namelen + 1; | ||
1743 | str = strndup(str, pos - str); | ||
1744 | |||
1745 | if (str == NULL) | ||
1746 | return scnprintf(hpp->buf, hpp->size, | ||
1747 | "%*.*s", len, len, "ERROR"); | ||
1748 | break; | ||
1749 | } | ||
1750 | |||
1751 | if (last) | ||
1752 | str = NULL; | ||
1753 | else | ||
1754 | str = pos + 1; | ||
1755 | } | ||
1756 | |||
1757 | if (str == NULL) { | ||
1758 | struct trace_seq seq; | ||
1759 | raw_field: | ||
1760 | trace_seq_init(&seq); | ||
1761 | pevent_print_field(&seq, he->raw_data, hde->field); | ||
1762 | str = seq.buffer; | ||
1763 | } | ||
1764 | |||
1765 | ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str); | ||
1766 | free(str); | ||
1767 | return ret; | ||
1768 | } | ||
1769 | |||
1770 | static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, | ||
1771 | struct hist_entry *a, struct hist_entry *b) | ||
1772 | { | ||
1773 | struct hpp_dynamic_entry *hde; | ||
1774 | struct format_field *field; | ||
1775 | unsigned offset, size; | ||
1776 | |||
1777 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1778 | |||
1779 | field = hde->field; | ||
1780 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
1781 | unsigned long long dyn; | ||
1782 | |||
1783 | pevent_read_number_field(field, a->raw_data, &dyn); | ||
1784 | offset = dyn & 0xffff; | ||
1785 | size = (dyn >> 16) & 0xffff; | ||
1786 | |||
1787 | /* record max width for output */ | ||
1788 | if (size > hde->dynamic_len) | ||
1789 | hde->dynamic_len = size; | ||
1790 | } else { | ||
1791 | offset = field->offset; | ||
1792 | size = field->size; | ||
1793 | |||
1794 | update_dynamic_len(hde, a); | ||
1795 | update_dynamic_len(hde, b); | ||
1796 | } | ||
1797 | |||
1798 | return memcmp(a->raw_data + offset, b->raw_data + offset, size); | ||
1799 | } | ||
1800 | |||
1801 | bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt) | ||
1802 | { | ||
1803 | return fmt->cmp == __sort__hde_cmp; | ||
1804 | } | ||
1805 | |||
1806 | static struct hpp_dynamic_entry * | ||
1807 | __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) | ||
1808 | { | ||
1809 | struct hpp_dynamic_entry *hde; | ||
1810 | |||
1811 | hde = malloc(sizeof(*hde)); | ||
1812 | if (hde == NULL) { | ||
1813 | pr_debug("Memory allocation failed\n"); | ||
1814 | return NULL; | ||
1815 | } | ||
1816 | |||
1817 | hde->evsel = evsel; | ||
1818 | hde->field = field; | ||
1819 | hde->dynamic_len = 0; | ||
1820 | |||
1821 | hde->hpp.name = field->name; | ||
1822 | hde->hpp.header = __sort__hde_header; | ||
1823 | hde->hpp.width = __sort__hde_width; | ||
1824 | hde->hpp.entry = __sort__hde_entry; | ||
1825 | hde->hpp.color = NULL; | ||
1826 | |||
1827 | hde->hpp.cmp = __sort__hde_cmp; | ||
1828 | hde->hpp.collapse = __sort__hde_cmp; | ||
1829 | hde->hpp.sort = __sort__hde_cmp; | ||
1830 | |||
1831 | INIT_LIST_HEAD(&hde->hpp.list); | ||
1832 | INIT_LIST_HEAD(&hde->hpp.sort_list); | ||
1833 | hde->hpp.elide = false; | ||
1834 | hde->hpp.len = 0; | ||
1835 | hde->hpp.user_len = 0; | ||
1836 | |||
1837 | return hde; | ||
1838 | } | ||
1839 | |||
1840 | static int parse_field_name(char *str, char **event, char **field, char **opt) | ||
1841 | { | ||
1842 | char *event_name, *field_name, *opt_name; | ||
1843 | |||
1844 | event_name = str; | ||
1845 | field_name = strchr(str, '.'); | ||
1846 | |||
1847 | if (field_name) { | ||
1848 | *field_name++ = '\0'; | ||
1849 | } else { | ||
1850 | event_name = NULL; | ||
1851 | field_name = str; | ||
1852 | } | ||
1853 | |||
1854 | opt_name = strchr(field_name, '/'); | ||
1855 | if (opt_name) | ||
1856 | *opt_name++ = '\0'; | ||
1857 | |||
1858 | *event = event_name; | ||
1859 | *field = field_name; | ||
1860 | *opt = opt_name; | ||
1861 | |||
1862 | return 0; | ||
1863 | } | ||
1864 | |||
1865 | /* find match evsel using a given event name. The event name can be: | ||
1866 | * 1. '%' + event index (e.g. '%1' for first event) | ||
1867 | * 2. full event name (e.g. sched:sched_switch) | ||
1868 | * 3. partial event name (should not contain ':') | ||
1869 | */ | ||
1870 | static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name) | ||
1871 | { | ||
1872 | struct perf_evsel *evsel = NULL; | ||
1873 | struct perf_evsel *pos; | ||
1874 | bool full_name; | ||
1875 | |||
1876 | /* case 1 */ | ||
1877 | if (event_name[0] == '%') { | ||
1878 | int nr = strtol(event_name+1, NULL, 0); | ||
1879 | |||
1880 | if (nr > evlist->nr_entries) | ||
1881 | return NULL; | ||
1882 | |||
1883 | evsel = perf_evlist__first(evlist); | ||
1884 | while (--nr > 0) | ||
1885 | evsel = perf_evsel__next(evsel); | ||
1886 | |||
1887 | return evsel; | ||
1888 | } | ||
1889 | |||
1890 | full_name = !!strchr(event_name, ':'); | ||
1891 | evlist__for_each(evlist, pos) { | ||
1892 | /* case 2 */ | ||
1893 | if (full_name && !strcmp(pos->name, event_name)) | ||
1894 | return pos; | ||
1895 | /* case 3 */ | ||
1896 | if (!full_name && strstr(pos->name, event_name)) { | ||
1897 | if (evsel) { | ||
1898 | pr_debug("'%s' event is ambiguous: it can be %s or %s\n", | ||
1899 | event_name, evsel->name, pos->name); | ||
1900 | return NULL; | ||
1901 | } | ||
1902 | evsel = pos; | ||
1903 | } | ||
1904 | } | ||
1905 | |||
1906 | return evsel; | ||
1907 | } | ||
1908 | |||
1909 | static int __dynamic_dimension__add(struct perf_evsel *evsel, | ||
1910 | struct format_field *field, | ||
1911 | bool raw_trace) | ||
1912 | { | ||
1913 | struct hpp_dynamic_entry *hde; | ||
1914 | |||
1915 | hde = __alloc_dynamic_entry(evsel, field); | ||
1916 | if (hde == NULL) | ||
1917 | return -ENOMEM; | ||
1918 | |||
1919 | hde->raw_trace = raw_trace; | ||
1920 | |||
1921 | perf_hpp__register_sort_field(&hde->hpp); | ||
1922 | return 0; | ||
1923 | } | ||
1924 | |||
1925 | static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace) | ||
1926 | { | ||
1927 | int ret; | ||
1928 | struct format_field *field; | ||
1929 | |||
1930 | field = evsel->tp_format->format.fields; | ||
1931 | while (field) { | ||
1932 | ret = __dynamic_dimension__add(evsel, field, raw_trace); | ||
1933 | if (ret < 0) | ||
1934 | return ret; | ||
1935 | |||
1936 | field = field->next; | ||
1937 | } | ||
1938 | return 0; | ||
1939 | } | ||
1940 | |||
1941 | static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) | ||
1942 | { | ||
1943 | int ret; | ||
1944 | struct perf_evsel *evsel; | ||
1945 | |||
1946 | evlist__for_each(evlist, evsel) { | ||
1947 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | ||
1948 | continue; | ||
1949 | |||
1950 | ret = add_evsel_fields(evsel, raw_trace); | ||
1951 | if (ret < 0) | ||
1952 | return ret; | ||
1953 | } | ||
1954 | return 0; | ||
1955 | } | ||
1956 | |||
1957 | static int add_all_matching_fields(struct perf_evlist *evlist, | ||
1958 | char *field_name, bool raw_trace) | ||
1959 | { | ||
1960 | int ret = -ESRCH; | ||
1961 | struct perf_evsel *evsel; | ||
1962 | struct format_field *field; | ||
1963 | |||
1964 | evlist__for_each(evlist, evsel) { | ||
1965 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | ||
1966 | continue; | ||
1967 | |||
1968 | field = pevent_find_any_field(evsel->tp_format, field_name); | ||
1969 | if (field == NULL) | ||
1970 | continue; | ||
1971 | |||
1972 | ret = __dynamic_dimension__add(evsel, field, raw_trace); | ||
1973 | if (ret < 0) | ||
1974 | break; | ||
1975 | } | ||
1976 | return ret; | ||
1977 | } | ||
1978 | |||
1979 | static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) | ||
1980 | { | ||
1981 | char *str, *event_name, *field_name, *opt_name; | ||
1982 | struct perf_evsel *evsel; | ||
1983 | struct format_field *field; | ||
1984 | bool raw_trace = symbol_conf.raw_trace; | ||
1985 | int ret = 0; | ||
1986 | |||
1987 | if (evlist == NULL) | ||
1988 | return -ENOENT; | ||
1989 | |||
1990 | str = strdup(tok); | ||
1991 | if (str == NULL) | ||
1992 | return -ENOMEM; | ||
1993 | |||
1994 | if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) { | ||
1995 | ret = -EINVAL; | ||
1996 | goto out; | ||
1997 | } | ||
1998 | |||
1999 | if (opt_name) { | ||
2000 | if (strcmp(opt_name, "raw")) { | ||
2001 | pr_debug("unsupported field option %s\n", opt_name); | ||
2002 | ret = -EINVAL; | ||
2003 | goto out; | ||
2004 | } | ||
2005 | raw_trace = true; | ||
2006 | } | ||
2007 | |||
2008 | if (!strcmp(field_name, "trace_fields")) { | ||
2009 | ret = add_all_dynamic_fields(evlist, raw_trace); | ||
2010 | goto out; | ||
2011 | } | ||
2012 | |||
2013 | if (event_name == NULL) { | ||
2014 | ret = add_all_matching_fields(evlist, field_name, raw_trace); | ||
2015 | goto out; | ||
2016 | } | ||
2017 | |||
2018 | evsel = find_evsel(evlist, event_name); | ||
2019 | if (evsel == NULL) { | ||
2020 | pr_debug("Cannot find event: %s\n", event_name); | ||
2021 | ret = -ENOENT; | ||
2022 | goto out; | ||
2023 | } | ||
2024 | |||
2025 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { | ||
2026 | pr_debug("%s is not a tracepoint event\n", event_name); | ||
2027 | ret = -EINVAL; | ||
2028 | goto out; | ||
2029 | } | ||
2030 | |||
2031 | if (!strcmp(field_name, "*")) { | ||
2032 | ret = add_evsel_fields(evsel, raw_trace); | ||
2033 | } else { | ||
2034 | field = pevent_find_any_field(evsel->tp_format, field_name); | ||
2035 | if (field == NULL) { | ||
2036 | pr_debug("Cannot find event field for %s.%s\n", | ||
2037 | event_name, field_name); | ||
2038 | return -ENOENT; | ||
2039 | } | ||
2040 | |||
2041 | ret = __dynamic_dimension__add(evsel, field, raw_trace); | ||
2042 | } | ||
2043 | |||
2044 | out: | ||
2045 | free(str); | ||
2046 | return ret; | ||
2047 | } | ||
2048 | |||
1532 | static int __sort_dimension__add(struct sort_dimension *sd) | 2049 | static int __sort_dimension__add(struct sort_dimension *sd) |
1533 | { | 2050 | { |
1534 | if (sd->taken) | 2051 | if (sd->taken) |
@@ -1583,7 +2100,8 @@ int hpp_dimension__add_output(unsigned col) | |||
1583 | return __hpp_dimension__add_output(&hpp_sort_dimensions[col]); | 2100 | return __hpp_dimension__add_output(&hpp_sort_dimensions[col]); |
1584 | } | 2101 | } |
1585 | 2102 | ||
1586 | int sort_dimension__add(const char *tok) | 2103 | static int sort_dimension__add(const char *tok, |
2104 | struct perf_evlist *evlist __maybe_unused) | ||
1587 | { | 2105 | { |
1588 | unsigned int i; | 2106 | unsigned int i; |
1589 | 2107 | ||
@@ -1664,10 +2182,13 @@ int sort_dimension__add(const char *tok) | |||
1664 | return 0; | 2182 | return 0; |
1665 | } | 2183 | } |
1666 | 2184 | ||
2185 | if (!add_dynamic_entry(evlist, tok)) | ||
2186 | return 0; | ||
2187 | |||
1667 | return -ESRCH; | 2188 | return -ESRCH; |
1668 | } | 2189 | } |
1669 | 2190 | ||
1670 | static const char *get_default_sort_order(void) | 2191 | static const char *get_default_sort_order(struct perf_evlist *evlist) |
1671 | { | 2192 | { |
1672 | const char *default_sort_orders[] = { | 2193 | const char *default_sort_orders[] = { |
1673 | default_sort_order, | 2194 | default_sort_order, |
@@ -1675,14 +2196,33 @@ static const char *get_default_sort_order(void) | |||
1675 | default_mem_sort_order, | 2196 | default_mem_sort_order, |
1676 | default_top_sort_order, | 2197 | default_top_sort_order, |
1677 | default_diff_sort_order, | 2198 | default_diff_sort_order, |
2199 | default_tracepoint_sort_order, | ||
1678 | }; | 2200 | }; |
2201 | bool use_trace = true; | ||
2202 | struct perf_evsel *evsel; | ||
1679 | 2203 | ||
1680 | BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); | 2204 | BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); |
1681 | 2205 | ||
2206 | if (evlist == NULL) | ||
2207 | goto out_no_evlist; | ||
2208 | |||
2209 | evlist__for_each(evlist, evsel) { | ||
2210 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { | ||
2211 | use_trace = false; | ||
2212 | break; | ||
2213 | } | ||
2214 | } | ||
2215 | |||
2216 | if (use_trace) { | ||
2217 | sort__mode = SORT_MODE__TRACEPOINT; | ||
2218 | if (symbol_conf.raw_trace) | ||
2219 | return "trace_fields"; | ||
2220 | } | ||
2221 | out_no_evlist: | ||
1682 | return default_sort_orders[sort__mode]; | 2222 | return default_sort_orders[sort__mode]; |
1683 | } | 2223 | } |
1684 | 2224 | ||
1685 | static int setup_sort_order(void) | 2225 | static int setup_sort_order(struct perf_evlist *evlist) |
1686 | { | 2226 | { |
1687 | char *new_sort_order; | 2227 | char *new_sort_order; |
1688 | 2228 | ||
@@ -1703,7 +2243,7 @@ static int setup_sort_order(void) | |||
1703 | * because it's checked over the rest of the code. | 2243 | * because it's checked over the rest of the code. |
1704 | */ | 2244 | */ |
1705 | if (asprintf(&new_sort_order, "%s,%s", | 2245 | if (asprintf(&new_sort_order, "%s,%s", |
1706 | get_default_sort_order(), sort_order + 1) < 0) { | 2246 | get_default_sort_order(evlist), sort_order + 1) < 0) { |
1707 | error("Not enough memory to set up --sort"); | 2247 | error("Not enough memory to set up --sort"); |
1708 | return -ENOMEM; | 2248 | return -ENOMEM; |
1709 | } | 2249 | } |
@@ -1712,13 +2252,41 @@ static int setup_sort_order(void) | |||
1712 | return 0; | 2252 | return 0; |
1713 | } | 2253 | } |
1714 | 2254 | ||
1715 | static int __setup_sorting(void) | 2255 | /* |
2256 | * Adds 'pre,' prefix into 'str' is 'pre' is | ||
2257 | * not already part of 'str'. | ||
2258 | */ | ||
2259 | static char *prefix_if_not_in(const char *pre, char *str) | ||
2260 | { | ||
2261 | char *n; | ||
2262 | |||
2263 | if (!str || strstr(str, pre)) | ||
2264 | return str; | ||
2265 | |||
2266 | if (asprintf(&n, "%s,%s", pre, str) < 0) | ||
2267 | return NULL; | ||
2268 | |||
2269 | free(str); | ||
2270 | return n; | ||
2271 | } | ||
2272 | |||
2273 | static char *setup_overhead(char *keys) | ||
2274 | { | ||
2275 | keys = prefix_if_not_in("overhead", keys); | ||
2276 | |||
2277 | if (symbol_conf.cumulate_callchain) | ||
2278 | keys = prefix_if_not_in("overhead_children", keys); | ||
2279 | |||
2280 | return keys; | ||
2281 | } | ||
2282 | |||
2283 | static int __setup_sorting(struct perf_evlist *evlist) | ||
1716 | { | 2284 | { |
1717 | char *tmp, *tok, *str; | 2285 | char *tmp, *tok, *str; |
1718 | const char *sort_keys; | 2286 | const char *sort_keys; |
1719 | int ret = 0; | 2287 | int ret = 0; |
1720 | 2288 | ||
1721 | ret = setup_sort_order(); | 2289 | ret = setup_sort_order(evlist); |
1722 | if (ret) | 2290 | if (ret) |
1723 | return ret; | 2291 | return ret; |
1724 | 2292 | ||
@@ -1732,7 +2300,7 @@ static int __setup_sorting(void) | |||
1732 | return 0; | 2300 | return 0; |
1733 | } | 2301 | } |
1734 | 2302 | ||
1735 | sort_keys = get_default_sort_order(); | 2303 | sort_keys = get_default_sort_order(evlist); |
1736 | } | 2304 | } |
1737 | 2305 | ||
1738 | str = strdup(sort_keys); | 2306 | str = strdup(sort_keys); |
@@ -1741,9 +2309,20 @@ static int __setup_sorting(void) | |||
1741 | return -ENOMEM; | 2309 | return -ENOMEM; |
1742 | } | 2310 | } |
1743 | 2311 | ||
2312 | /* | ||
2313 | * Prepend overhead fields for backward compatibility. | ||
2314 | */ | ||
2315 | if (!is_strict_order(field_order)) { | ||
2316 | str = setup_overhead(str); | ||
2317 | if (str == NULL) { | ||
2318 | error("Not enough memory to setup overhead keys"); | ||
2319 | return -ENOMEM; | ||
2320 | } | ||
2321 | } | ||
2322 | |||
1744 | for (tok = strtok_r(str, ", ", &tmp); | 2323 | for (tok = strtok_r(str, ", ", &tmp); |
1745 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 2324 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
1746 | ret = sort_dimension__add(tok); | 2325 | ret = sort_dimension__add(tok, evlist); |
1747 | if (ret == -EINVAL) { | 2326 | if (ret == -EINVAL) { |
1748 | error("Invalid --sort key: `%s'", tok); | 2327 | error("Invalid --sort key: `%s'", tok); |
1749 | break; | 2328 | break; |
@@ -1954,16 +2533,16 @@ out: | |||
1954 | return ret; | 2533 | return ret; |
1955 | } | 2534 | } |
1956 | 2535 | ||
1957 | int setup_sorting(void) | 2536 | int setup_sorting(struct perf_evlist *evlist) |
1958 | { | 2537 | { |
1959 | int err; | 2538 | int err; |
1960 | 2539 | ||
1961 | err = __setup_sorting(); | 2540 | err = __setup_sorting(evlist); |
1962 | if (err < 0) | 2541 | if (err < 0) |
1963 | return err; | 2542 | return err; |
1964 | 2543 | ||
1965 | if (parent_pattern != default_parent_pattern) { | 2544 | if (parent_pattern != default_parent_pattern) { |
1966 | err = sort_dimension__add("parent"); | 2545 | err = sort_dimension__add("parent", evlist); |
1967 | if (err < 0) | 2546 | if (err < 0) |
1968 | return err; | 2547 | return err; |
1969 | } | 2548 | } |