diff options
Diffstat (limited to 'tools/perf/util')
37 files changed, 1652 insertions, 232 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 5f6dbbf5d749..c8b01176c9e1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -10,6 +10,10 @@ | |||
| 10 | #include <errno.h> | 10 | #include <errno.h> |
| 11 | #include <inttypes.h> | 11 | #include <inttypes.h> |
| 12 | #include <libgen.h> | 12 | #include <libgen.h> |
| 13 | #include <bpf/bpf.h> | ||
| 14 | #include <bpf/btf.h> | ||
| 15 | #include <bpf/libbpf.h> | ||
| 16 | #include <linux/btf.h> | ||
| 13 | #include "util.h" | 17 | #include "util.h" |
| 14 | #include "ui/ui.h" | 18 | #include "ui/ui.h" |
| 15 | #include "sort.h" | 19 | #include "sort.h" |
| @@ -24,6 +28,7 @@ | |||
| 24 | #include "annotate.h" | 28 | #include "annotate.h" |
| 25 | #include "evsel.h" | 29 | #include "evsel.h" |
| 26 | #include "evlist.h" | 30 | #include "evlist.h" |
| 31 | #include "bpf-event.h" | ||
| 27 | #include "block-range.h" | 32 | #include "block-range.h" |
| 28 | #include "string2.h" | 33 | #include "string2.h" |
| 29 | #include "arch/common.h" | 34 | #include "arch/common.h" |
| @@ -31,6 +36,7 @@ | |||
| 31 | #include <pthread.h> | 36 | #include <pthread.h> |
| 32 | #include <linux/bitops.h> | 37 | #include <linux/bitops.h> |
| 33 | #include <linux/kernel.h> | 38 | #include <linux/kernel.h> |
| 39 | #include <bpf/libbpf.h> | ||
| 34 | 40 | ||
| 35 | /* FIXME: For the HE_COLORSET */ | 41 | /* FIXME: For the HE_COLORSET */ |
| 36 | #include "ui/browser.h" | 42 | #include "ui/browser.h" |
| @@ -1615,6 +1621,9 @@ int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map * | |||
| 1615 | " --vmlinux vmlinux\n", build_id_msg ?: ""); | 1621 | " --vmlinux vmlinux\n", build_id_msg ?: ""); |
| 1616 | } | 1622 | } |
| 1617 | break; | 1623 | break; |
| 1624 | case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF: | ||
| 1625 | scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation"); | ||
| 1626 | break; | ||
| 1618 | default: | 1627 | default: |
| 1619 | scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); | 1628 | scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); |
| 1620 | break; | 1629 | break; |
| @@ -1674,6 +1683,156 @@ fallback: | |||
| 1674 | return 0; | 1683 | return 0; |
| 1675 | } | 1684 | } |
| 1676 | 1685 | ||
| 1686 | #if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) | ||
| 1687 | #define PACKAGE "perf" | ||
| 1688 | #include <bfd.h> | ||
| 1689 | #include <dis-asm.h> | ||
| 1690 | |||
| 1691 | static int symbol__disassemble_bpf(struct symbol *sym, | ||
| 1692 | struct annotate_args *args) | ||
| 1693 | { | ||
| 1694 | struct annotation *notes = symbol__annotation(sym); | ||
| 1695 | struct annotation_options *opts = args->options; | ||
| 1696 | struct bpf_prog_info_linear *info_linear; | ||
| 1697 | struct bpf_prog_linfo *prog_linfo = NULL; | ||
| 1698 | struct bpf_prog_info_node *info_node; | ||
| 1699 | int len = sym->end - sym->start; | ||
| 1700 | disassembler_ftype disassemble; | ||
| 1701 | struct map *map = args->ms.map; | ||
| 1702 | struct disassemble_info info; | ||
| 1703 | struct dso *dso = map->dso; | ||
| 1704 | int pc = 0, count, sub_id; | ||
| 1705 | struct btf *btf = NULL; | ||
| 1706 | char tpath[PATH_MAX]; | ||
| 1707 | size_t buf_size; | ||
| 1708 | int nr_skip = 0; | ||
| 1709 | int ret = -1; | ||
| 1710 | char *buf; | ||
| 1711 | bfd *bfdf; | ||
| 1712 | FILE *s; | ||
| 1713 | |||
| 1714 | if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO) | ||
| 1715 | return -1; | ||
| 1716 | |||
| 1717 | pr_debug("%s: handling sym %s addr %lx len %lx\n", __func__, | ||
| 1718 | sym->name, sym->start, sym->end - sym->start); | ||
| 1719 | |||
| 1720 | memset(tpath, 0, sizeof(tpath)); | ||
| 1721 | perf_exe(tpath, sizeof(tpath)); | ||
| 1722 | |||
| 1723 | bfdf = bfd_openr(tpath, NULL); | ||
| 1724 | assert(bfdf); | ||
| 1725 | assert(bfd_check_format(bfdf, bfd_object)); | ||
| 1726 | |||
| 1727 | s = open_memstream(&buf, &buf_size); | ||
| 1728 | if (!s) | ||
| 1729 | goto out; | ||
| 1730 | init_disassemble_info(&info, s, | ||
| 1731 | (fprintf_ftype) fprintf); | ||
| 1732 | |||
| 1733 | info.arch = bfd_get_arch(bfdf); | ||
| 1734 | info.mach = bfd_get_mach(bfdf); | ||
| 1735 | |||
| 1736 | info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, | ||
| 1737 | dso->bpf_prog.id); | ||
| 1738 | if (!info_node) | ||
| 1739 | goto out; | ||
| 1740 | info_linear = info_node->info_linear; | ||
| 1741 | sub_id = dso->bpf_prog.sub_id; | ||
| 1742 | |||
| 1743 | info.buffer = (void *)(info_linear->info.jited_prog_insns); | ||
| 1744 | info.buffer_length = info_linear->info.jited_prog_len; | ||
| 1745 | |||
| 1746 | if (info_linear->info.nr_line_info) | ||
| 1747 | prog_linfo = bpf_prog_linfo__new(&info_linear->info); | ||
| 1748 | |||
| 1749 | if (info_linear->info.btf_id) { | ||
| 1750 | struct btf_node *node; | ||
| 1751 | |||
| 1752 | node = perf_env__find_btf(dso->bpf_prog.env, | ||
| 1753 | info_linear->info.btf_id); | ||
| 1754 | if (node) | ||
| 1755 | btf = btf__new((__u8 *)(node->data), | ||
| 1756 | node->data_size); | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | disassemble_init_for_target(&info); | ||
| 1760 | |||
| 1761 | #ifdef DISASM_FOUR_ARGS_SIGNATURE | ||
| 1762 | disassemble = disassembler(info.arch, | ||
| 1763 | bfd_big_endian(bfdf), | ||
| 1764 | info.mach, | ||
| 1765 | bfdf); | ||
| 1766 | #else | ||
| 1767 | disassemble = disassembler(bfdf); | ||
| 1768 | #endif | ||
| 1769 | assert(disassemble); | ||
| 1770 | |||
| 1771 | fflush(s); | ||
| 1772 | do { | ||
| 1773 | const struct bpf_line_info *linfo = NULL; | ||
| 1774 | struct disasm_line *dl; | ||
| 1775 | size_t prev_buf_size; | ||
| 1776 | const char *srcline; | ||
| 1777 | u64 addr; | ||
| 1778 | |||
| 1779 | addr = pc + ((u64 *)(info_linear->info.jited_ksyms))[sub_id]; | ||
| 1780 | count = disassemble(pc, &info); | ||
| 1781 | |||
| 1782 | if (prog_linfo) | ||
| 1783 | linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, | ||
| 1784 | addr, sub_id, | ||
| 1785 | nr_skip); | ||
| 1786 | |||
| 1787 | if (linfo && btf) { | ||
| 1788 | srcline = btf__name_by_offset(btf, linfo->line_off); | ||
| 1789 | nr_skip++; | ||
| 1790 | } else | ||
| 1791 | srcline = NULL; | ||
| 1792 | |||
| 1793 | fprintf(s, "\n"); | ||
| 1794 | prev_buf_size = buf_size; | ||
| 1795 | fflush(s); | ||
| 1796 | |||
| 1797 | if (!opts->hide_src_code && srcline) { | ||
| 1798 | args->offset = -1; | ||
| 1799 | args->line = strdup(srcline); | ||
| 1800 | args->line_nr = 0; | ||
| 1801 | args->ms.sym = sym; | ||
| 1802 | dl = disasm_line__new(args); | ||
| 1803 | if (dl) { | ||
| 1804 | annotation_line__add(&dl->al, | ||
| 1805 | ¬es->src->source); | ||
| 1806 | } | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | args->offset = pc; | ||
| 1810 | args->line = buf + prev_buf_size; | ||
| 1811 | args->line_nr = 0; | ||
| 1812 | args->ms.sym = sym; | ||
| 1813 | dl = disasm_line__new(args); | ||
| 1814 | if (dl) | ||
| 1815 | annotation_line__add(&dl->al, ¬es->src->source); | ||
| 1816 | |||
| 1817 | pc += count; | ||
| 1818 | } while (count > 0 && pc < len); | ||
| 1819 | |||
| 1820 | ret = 0; | ||
| 1821 | out: | ||
| 1822 | free(prog_linfo); | ||
| 1823 | free(btf); | ||
| 1824 | fclose(s); | ||
| 1825 | bfd_close(bfdf); | ||
| 1826 | return ret; | ||
| 1827 | } | ||
| 1828 | #else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) | ||
| 1829 | static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, | ||
| 1830 | struct annotate_args *args __maybe_unused) | ||
| 1831 | { | ||
| 1832 | return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; | ||
| 1833 | } | ||
| 1834 | #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) | ||
| 1835 | |||
| 1677 | static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) | 1836 | static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) |
| 1678 | { | 1837 | { |
| 1679 | struct annotation_options *opts = args->options; | 1838 | struct annotation_options *opts = args->options; |
| @@ -1701,7 +1860,9 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) | |||
| 1701 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | 1860 | pr_debug("annotating [%p] %30s : [%p] %30s\n", |
| 1702 | dso, dso->long_name, sym, sym->name); | 1861 | dso, dso->long_name, sym, sym->name); |
| 1703 | 1862 | ||
| 1704 | if (dso__is_kcore(dso)) { | 1863 | if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) { |
| 1864 | return symbol__disassemble_bpf(sym, args); | ||
| 1865 | } else if (dso__is_kcore(dso)) { | ||
| 1705 | kce.kcore_filename = symfs_filename; | 1866 | kce.kcore_filename = symfs_filename; |
| 1706 | kce.addr = map__rip_2objdump(map, sym->start); | 1867 | kce.addr = map__rip_2objdump(map, sym->start); |
| 1707 | kce.offs = sym->start; | 1868 | kce.offs = sym->start; |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index df34fe483164..5bc0cf655d37 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
| @@ -369,6 +369,7 @@ enum symbol_disassemble_errno { | |||
| 369 | __SYMBOL_ANNOTATE_ERRNO__START = -10000, | 369 | __SYMBOL_ANNOTATE_ERRNO__START = -10000, |
| 370 | 370 | ||
| 371 | SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START, | 371 | SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START, |
| 372 | SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF, | ||
| 372 | 373 | ||
| 373 | __SYMBOL_ANNOTATE_ERRNO__END, | 374 | __SYMBOL_ANNOTATE_ERRNO__END, |
| 374 | }; | 375 | }; |
diff --git a/tools/perf/util/archinsn.h b/tools/perf/util/archinsn.h new file mode 100644 index 000000000000..448cbb6b8d7e --- /dev/null +++ b/tools/perf/util/archinsn.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef INSN_H | ||
| 2 | #define INSN_H 1 | ||
| 3 | |||
| 4 | struct perf_sample; | ||
| 5 | struct machine; | ||
| 6 | struct thread; | ||
| 7 | |||
| 8 | void arch_fetch_insn(struct perf_sample *sample, | ||
| 9 | struct thread *thread, | ||
| 10 | struct machine *machine); | ||
| 11 | |||
| 12 | #endif | ||
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 028c8ec1f62a..2a4a0da35632 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c | |||
| @@ -3,11 +3,17 @@ | |||
| 3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
| 4 | #include <bpf/bpf.h> | 4 | #include <bpf/bpf.h> |
| 5 | #include <bpf/btf.h> | 5 | #include <bpf/btf.h> |
| 6 | #include <bpf/libbpf.h> | ||
| 6 | #include <linux/btf.h> | 7 | #include <linux/btf.h> |
| 8 | #include <linux/err.h> | ||
| 7 | #include "bpf-event.h" | 9 | #include "bpf-event.h" |
| 8 | #include "debug.h" | 10 | #include "debug.h" |
| 9 | #include "symbol.h" | 11 | #include "symbol.h" |
| 10 | #include "machine.h" | 12 | #include "machine.h" |
| 13 | #include "env.h" | ||
| 14 | #include "session.h" | ||
| 15 | #include "map.h" | ||
| 16 | #include "evlist.h" | ||
| 11 | 17 | ||
| 12 | #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) | 18 | #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) |
| 13 | 19 | ||
| @@ -21,15 +27,122 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) | |||
| 21 | return ret; | 27 | return ret; |
| 22 | } | 28 | } |
| 23 | 29 | ||
| 30 | static int machine__process_bpf_event_load(struct machine *machine, | ||
| 31 | union perf_event *event, | ||
| 32 | struct perf_sample *sample __maybe_unused) | ||
| 33 | { | ||
| 34 | struct bpf_prog_info_linear *info_linear; | ||
| 35 | struct bpf_prog_info_node *info_node; | ||
| 36 | struct perf_env *env = machine->env; | ||
| 37 | int id = event->bpf_event.id; | ||
| 38 | unsigned int i; | ||
| 39 | |||
| 40 | /* perf-record, no need to handle bpf-event */ | ||
| 41 | if (env == NULL) | ||
| 42 | return 0; | ||
| 43 | |||
| 44 | info_node = perf_env__find_bpf_prog_info(env, id); | ||
| 45 | if (!info_node) | ||
| 46 | return 0; | ||
| 47 | info_linear = info_node->info_linear; | ||
| 48 | |||
| 49 | for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) { | ||
| 50 | u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms); | ||
| 51 | u64 addr = addrs[i]; | ||
| 52 | struct map *map; | ||
| 53 | |||
| 54 | map = map_groups__find(&machine->kmaps, addr); | ||
| 55 | |||
| 56 | if (map) { | ||
| 57 | map->dso->binary_type = DSO_BINARY_TYPE__BPF_PROG_INFO; | ||
| 58 | map->dso->bpf_prog.id = id; | ||
| 59 | map->dso->bpf_prog.sub_id = i; | ||
| 60 | map->dso->bpf_prog.env = env; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 24 | int machine__process_bpf_event(struct machine *machine __maybe_unused, | 66 | int machine__process_bpf_event(struct machine *machine __maybe_unused, |
| 25 | union perf_event *event, | 67 | union perf_event *event, |
| 26 | struct perf_sample *sample __maybe_unused) | 68 | struct perf_sample *sample __maybe_unused) |
| 27 | { | 69 | { |
| 28 | if (dump_trace) | 70 | if (dump_trace) |
| 29 | perf_event__fprintf_bpf_event(event, stdout); | 71 | perf_event__fprintf_bpf_event(event, stdout); |
| 72 | |||
| 73 | switch (event->bpf_event.type) { | ||
| 74 | case PERF_BPF_EVENT_PROG_LOAD: | ||
| 75 | return machine__process_bpf_event_load(machine, event, sample); | ||
| 76 | |||
| 77 | case PERF_BPF_EVENT_PROG_UNLOAD: | ||
| 78 | /* | ||
| 79 | * Do not free bpf_prog_info and btf of the program here, | ||
| 80 | * as annotation still need them. They will be freed at | ||
| 81 | * the end of the session. | ||
| 82 | */ | ||
| 83 | break; | ||
| 84 | default: | ||
| 85 | pr_debug("unexpected bpf_event type of %d\n", | ||
| 86 | event->bpf_event.type); | ||
| 87 | break; | ||
| 88 | } | ||
| 30 | return 0; | 89 | return 0; |
| 31 | } | 90 | } |
| 32 | 91 | ||
| 92 | static int perf_env__fetch_btf(struct perf_env *env, | ||
| 93 | u32 btf_id, | ||
| 94 | struct btf *btf) | ||
| 95 | { | ||
| 96 | struct btf_node *node; | ||
| 97 | u32 data_size; | ||
| 98 | const void *data; | ||
| 99 | |||
| 100 | data = btf__get_raw_data(btf, &data_size); | ||
| 101 | |||
| 102 | node = malloc(data_size + sizeof(struct btf_node)); | ||
| 103 | if (!node) | ||
| 104 | return -1; | ||
| 105 | |||
| 106 | node->id = btf_id; | ||
| 107 | node->data_size = data_size; | ||
| 108 | memcpy(node->data, data, data_size); | ||
| 109 | |||
| 110 | perf_env__insert_btf(env, node); | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int synthesize_bpf_prog_name(char *buf, int size, | ||
| 115 | struct bpf_prog_info *info, | ||
| 116 | struct btf *btf, | ||
| 117 | u32 sub_id) | ||
| 118 | { | ||
| 119 | u8 (*prog_tags)[BPF_TAG_SIZE] = (void *)(uintptr_t)(info->prog_tags); | ||
| 120 | void *func_infos = (void *)(uintptr_t)(info->func_info); | ||
| 121 | u32 sub_prog_cnt = info->nr_jited_ksyms; | ||
| 122 | const struct bpf_func_info *finfo; | ||
| 123 | const char *short_name = NULL; | ||
| 124 | const struct btf_type *t; | ||
| 125 | int name_len; | ||
| 126 | |||
| 127 | name_len = snprintf(buf, size, "bpf_prog_"); | ||
| 128 | name_len += snprintf_hex(buf + name_len, size - name_len, | ||
| 129 | prog_tags[sub_id], BPF_TAG_SIZE); | ||
| 130 | if (btf) { | ||
| 131 | finfo = func_infos + sub_id * info->func_info_rec_size; | ||
| 132 | t = btf__type_by_id(btf, finfo->type_id); | ||
| 133 | short_name = btf__name_by_offset(btf, t->name_off); | ||
| 134 | } else if (sub_id == 0 && sub_prog_cnt == 1) { | ||
| 135 | /* no subprog */ | ||
| 136 | if (info->name[0]) | ||
| 137 | short_name = info->name; | ||
| 138 | } else | ||
| 139 | short_name = "F"; | ||
| 140 | if (short_name) | ||
| 141 | name_len += snprintf(buf + name_len, size - name_len, | ||
| 142 | "_%s", short_name); | ||
| 143 | return name_len; | ||
| 144 | } | ||
| 145 | |||
| 33 | /* | 146 | /* |
| 34 | * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf | 147 | * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf |
| 35 | * program. One PERF_RECORD_BPF_EVENT is generated for the program. And | 148 | * program. One PERF_RECORD_BPF_EVENT is generated for the program. And |
| @@ -40,7 +153,7 @@ int machine__process_bpf_event(struct machine *machine __maybe_unused, | |||
| 40 | * -1 for failures; | 153 | * -1 for failures; |
| 41 | * -2 for lack of kernel support. | 154 | * -2 for lack of kernel support. |
| 42 | */ | 155 | */ |
| 43 | static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool, | 156 | static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, |
| 44 | perf_event__handler_t process, | 157 | perf_event__handler_t process, |
| 45 | struct machine *machine, | 158 | struct machine *machine, |
| 46 | int fd, | 159 | int fd, |
| @@ -49,102 +162,71 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool, | |||
| 49 | { | 162 | { |
| 50 | struct ksymbol_event *ksymbol_event = &event->ksymbol_event; | 163 | struct ksymbol_event *ksymbol_event = &event->ksymbol_event; |
| 51 | struct bpf_event *bpf_event = &event->bpf_event; | 164 | struct bpf_event *bpf_event = &event->bpf_event; |
| 52 | u32 sub_prog_cnt, i, func_info_rec_size = 0; | 165 | struct bpf_prog_info_linear *info_linear; |
| 53 | u8 (*prog_tags)[BPF_TAG_SIZE] = NULL; | 166 | struct perf_tool *tool = session->tool; |
| 54 | struct bpf_prog_info info = { .type = 0, }; | 167 | struct bpf_prog_info_node *info_node; |
| 55 | u32 info_len = sizeof(info); | 168 | struct bpf_prog_info *info; |
| 56 | void *func_infos = NULL; | ||
| 57 | u64 *prog_addrs = NULL; | ||
| 58 | struct btf *btf = NULL; | 169 | struct btf *btf = NULL; |
| 59 | u32 *prog_lens = NULL; | 170 | struct perf_env *env; |
| 60 | bool has_btf = false; | 171 | u32 sub_prog_cnt, i; |
| 61 | char errbuf[512]; | ||
| 62 | int err = 0; | 172 | int err = 0; |
| 173 | u64 arrays; | ||
| 174 | |||
| 175 | /* | ||
| 176 | * for perf-record and perf-report use header.env; | ||
| 177 | * otherwise, use global perf_env. | ||
| 178 | */ | ||
| 179 | env = session->data ? &session->header.env : &perf_env; | ||
| 63 | 180 | ||
| 64 | /* Call bpf_obj_get_info_by_fd() to get sizes of arrays */ | 181 | arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS; |
| 65 | err = bpf_obj_get_info_by_fd(fd, &info, &info_len); | 182 | arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; |
| 183 | arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; | ||
| 184 | arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS; | ||
| 185 | arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS; | ||
| 186 | arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; | ||
| 187 | arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; | ||
| 66 | 188 | ||
| 67 | if (err) { | 189 | info_linear = bpf_program__get_prog_info_linear(fd, arrays); |
| 68 | pr_debug("%s: failed to get BPF program info: %s, aborting\n", | 190 | if (IS_ERR_OR_NULL(info_linear)) { |
| 69 | __func__, str_error_r(errno, errbuf, sizeof(errbuf))); | 191 | info_linear = NULL; |
| 192 | pr_debug("%s: failed to get BPF program info. aborting\n", __func__); | ||
| 70 | return -1; | 193 | return -1; |
| 71 | } | 194 | } |
| 72 | if (info_len < offsetof(struct bpf_prog_info, prog_tags)) { | 195 | |
| 196 | if (info_linear->info_len < offsetof(struct bpf_prog_info, prog_tags)) { | ||
| 73 | pr_debug("%s: the kernel is too old, aborting\n", __func__); | 197 | pr_debug("%s: the kernel is too old, aborting\n", __func__); |
| 74 | return -2; | 198 | return -2; |
| 75 | } | 199 | } |
| 76 | 200 | ||
| 201 | info = &info_linear->info; | ||
| 202 | |||
| 77 | /* number of ksyms, func_lengths, and tags should match */ | 203 | /* number of ksyms, func_lengths, and tags should match */ |
| 78 | sub_prog_cnt = info.nr_jited_ksyms; | 204 | sub_prog_cnt = info->nr_jited_ksyms; |
| 79 | if (sub_prog_cnt != info.nr_prog_tags || | 205 | if (sub_prog_cnt != info->nr_prog_tags || |
| 80 | sub_prog_cnt != info.nr_jited_func_lens) | 206 | sub_prog_cnt != info->nr_jited_func_lens) |
| 81 | return -1; | 207 | return -1; |
| 82 | 208 | ||
| 83 | /* check BTF func info support */ | 209 | /* check BTF func info support */ |
| 84 | if (info.btf_id && info.nr_func_info && info.func_info_rec_size) { | 210 | if (info->btf_id && info->nr_func_info && info->func_info_rec_size) { |
| 85 | /* btf func info number should be same as sub_prog_cnt */ | 211 | /* btf func info number should be same as sub_prog_cnt */ |
| 86 | if (sub_prog_cnt != info.nr_func_info) { | 212 | if (sub_prog_cnt != info->nr_func_info) { |
| 87 | pr_debug("%s: mismatch in BPF sub program count and BTF function info count, aborting\n", __func__); | 213 | pr_debug("%s: mismatch in BPF sub program count and BTF function info count, aborting\n", __func__); |
| 88 | return -1; | 214 | err = -1; |
| 89 | } | 215 | goto out; |
| 90 | if (btf__get_from_id(info.btf_id, &btf)) { | ||
| 91 | pr_debug("%s: failed to get BTF of id %u, aborting\n", __func__, info.btf_id); | ||
| 92 | return -1; | ||
| 93 | } | 216 | } |
| 94 | func_info_rec_size = info.func_info_rec_size; | 217 | if (btf__get_from_id(info->btf_id, &btf)) { |
| 95 | func_infos = calloc(sub_prog_cnt, func_info_rec_size); | 218 | pr_debug("%s: failed to get BTF of id %u, aborting\n", __func__, info->btf_id); |
| 96 | if (!func_infos) { | 219 | err = -1; |
| 97 | pr_debug("%s: failed to allocate memory for func_infos, aborting\n", __func__); | 220 | btf = NULL; |
| 98 | return -1; | 221 | goto out; |
| 99 | } | 222 | } |
| 100 | has_btf = true; | 223 | perf_env__fetch_btf(env, info->btf_id, btf); |
| 101 | } | ||
| 102 | |||
| 103 | /* | ||
| 104 | * We need address, length, and tag for each sub program. | ||
| 105 | * Allocate memory and call bpf_obj_get_info_by_fd() again | ||
| 106 | */ | ||
| 107 | prog_addrs = calloc(sub_prog_cnt, sizeof(u64)); | ||
| 108 | if (!prog_addrs) { | ||
| 109 | pr_debug("%s: failed to allocate memory for prog_addrs, aborting\n", __func__); | ||
| 110 | goto out; | ||
| 111 | } | ||
| 112 | prog_lens = calloc(sub_prog_cnt, sizeof(u32)); | ||
| 113 | if (!prog_lens) { | ||
| 114 | pr_debug("%s: failed to allocate memory for prog_lens, aborting\n", __func__); | ||
| 115 | goto out; | ||
| 116 | } | ||
| 117 | prog_tags = calloc(sub_prog_cnt, BPF_TAG_SIZE); | ||
| 118 | if (!prog_tags) { | ||
| 119 | pr_debug("%s: failed to allocate memory for prog_tags, aborting\n", __func__); | ||
| 120 | goto out; | ||
| 121 | } | ||
| 122 | |||
| 123 | memset(&info, 0, sizeof(info)); | ||
| 124 | info.nr_jited_ksyms = sub_prog_cnt; | ||
| 125 | info.nr_jited_func_lens = sub_prog_cnt; | ||
| 126 | info.nr_prog_tags = sub_prog_cnt; | ||
| 127 | info.jited_ksyms = ptr_to_u64(prog_addrs); | ||
| 128 | info.jited_func_lens = ptr_to_u64(prog_lens); | ||
| 129 | info.prog_tags = ptr_to_u64(prog_tags); | ||
| 130 | info_len = sizeof(info); | ||
| 131 | if (has_btf) { | ||
| 132 | info.nr_func_info = sub_prog_cnt; | ||
| 133 | info.func_info_rec_size = func_info_rec_size; | ||
| 134 | info.func_info = ptr_to_u64(func_infos); | ||
| 135 | } | ||
| 136 | |||
| 137 | err = bpf_obj_get_info_by_fd(fd, &info, &info_len); | ||
| 138 | if (err) { | ||
| 139 | pr_debug("%s: failed to get BPF program info, aborting\n", __func__); | ||
| 140 | goto out; | ||
| 141 | } | 224 | } |
| 142 | 225 | ||
| 143 | /* Synthesize PERF_RECORD_KSYMBOL */ | 226 | /* Synthesize PERF_RECORD_KSYMBOL */ |
| 144 | for (i = 0; i < sub_prog_cnt; i++) { | 227 | for (i = 0; i < sub_prog_cnt; i++) { |
| 145 | const struct bpf_func_info *finfo; | 228 | __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); |
| 146 | const char *short_name = NULL; | 229 | __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); |
| 147 | const struct btf_type *t; | ||
| 148 | int name_len; | 230 | int name_len; |
| 149 | 231 | ||
| 150 | *ksymbol_event = (struct ksymbol_event){ | 232 | *ksymbol_event = (struct ksymbol_event){ |
| @@ -157,26 +239,9 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool, | |||
| 157 | .ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF, | 239 | .ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF, |
| 158 | .flags = 0, | 240 | .flags = 0, |
| 159 | }; | 241 | }; |
| 160 | name_len = snprintf(ksymbol_event->name, KSYM_NAME_LEN, | ||
| 161 | "bpf_prog_"); | ||
| 162 | name_len += snprintf_hex(ksymbol_event->name + name_len, | ||
| 163 | KSYM_NAME_LEN - name_len, | ||
| 164 | prog_tags[i], BPF_TAG_SIZE); | ||
| 165 | if (has_btf) { | ||
| 166 | finfo = func_infos + i * info.func_info_rec_size; | ||
| 167 | t = btf__type_by_id(btf, finfo->type_id); | ||
| 168 | short_name = btf__name_by_offset(btf, t->name_off); | ||
| 169 | } else if (i == 0 && sub_prog_cnt == 1) { | ||
| 170 | /* no subprog */ | ||
| 171 | if (info.name[0]) | ||
| 172 | short_name = info.name; | ||
| 173 | } else | ||
| 174 | short_name = "F"; | ||
| 175 | if (short_name) | ||
| 176 | name_len += snprintf(ksymbol_event->name + name_len, | ||
| 177 | KSYM_NAME_LEN - name_len, | ||
| 178 | "_%s", short_name); | ||
| 179 | 242 | ||
| 243 | name_len = synthesize_bpf_prog_name(ksymbol_event->name, | ||
| 244 | KSYM_NAME_LEN, info, btf, i); | ||
| 180 | ksymbol_event->header.size += PERF_ALIGN(name_len + 1, | 245 | ksymbol_event->header.size += PERF_ALIGN(name_len + 1, |
| 181 | sizeof(u64)); | 246 | sizeof(u64)); |
| 182 | 247 | ||
| @@ -186,8 +251,8 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool, | |||
| 186 | machine, process); | 251 | machine, process); |
| 187 | } | 252 | } |
| 188 | 253 | ||
| 189 | /* Synthesize PERF_RECORD_BPF_EVENT */ | 254 | if (!opts->no_bpf_event) { |
| 190 | if (opts->bpf_event) { | 255 | /* Synthesize PERF_RECORD_BPF_EVENT */ |
| 191 | *bpf_event = (struct bpf_event){ | 256 | *bpf_event = (struct bpf_event){ |
| 192 | .header = { | 257 | .header = { |
| 193 | .type = PERF_RECORD_BPF_EVENT, | 258 | .type = PERF_RECORD_BPF_EVENT, |
| @@ -195,25 +260,38 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool, | |||
| 195 | }, | 260 | }, |
| 196 | .type = PERF_BPF_EVENT_PROG_LOAD, | 261 | .type = PERF_BPF_EVENT_PROG_LOAD, |
| 197 | .flags = 0, | 262 | .flags = 0, |
| 198 | .id = info.id, | 263 | .id = info->id, |
| 199 | }; | 264 | }; |
| 200 | memcpy(bpf_event->tag, prog_tags[i], BPF_TAG_SIZE); | 265 | memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE); |
| 201 | memset((void *)event + event->header.size, 0, machine->id_hdr_size); | 266 | memset((void *)event + event->header.size, 0, machine->id_hdr_size); |
| 202 | event->header.size += machine->id_hdr_size; | 267 | event->header.size += machine->id_hdr_size; |
| 268 | |||
| 269 | /* save bpf_prog_info to env */ | ||
| 270 | info_node = malloc(sizeof(struct bpf_prog_info_node)); | ||
| 271 | if (!info_node) { | ||
| 272 | err = -1; | ||
| 273 | goto out; | ||
| 274 | } | ||
| 275 | |||
| 276 | info_node->info_linear = info_linear; | ||
| 277 | perf_env__insert_bpf_prog_info(env, info_node); | ||
| 278 | info_linear = NULL; | ||
| 279 | |||
| 280 | /* | ||
| 281 | * process after saving bpf_prog_info to env, so that | ||
| 282 | * required information is ready for look up | ||
| 283 | */ | ||
| 203 | err = perf_tool__process_synth_event(tool, event, | 284 | err = perf_tool__process_synth_event(tool, event, |
| 204 | machine, process); | 285 | machine, process); |
| 205 | } | 286 | } |
| 206 | 287 | ||
| 207 | out: | 288 | out: |
| 208 | free(prog_tags); | 289 | free(info_linear); |
| 209 | free(prog_lens); | ||
| 210 | free(prog_addrs); | ||
| 211 | free(func_infos); | ||
| 212 | free(btf); | 290 | free(btf); |
| 213 | return err ? -1 : 0; | 291 | return err ? -1 : 0; |
| 214 | } | 292 | } |
| 215 | 293 | ||
| 216 | int perf_event__synthesize_bpf_events(struct perf_tool *tool, | 294 | int perf_event__synthesize_bpf_events(struct perf_session *session, |
| 217 | perf_event__handler_t process, | 295 | perf_event__handler_t process, |
| 218 | struct machine *machine, | 296 | struct machine *machine, |
| 219 | struct record_opts *opts) | 297 | struct record_opts *opts) |
| @@ -247,7 +325,7 @@ int perf_event__synthesize_bpf_events(struct perf_tool *tool, | |||
| 247 | continue; | 325 | continue; |
| 248 | } | 326 | } |
| 249 | 327 | ||
| 250 | err = perf_event__synthesize_one_bpf_prog(tool, process, | 328 | err = perf_event__synthesize_one_bpf_prog(session, process, |
| 251 | machine, fd, | 329 | machine, fd, |
| 252 | event, opts); | 330 | event, opts); |
| 253 | close(fd); | 331 | close(fd); |
| @@ -261,3 +339,142 @@ int perf_event__synthesize_bpf_events(struct perf_tool *tool, | |||
| 261 | free(event); | 339 | free(event); |
| 262 | return err; | 340 | return err; |
| 263 | } | 341 | } |
| 342 | |||
| 343 | static void perf_env__add_bpf_info(struct perf_env *env, u32 id) | ||
| 344 | { | ||
| 345 | struct bpf_prog_info_linear *info_linear; | ||
| 346 | struct bpf_prog_info_node *info_node; | ||
| 347 | struct btf *btf = NULL; | ||
| 348 | u64 arrays; | ||
| 349 | u32 btf_id; | ||
| 350 | int fd; | ||
| 351 | |||
| 352 | fd = bpf_prog_get_fd_by_id(id); | ||
| 353 | if (fd < 0) | ||
| 354 | return; | ||
| 355 | |||
| 356 | arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS; | ||
| 357 | arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; | ||
| 358 | arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; | ||
| 359 | arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS; | ||
| 360 | arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS; | ||
| 361 | arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; | ||
| 362 | arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; | ||
| 363 | |||
| 364 | info_linear = bpf_program__get_prog_info_linear(fd, arrays); | ||
| 365 | if (IS_ERR_OR_NULL(info_linear)) { | ||
| 366 | pr_debug("%s: failed to get BPF program info. aborting\n", __func__); | ||
| 367 | goto out; | ||
| 368 | } | ||
| 369 | |||
| 370 | btf_id = info_linear->info.btf_id; | ||
| 371 | |||
| 372 | info_node = malloc(sizeof(struct bpf_prog_info_node)); | ||
| 373 | if (info_node) { | ||
| 374 | info_node->info_linear = info_linear; | ||
| 375 | perf_env__insert_bpf_prog_info(env, info_node); | ||
| 376 | } else | ||
| 377 | free(info_linear); | ||
| 378 | |||
| 379 | if (btf_id == 0) | ||
| 380 | goto out; | ||
| 381 | |||
| 382 | if (btf__get_from_id(btf_id, &btf)) { | ||
| 383 | pr_debug("%s: failed to get BTF of id %u, aborting\n", | ||
| 384 | __func__, btf_id); | ||
| 385 | goto out; | ||
| 386 | } | ||
| 387 | perf_env__fetch_btf(env, btf_id, btf); | ||
| 388 | |||
| 389 | out: | ||
| 390 | free(btf); | ||
| 391 | close(fd); | ||
| 392 | } | ||
| 393 | |||
| 394 | static int bpf_event__sb_cb(union perf_event *event, void *data) | ||
| 395 | { | ||
| 396 | struct perf_env *env = data; | ||
| 397 | |||
| 398 | if (event->header.type != PERF_RECORD_BPF_EVENT) | ||
| 399 | return -1; | ||
| 400 | |||
| 401 | switch (event->bpf_event.type) { | ||
| 402 | case PERF_BPF_EVENT_PROG_LOAD: | ||
| 403 | perf_env__add_bpf_info(env, event->bpf_event.id); | ||
| 404 | |||
| 405 | case PERF_BPF_EVENT_PROG_UNLOAD: | ||
| 406 | /* | ||
| 407 | * Do not free bpf_prog_info and btf of the program here, | ||
| 408 | * as annotation still need them. They will be freed at | ||
| 409 | * the end of the session. | ||
| 410 | */ | ||
| 411 | break; | ||
| 412 | default: | ||
| 413 | pr_debug("unexpected bpf_event type of %d\n", | ||
| 414 | event->bpf_event.type); | ||
| 415 | break; | ||
| 416 | } | ||
| 417 | |||
| 418 | return 0; | ||
| 419 | } | ||
| 420 | |||
| 421 | int bpf_event__add_sb_event(struct perf_evlist **evlist, | ||
| 422 | struct perf_env *env) | ||
| 423 | { | ||
| 424 | struct perf_event_attr attr = { | ||
| 425 | .type = PERF_TYPE_SOFTWARE, | ||
| 426 | .config = PERF_COUNT_SW_DUMMY, | ||
| 427 | .sample_id_all = 1, | ||
| 428 | .watermark = 1, | ||
| 429 | .bpf_event = 1, | ||
| 430 | .size = sizeof(attr), /* to capture ABI version */ | ||
| 431 | }; | ||
| 432 | |||
| 433 | /* | ||
| 434 | * Older gcc versions don't support designated initializers, like above, | ||
| 435 | * for unnamed union members, such as the following: | ||
| 436 | */ | ||
| 437 | attr.wakeup_watermark = 1; | ||
| 438 | |||
| 439 | return perf_evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env); | ||
| 440 | } | ||
| 441 | |||
| 442 | void bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, | ||
| 443 | struct perf_env *env, | ||
| 444 | FILE *fp) | ||
| 445 | { | ||
| 446 | __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); | ||
| 447 | __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); | ||
| 448 | char name[KSYM_NAME_LEN]; | ||
| 449 | struct btf *btf = NULL; | ||
| 450 | u32 sub_prog_cnt, i; | ||
| 451 | |||
| 452 | sub_prog_cnt = info->nr_jited_ksyms; | ||
| 453 | if (sub_prog_cnt != info->nr_prog_tags || | ||
| 454 | sub_prog_cnt != info->nr_jited_func_lens) | ||
| 455 | return; | ||
| 456 | |||
| 457 | if (info->btf_id) { | ||
| 458 | struct btf_node *node; | ||
| 459 | |||
| 460 | node = perf_env__find_btf(env, info->btf_id); | ||
| 461 | if (node) | ||
| 462 | btf = btf__new((__u8 *)(node->data), | ||
| 463 | node->data_size); | ||
| 464 | } | ||
| 465 | |||
| 466 | if (sub_prog_cnt == 1) { | ||
| 467 | synthesize_bpf_prog_name(name, KSYM_NAME_LEN, info, btf, 0); | ||
| 468 | fprintf(fp, "# bpf_prog_info %u: %s addr 0x%llx size %u\n", | ||
| 469 | info->id, name, prog_addrs[0], prog_lens[0]); | ||
| 470 | return; | ||
| 471 | } | ||
| 472 | |||
| 473 | fprintf(fp, "# bpf_prog_info %u:\n", info->id); | ||
| 474 | for (i = 0; i < sub_prog_cnt; i++) { | ||
| 475 | synthesize_bpf_prog_name(name, KSYM_NAME_LEN, info, btf, i); | ||
| 476 | |||
| 477 | fprintf(fp, "# \tsub_prog %u: %s addr 0x%llx size %u\n", | ||
| 478 | i, name, prog_addrs[i], prog_lens[i]); | ||
| 479 | } | ||
| 480 | } | ||
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index 7890067e1a37..04c33b3bfe28 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h | |||
| @@ -3,22 +3,45 @@ | |||
| 3 | #define __PERF_BPF_EVENT_H | 3 | #define __PERF_BPF_EVENT_H |
| 4 | 4 | ||
| 5 | #include <linux/compiler.h> | 5 | #include <linux/compiler.h> |
| 6 | #include <linux/rbtree.h> | ||
| 7 | #include <pthread.h> | ||
| 8 | #include <api/fd/array.h> | ||
| 6 | #include "event.h" | 9 | #include "event.h" |
| 10 | #include <stdio.h> | ||
| 7 | 11 | ||
| 8 | struct machine; | 12 | struct machine; |
| 9 | union perf_event; | 13 | union perf_event; |
| 14 | struct perf_env; | ||
| 10 | struct perf_sample; | 15 | struct perf_sample; |
| 11 | struct perf_tool; | ||
| 12 | struct record_opts; | 16 | struct record_opts; |
| 17 | struct evlist; | ||
| 18 | struct target; | ||
| 19 | |||
| 20 | struct bpf_prog_info_node { | ||
| 21 | struct bpf_prog_info_linear *info_linear; | ||
| 22 | struct rb_node rb_node; | ||
| 23 | }; | ||
| 24 | |||
| 25 | struct btf_node { | ||
| 26 | struct rb_node rb_node; | ||
| 27 | u32 id; | ||
| 28 | u32 data_size; | ||
| 29 | char data[]; | ||
| 30 | }; | ||
| 13 | 31 | ||
| 14 | #ifdef HAVE_LIBBPF_SUPPORT | 32 | #ifdef HAVE_LIBBPF_SUPPORT |
| 15 | int machine__process_bpf_event(struct machine *machine, union perf_event *event, | 33 | int machine__process_bpf_event(struct machine *machine, union perf_event *event, |
| 16 | struct perf_sample *sample); | 34 | struct perf_sample *sample); |
| 17 | 35 | ||
| 18 | int perf_event__synthesize_bpf_events(struct perf_tool *tool, | 36 | int perf_event__synthesize_bpf_events(struct perf_session *session, |
| 19 | perf_event__handler_t process, | 37 | perf_event__handler_t process, |
| 20 | struct machine *machine, | 38 | struct machine *machine, |
| 21 | struct record_opts *opts); | 39 | struct record_opts *opts); |
| 40 | int bpf_event__add_sb_event(struct perf_evlist **evlist, | ||
| 41 | struct perf_env *env); | ||
| 42 | void bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, | ||
| 43 | struct perf_env *env, | ||
| 44 | FILE *fp); | ||
| 22 | #else | 45 | #else |
| 23 | static inline int machine__process_bpf_event(struct machine *machine __maybe_unused, | 46 | static inline int machine__process_bpf_event(struct machine *machine __maybe_unused, |
| 24 | union perf_event *event __maybe_unused, | 47 | union perf_event *event __maybe_unused, |
| @@ -27,12 +50,25 @@ static inline int machine__process_bpf_event(struct machine *machine __maybe_unu | |||
| 27 | return 0; | 50 | return 0; |
| 28 | } | 51 | } |
| 29 | 52 | ||
| 30 | static inline int perf_event__synthesize_bpf_events(struct perf_tool *tool __maybe_unused, | 53 | static inline int perf_event__synthesize_bpf_events(struct perf_session *session __maybe_unused, |
| 31 | perf_event__handler_t process __maybe_unused, | 54 | perf_event__handler_t process __maybe_unused, |
| 32 | struct machine *machine __maybe_unused, | 55 | struct machine *machine __maybe_unused, |
| 33 | struct record_opts *opts __maybe_unused) | 56 | struct record_opts *opts __maybe_unused) |
| 34 | { | 57 | { |
| 35 | return 0; | 58 | return 0; |
| 36 | } | 59 | } |
| 60 | |||
| 61 | static inline int bpf_event__add_sb_event(struct perf_evlist **evlist __maybe_unused, | ||
| 62 | struct perf_env *env __maybe_unused) | ||
| 63 | { | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | static inline void bpf_event__print_bpf_prog_info(struct bpf_prog_info *info __maybe_unused, | ||
| 68 | struct perf_env *env __maybe_unused, | ||
| 69 | FILE *fp __maybe_unused) | ||
| 70 | { | ||
| 71 | |||
| 72 | } | ||
| 37 | #endif // HAVE_LIBBPF_SUPPORT | 73 | #endif // HAVE_LIBBPF_SUPPORT |
| 38 | #endif | 74 | #endif |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index bff0d17920ed..0c5517a8d0b7 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
| @@ -185,6 +185,7 @@ char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size) | |||
| 185 | return bf; | 185 | return bf; |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | /* The caller is responsible to free the returned buffer. */ | ||
| 188 | char *build_id_cache__origname(const char *sbuild_id) | 189 | char *build_id_cache__origname(const char *sbuild_id) |
| 189 | { | 190 | { |
| 190 | char *linkname; | 191 | char *linkname; |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index fa092511c52b..7e3c1b60120c 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
| @@ -633,11 +633,10 @@ static int collect_config(const char *var, const char *value, | |||
| 633 | } | 633 | } |
| 634 | 634 | ||
| 635 | ret = set_value(item, value); | 635 | ret = set_value(item, value); |
| 636 | return ret; | ||
| 637 | 636 | ||
| 638 | out_free: | 637 | out_free: |
| 639 | free(key); | 638 | free(key); |
| 640 | return -1; | 639 | return ret; |
| 641 | } | 640 | } |
| 642 | 641 | ||
| 643 | int perf_config_set__collect(struct perf_config_set *set, const char *file_name, | 642 | int perf_config_set__collect(struct perf_config_set *set, const char *file_name, |
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index ba4c623cd8de..39fe21e1cf93 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | |||
| @@ -387,6 +387,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_decoder *decoder, | |||
| 387 | break; | 387 | break; |
| 388 | case OCSD_INSTR_ISB: | 388 | case OCSD_INSTR_ISB: |
| 389 | case OCSD_INSTR_DSB_DMB: | 389 | case OCSD_INSTR_DSB_DMB: |
| 390 | case OCSD_INSTR_WFI_WFE: | ||
| 390 | case OCSD_INSTR_OTHER: | 391 | case OCSD_INSTR_OTHER: |
| 391 | default: | 392 | default: |
| 392 | packet->last_instr_taken_branch = false; | 393 | packet->last_instr_taken_branch = false; |
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index e098e189f93e..6a64f713710d 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "data.h" | 14 | #include "data.h" |
| 15 | #include "util.h" | 15 | #include "util.h" |
| 16 | #include "debug.h" | 16 | #include "debug.h" |
| 17 | #include "header.h" | ||
| 17 | 18 | ||
| 18 | static void close_dir(struct perf_data_file *files, int nr) | 19 | static void close_dir(struct perf_data_file *files, int nr) |
| 19 | { | 20 | { |
| @@ -34,12 +35,16 @@ int perf_data__create_dir(struct perf_data *data, int nr) | |||
| 34 | struct perf_data_file *files = NULL; | 35 | struct perf_data_file *files = NULL; |
| 35 | int i, ret = -1; | 36 | int i, ret = -1; |
| 36 | 37 | ||
| 38 | if (WARN_ON(!data->is_dir)) | ||
| 39 | return -EINVAL; | ||
| 40 | |||
| 37 | files = zalloc(nr * sizeof(*files)); | 41 | files = zalloc(nr * sizeof(*files)); |
| 38 | if (!files) | 42 | if (!files) |
| 39 | return -ENOMEM; | 43 | return -ENOMEM; |
| 40 | 44 | ||
| 41 | data->dir.files = files; | 45 | data->dir.version = PERF_DIR_VERSION; |
| 42 | data->dir.nr = nr; | 46 | data->dir.files = files; |
| 47 | data->dir.nr = nr; | ||
| 43 | 48 | ||
| 44 | for (i = 0; i < nr; i++) { | 49 | for (i = 0; i < nr; i++) { |
| 45 | struct perf_data_file *file = &files[i]; | 50 | struct perf_data_file *file = &files[i]; |
| @@ -69,6 +74,13 @@ int perf_data__open_dir(struct perf_data *data) | |||
| 69 | DIR *dir; | 74 | DIR *dir; |
| 70 | int nr = 0; | 75 | int nr = 0; |
| 71 | 76 | ||
| 77 | if (WARN_ON(!data->is_dir)) | ||
| 78 | return -EINVAL; | ||
| 79 | |||
| 80 | /* The version is provided by DIR_FORMAT feature. */ | ||
| 81 | if (WARN_ON(data->dir.version != PERF_DIR_VERSION)) | ||
| 82 | return -1; | ||
| 83 | |||
| 72 | dir = opendir(data->path); | 84 | dir = opendir(data->path); |
| 73 | if (!dir) | 85 | if (!dir) |
| 74 | return -EINVAL; | 86 | return -EINVAL; |
| @@ -118,6 +130,26 @@ out_err: | |||
| 118 | return ret; | 130 | return ret; |
| 119 | } | 131 | } |
| 120 | 132 | ||
| 133 | int perf_data__update_dir(struct perf_data *data) | ||
| 134 | { | ||
| 135 | int i; | ||
| 136 | |||
| 137 | if (WARN_ON(!data->is_dir)) | ||
| 138 | return -EINVAL; | ||
| 139 | |||
| 140 | for (i = 0; i < data->dir.nr; i++) { | ||
| 141 | struct perf_data_file *file = &data->dir.files[i]; | ||
| 142 | struct stat st; | ||
| 143 | |||
| 144 | if (fstat(file->fd, &st)) | ||
| 145 | return -1; | ||
| 146 | |||
| 147 | file->size = st.st_size; | ||
| 148 | } | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 121 | static bool check_pipe(struct perf_data *data) | 153 | static bool check_pipe(struct perf_data *data) |
| 122 | { | 154 | { |
| 123 | struct stat st; | 155 | struct stat st; |
| @@ -173,6 +205,16 @@ static int check_backup(struct perf_data *data) | |||
| 173 | return 0; | 205 | return 0; |
| 174 | } | 206 | } |
| 175 | 207 | ||
| 208 | static bool is_dir(struct perf_data *data) | ||
| 209 | { | ||
| 210 | struct stat st; | ||
| 211 | |||
| 212 | if (stat(data->path, &st)) | ||
| 213 | return false; | ||
| 214 | |||
| 215 | return (st.st_mode & S_IFMT) == S_IFDIR; | ||
| 216 | } | ||
| 217 | |||
| 176 | static int open_file_read(struct perf_data *data) | 218 | static int open_file_read(struct perf_data *data) |
| 177 | { | 219 | { |
| 178 | struct stat st; | 220 | struct stat st; |
| @@ -254,6 +296,30 @@ static int open_file_dup(struct perf_data *data) | |||
| 254 | return open_file(data); | 296 | return open_file(data); |
| 255 | } | 297 | } |
| 256 | 298 | ||
| 299 | static int open_dir(struct perf_data *data) | ||
| 300 | { | ||
| 301 | int ret; | ||
| 302 | |||
| 303 | /* | ||
| 304 | * So far we open only the header, so we can read the data version and | ||
| 305 | * layout. | ||
| 306 | */ | ||
| 307 | if (asprintf(&data->file.path, "%s/header", data->path) < 0) | ||
| 308 | return -1; | ||
| 309 | |||
| 310 | if (perf_data__is_write(data) && | ||
| 311 | mkdir(data->path, S_IRWXU) < 0) | ||
| 312 | return -1; | ||
| 313 | |||
| 314 | ret = open_file(data); | ||
| 315 | |||
| 316 | /* Cleanup whatever we managed to create so far. */ | ||
| 317 | if (ret && perf_data__is_write(data)) | ||
| 318 | rm_rf_perf_data(data->path); | ||
| 319 | |||
| 320 | return ret; | ||
| 321 | } | ||
| 322 | |||
| 257 | int perf_data__open(struct perf_data *data) | 323 | int perf_data__open(struct perf_data *data) |
| 258 | { | 324 | { |
| 259 | if (check_pipe(data)) | 325 | if (check_pipe(data)) |
| @@ -265,11 +331,18 @@ int perf_data__open(struct perf_data *data) | |||
| 265 | if (check_backup(data)) | 331 | if (check_backup(data)) |
| 266 | return -1; | 332 | return -1; |
| 267 | 333 | ||
| 268 | return open_file_dup(data); | 334 | if (perf_data__is_read(data)) |
| 335 | data->is_dir = is_dir(data); | ||
| 336 | |||
| 337 | return perf_data__is_dir(data) ? | ||
| 338 | open_dir(data) : open_file_dup(data); | ||
| 269 | } | 339 | } |
| 270 | 340 | ||
| 271 | void perf_data__close(struct perf_data *data) | 341 | void perf_data__close(struct perf_data *data) |
| 272 | { | 342 | { |
| 343 | if (perf_data__is_dir(data)) | ||
| 344 | perf_data__close_dir(data); | ||
| 345 | |||
| 273 | zfree(&data->file.path); | 346 | zfree(&data->file.path); |
| 274 | close(data->file.fd); | 347 | close(data->file.fd); |
| 275 | } | 348 | } |
| @@ -288,9 +361,9 @@ ssize_t perf_data__write(struct perf_data *data, | |||
| 288 | 361 | ||
| 289 | int perf_data__switch(struct perf_data *data, | 362 | int perf_data__switch(struct perf_data *data, |
| 290 | const char *postfix, | 363 | const char *postfix, |
| 291 | size_t pos, bool at_exit) | 364 | size_t pos, bool at_exit, |
| 365 | char **new_filepath) | ||
| 292 | { | 366 | { |
| 293 | char *new_filepath; | ||
| 294 | int ret; | 367 | int ret; |
| 295 | 368 | ||
| 296 | if (check_pipe(data)) | 369 | if (check_pipe(data)) |
| @@ -298,15 +371,15 @@ int perf_data__switch(struct perf_data *data, | |||
| 298 | if (perf_data__is_read(data)) | 371 | if (perf_data__is_read(data)) |
| 299 | return -EINVAL; | 372 | return -EINVAL; |
| 300 | 373 | ||
| 301 | if (asprintf(&new_filepath, "%s.%s", data->path, postfix) < 0) | 374 | if (asprintf(new_filepath, "%s.%s", data->path, postfix) < 0) |
| 302 | return -ENOMEM; | 375 | return -ENOMEM; |
| 303 | 376 | ||
| 304 | /* | 377 | /* |
| 305 | * Only fire a warning, don't return error, continue fill | 378 | * Only fire a warning, don't return error, continue fill |
| 306 | * original file. | 379 | * original file. |
| 307 | */ | 380 | */ |
| 308 | if (rename(data->path, new_filepath)) | 381 | if (rename(data->path, *new_filepath)) |
| 309 | pr_warning("Failed to rename %s to %s\n", data->path, new_filepath); | 382 | pr_warning("Failed to rename %s to %s\n", data->path, *new_filepath); |
| 310 | 383 | ||
| 311 | if (!at_exit) { | 384 | if (!at_exit) { |
| 312 | close(data->file.fd); | 385 | close(data->file.fd); |
| @@ -323,6 +396,22 @@ int perf_data__switch(struct perf_data *data, | |||
| 323 | } | 396 | } |
| 324 | ret = data->file.fd; | 397 | ret = data->file.fd; |
| 325 | out: | 398 | out: |
| 326 | free(new_filepath); | ||
| 327 | return ret; | 399 | return ret; |
| 328 | } | 400 | } |
| 401 | |||
| 402 | unsigned long perf_data__size(struct perf_data *data) | ||
| 403 | { | ||
| 404 | u64 size = data->file.size; | ||
| 405 | int i; | ||
| 406 | |||
| 407 | if (!data->is_dir) | ||
| 408 | return size; | ||
| 409 | |||
| 410 | for (i = 0; i < data->dir.nr; i++) { | ||
| 411 | struct perf_data_file *file = &data->dir.files[i]; | ||
| 412 | |||
| 413 | size += file->size; | ||
| 414 | } | ||
| 415 | |||
| 416 | return size; | ||
| 417 | } | ||
diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 14b47be2bd69..259868a39019 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h | |||
| @@ -19,10 +19,12 @@ struct perf_data { | |||
| 19 | const char *path; | 19 | const char *path; |
| 20 | struct perf_data_file file; | 20 | struct perf_data_file file; |
| 21 | bool is_pipe; | 21 | bool is_pipe; |
| 22 | bool is_dir; | ||
| 22 | bool force; | 23 | bool force; |
| 23 | enum perf_data_mode mode; | 24 | enum perf_data_mode mode; |
| 24 | 25 | ||
| 25 | struct { | 26 | struct { |
| 27 | u64 version; | ||
| 26 | struct perf_data_file *files; | 28 | struct perf_data_file *files; |
| 27 | int nr; | 29 | int nr; |
| 28 | } dir; | 30 | } dir; |
| @@ -43,14 +45,14 @@ static inline int perf_data__is_pipe(struct perf_data *data) | |||
| 43 | return data->is_pipe; | 45 | return data->is_pipe; |
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | static inline int perf_data__fd(struct perf_data *data) | 48 | static inline bool perf_data__is_dir(struct perf_data *data) |
| 47 | { | 49 | { |
| 48 | return data->file.fd; | 50 | return data->is_dir; |
| 49 | } | 51 | } |
| 50 | 52 | ||
| 51 | static inline unsigned long perf_data__size(struct perf_data *data) | 53 | static inline int perf_data__fd(struct perf_data *data) |
| 52 | { | 54 | { |
| 53 | return data->file.size; | 55 | return data->file.fd; |
| 54 | } | 56 | } |
| 55 | 57 | ||
| 56 | int perf_data__open(struct perf_data *data); | 58 | int perf_data__open(struct perf_data *data); |
| @@ -68,9 +70,11 @@ ssize_t perf_data_file__write(struct perf_data_file *file, | |||
| 68 | */ | 70 | */ |
| 69 | int perf_data__switch(struct perf_data *data, | 71 | int perf_data__switch(struct perf_data *data, |
| 70 | const char *postfix, | 72 | const char *postfix, |
| 71 | size_t pos, bool at_exit); | 73 | size_t pos, bool at_exit, char **new_filepath); |
| 72 | 74 | ||
| 73 | int perf_data__create_dir(struct perf_data *data, int nr); | 75 | int perf_data__create_dir(struct perf_data *data, int nr); |
| 74 | int perf_data__open_dir(struct perf_data *data); | 76 | int perf_data__open_dir(struct perf_data *data); |
| 75 | void perf_data__close_dir(struct perf_data *data); | 77 | void perf_data__close_dir(struct perf_data *data); |
| 78 | int perf_data__update_dir(struct perf_data *data); | ||
| 79 | unsigned long perf_data__size(struct perf_data *data); | ||
| 76 | #endif /* __PERF_DATA_H */ | 80 | #endif /* __PERF_DATA_H */ |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index ba58ba603b69..e059976d9d93 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
| @@ -184,6 +184,7 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
| 184 | case DSO_BINARY_TYPE__KALLSYMS: | 184 | case DSO_BINARY_TYPE__KALLSYMS: |
| 185 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | 185 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: |
| 186 | case DSO_BINARY_TYPE__JAVA_JIT: | 186 | case DSO_BINARY_TYPE__JAVA_JIT: |
| 187 | case DSO_BINARY_TYPE__BPF_PROG_INFO: | ||
| 187 | case DSO_BINARY_TYPE__NOT_FOUND: | 188 | case DSO_BINARY_TYPE__NOT_FOUND: |
| 188 | ret = -1; | 189 | ret = -1; |
| 189 | break; | 190 | break; |
| @@ -1141,28 +1142,34 @@ void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated) | |||
| 1141 | 1142 | ||
| 1142 | static void dso__set_basename(struct dso *dso) | 1143 | static void dso__set_basename(struct dso *dso) |
| 1143 | { | 1144 | { |
| 1144 | /* | 1145 | char *base, *lname; |
| 1145 | * basename() may modify path buffer, so we must pass | 1146 | int tid; |
| 1146 | * a copy. | ||
| 1147 | */ | ||
| 1148 | char *base, *lname = strdup(dso->long_name); | ||
| 1149 | 1147 | ||
| 1150 | if (!lname) | 1148 | if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) { |
| 1151 | return; | 1149 | if (asprintf(&base, "[JIT] tid %d", tid) < 0) |
| 1152 | 1150 | return; | |
| 1153 | /* | 1151 | } else { |
| 1154 | * basename() may return a pointer to internal | 1152 | /* |
| 1155 | * storage which is reused in subsequent calls | 1153 | * basename() may modify path buffer, so we must pass |
| 1156 | * so copy the result. | 1154 | * a copy. |
| 1157 | */ | 1155 | */ |
| 1158 | base = strdup(basename(lname)); | 1156 | lname = strdup(dso->long_name); |
| 1157 | if (!lname) | ||
| 1158 | return; | ||
| 1159 | 1159 | ||
| 1160 | free(lname); | 1160 | /* |
| 1161 | * basename() may return a pointer to internal | ||
| 1162 | * storage which is reused in subsequent calls | ||
| 1163 | * so copy the result. | ||
| 1164 | */ | ||
| 1165 | base = strdup(basename(lname)); | ||
| 1161 | 1166 | ||
| 1162 | if (!base) | 1167 | free(lname); |
| 1163 | return; | ||
| 1164 | 1168 | ||
| 1165 | dso__set_short_name(dso, base, true); | 1169 | if (!base) |
| 1170 | return; | ||
| 1171 | } | ||
| 1172 | dso__set_short_name(dso, base, true); | ||
| 1166 | } | 1173 | } |
| 1167 | 1174 | ||
| 1168 | int dso__name_len(const struct dso *dso) | 1175 | int dso__name_len(const struct dso *dso) |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index bb417c54c25a..6e3f63781e51 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | struct machine; | 15 | struct machine; |
| 16 | struct map; | 16 | struct map; |
| 17 | struct perf_env; | ||
| 17 | 18 | ||
| 18 | enum dso_binary_type { | 19 | enum dso_binary_type { |
| 19 | DSO_BINARY_TYPE__KALLSYMS = 0, | 20 | DSO_BINARY_TYPE__KALLSYMS = 0, |
| @@ -35,6 +36,7 @@ enum dso_binary_type { | |||
| 35 | DSO_BINARY_TYPE__KCORE, | 36 | DSO_BINARY_TYPE__KCORE, |
| 36 | DSO_BINARY_TYPE__GUEST_KCORE, | 37 | DSO_BINARY_TYPE__GUEST_KCORE, |
| 37 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 38 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
| 39 | DSO_BINARY_TYPE__BPF_PROG_INFO, | ||
| 38 | DSO_BINARY_TYPE__NOT_FOUND, | 40 | DSO_BINARY_TYPE__NOT_FOUND, |
| 39 | }; | 41 | }; |
| 40 | 42 | ||
| @@ -189,6 +191,12 @@ struct dso { | |||
| 189 | u64 debug_frame_offset; | 191 | u64 debug_frame_offset; |
| 190 | u64 eh_frame_hdr_offset; | 192 | u64 eh_frame_hdr_offset; |
| 191 | } data; | 193 | } data; |
| 194 | /* bpf prog information */ | ||
| 195 | struct { | ||
| 196 | u32 id; | ||
| 197 | u32 sub_id; | ||
| 198 | struct perf_env *env; | ||
| 199 | } bpf_prog; | ||
| 192 | 200 | ||
| 193 | union { /* Tool specific area */ | 201 | union { /* Tool specific area */ |
| 194 | void *priv; | 202 | void *priv; |
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 4c23779e271a..c6351b557bb0 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c | |||
| @@ -3,15 +3,163 @@ | |||
| 3 | #include "env.h" | 3 | #include "env.h" |
| 4 | #include "sane_ctype.h" | 4 | #include "sane_ctype.h" |
| 5 | #include "util.h" | 5 | #include "util.h" |
| 6 | #include "bpf-event.h" | ||
| 6 | #include <errno.h> | 7 | #include <errno.h> |
| 7 | #include <sys/utsname.h> | 8 | #include <sys/utsname.h> |
| 9 | #include <bpf/libbpf.h> | ||
| 8 | 10 | ||
| 9 | struct perf_env perf_env; | 11 | struct perf_env perf_env; |
| 10 | 12 | ||
| 13 | void perf_env__insert_bpf_prog_info(struct perf_env *env, | ||
| 14 | struct bpf_prog_info_node *info_node) | ||
| 15 | { | ||
| 16 | __u32 prog_id = info_node->info_linear->info.id; | ||
| 17 | struct bpf_prog_info_node *node; | ||
| 18 | struct rb_node *parent = NULL; | ||
| 19 | struct rb_node **p; | ||
| 20 | |||
| 21 | down_write(&env->bpf_progs.lock); | ||
| 22 | p = &env->bpf_progs.infos.rb_node; | ||
| 23 | |||
| 24 | while (*p != NULL) { | ||
| 25 | parent = *p; | ||
| 26 | node = rb_entry(parent, struct bpf_prog_info_node, rb_node); | ||
| 27 | if (prog_id < node->info_linear->info.id) { | ||
| 28 | p = &(*p)->rb_left; | ||
| 29 | } else if (prog_id > node->info_linear->info.id) { | ||
| 30 | p = &(*p)->rb_right; | ||
| 31 | } else { | ||
| 32 | pr_debug("duplicated bpf prog info %u\n", prog_id); | ||
| 33 | goto out; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | rb_link_node(&info_node->rb_node, parent, p); | ||
| 38 | rb_insert_color(&info_node->rb_node, &env->bpf_progs.infos); | ||
| 39 | env->bpf_progs.infos_cnt++; | ||
| 40 | out: | ||
| 41 | up_write(&env->bpf_progs.lock); | ||
| 42 | } | ||
| 43 | |||
| 44 | struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, | ||
| 45 | __u32 prog_id) | ||
| 46 | { | ||
| 47 | struct bpf_prog_info_node *node = NULL; | ||
| 48 | struct rb_node *n; | ||
| 49 | |||
| 50 | down_read(&env->bpf_progs.lock); | ||
| 51 | n = env->bpf_progs.infos.rb_node; | ||
| 52 | |||
| 53 | while (n) { | ||
| 54 | node = rb_entry(n, struct bpf_prog_info_node, rb_node); | ||
| 55 | if (prog_id < node->info_linear->info.id) | ||
| 56 | n = n->rb_left; | ||
| 57 | else if (prog_id > node->info_linear->info.id) | ||
| 58 | n = n->rb_right; | ||
| 59 | else | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 63 | up_read(&env->bpf_progs.lock); | ||
| 64 | return node; | ||
| 65 | } | ||
| 66 | |||
| 67 | void perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node) | ||
| 68 | { | ||
| 69 | struct rb_node *parent = NULL; | ||
| 70 | __u32 btf_id = btf_node->id; | ||
| 71 | struct btf_node *node; | ||
| 72 | struct rb_node **p; | ||
| 73 | |||
| 74 | down_write(&env->bpf_progs.lock); | ||
| 75 | p = &env->bpf_progs.btfs.rb_node; | ||
| 76 | |||
| 77 | while (*p != NULL) { | ||
| 78 | parent = *p; | ||
| 79 | node = rb_entry(parent, struct btf_node, rb_node); | ||
| 80 | if (btf_id < node->id) { | ||
| 81 | p = &(*p)->rb_left; | ||
| 82 | } else if (btf_id > node->id) { | ||
| 83 | p = &(*p)->rb_right; | ||
| 84 | } else { | ||
| 85 | pr_debug("duplicated btf %u\n", btf_id); | ||
| 86 | goto out; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | rb_link_node(&btf_node->rb_node, parent, p); | ||
| 91 | rb_insert_color(&btf_node->rb_node, &env->bpf_progs.btfs); | ||
| 92 | env->bpf_progs.btfs_cnt++; | ||
| 93 | out: | ||
| 94 | up_write(&env->bpf_progs.lock); | ||
| 95 | } | ||
| 96 | |||
| 97 | struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id) | ||
| 98 | { | ||
| 99 | struct btf_node *node = NULL; | ||
| 100 | struct rb_node *n; | ||
| 101 | |||
| 102 | down_read(&env->bpf_progs.lock); | ||
| 103 | n = env->bpf_progs.btfs.rb_node; | ||
| 104 | |||
| 105 | while (n) { | ||
| 106 | node = rb_entry(n, struct btf_node, rb_node); | ||
| 107 | if (btf_id < node->id) | ||
| 108 | n = n->rb_left; | ||
| 109 | else if (btf_id > node->id) | ||
| 110 | n = n->rb_right; | ||
| 111 | else | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | |||
| 115 | up_read(&env->bpf_progs.lock); | ||
| 116 | return node; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* purge data in bpf_progs.infos tree */ | ||
| 120 | static void perf_env__purge_bpf(struct perf_env *env) | ||
| 121 | { | ||
| 122 | struct rb_root *root; | ||
| 123 | struct rb_node *next; | ||
| 124 | |||
| 125 | down_write(&env->bpf_progs.lock); | ||
| 126 | |||
| 127 | root = &env->bpf_progs.infos; | ||
| 128 | next = rb_first(root); | ||
| 129 | |||
| 130 | while (next) { | ||
| 131 | struct bpf_prog_info_node *node; | ||
| 132 | |||
| 133 | node = rb_entry(next, struct bpf_prog_info_node, rb_node); | ||
| 134 | next = rb_next(&node->rb_node); | ||
| 135 | rb_erase(&node->rb_node, root); | ||
| 136 | free(node); | ||
| 137 | } | ||
| 138 | |||
| 139 | env->bpf_progs.infos_cnt = 0; | ||
| 140 | |||
| 141 | root = &env->bpf_progs.btfs; | ||
| 142 | next = rb_first(root); | ||
| 143 | |||
| 144 | while (next) { | ||
| 145 | struct btf_node *node; | ||
| 146 | |||
| 147 | node = rb_entry(next, struct btf_node, rb_node); | ||
| 148 | next = rb_next(&node->rb_node); | ||
| 149 | rb_erase(&node->rb_node, root); | ||
| 150 | free(node); | ||
| 151 | } | ||
| 152 | |||
| 153 | env->bpf_progs.btfs_cnt = 0; | ||
| 154 | |||
| 155 | up_write(&env->bpf_progs.lock); | ||
| 156 | } | ||
| 157 | |||
| 11 | void perf_env__exit(struct perf_env *env) | 158 | void perf_env__exit(struct perf_env *env) |
| 12 | { | 159 | { |
| 13 | int i; | 160 | int i; |
| 14 | 161 | ||
| 162 | perf_env__purge_bpf(env); | ||
| 15 | zfree(&env->hostname); | 163 | zfree(&env->hostname); |
| 16 | zfree(&env->os_release); | 164 | zfree(&env->os_release); |
| 17 | zfree(&env->version); | 165 | zfree(&env->version); |
| @@ -38,6 +186,13 @@ void perf_env__exit(struct perf_env *env) | |||
| 38 | zfree(&env->memory_nodes); | 186 | zfree(&env->memory_nodes); |
| 39 | } | 187 | } |
| 40 | 188 | ||
| 189 | void perf_env__init(struct perf_env *env) | ||
| 190 | { | ||
| 191 | env->bpf_progs.infos = RB_ROOT; | ||
| 192 | env->bpf_progs.btfs = RB_ROOT; | ||
| 193 | init_rwsem(&env->bpf_progs.lock); | ||
| 194 | } | ||
| 195 | |||
| 41 | int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) | 196 | int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) |
| 42 | { | 197 | { |
| 43 | int i; | 198 | int i; |
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index d01b8355f4ca..4f8e2b485c01 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | #define __PERF_ENV_H | 3 | #define __PERF_ENV_H |
| 4 | 4 | ||
| 5 | #include <linux/types.h> | 5 | #include <linux/types.h> |
| 6 | #include <linux/rbtree.h> | ||
| 6 | #include "cpumap.h" | 7 | #include "cpumap.h" |
| 8 | #include "rwsem.h" | ||
| 7 | 9 | ||
| 8 | struct cpu_topology_map { | 10 | struct cpu_topology_map { |
| 9 | int socket_id; | 11 | int socket_id; |
| @@ -64,8 +66,23 @@ struct perf_env { | |||
| 64 | struct memory_node *memory_nodes; | 66 | struct memory_node *memory_nodes; |
| 65 | unsigned long long memory_bsize; | 67 | unsigned long long memory_bsize; |
| 66 | u64 clockid_res_ns; | 68 | u64 clockid_res_ns; |
| 69 | |||
| 70 | /* | ||
| 71 | * bpf_info_lock protects bpf rbtrees. This is needed because the | ||
| 72 | * trees are accessed by different threads in perf-top | ||
| 73 | */ | ||
| 74 | struct { | ||
| 75 | struct rw_semaphore lock; | ||
| 76 | struct rb_root infos; | ||
| 77 | u32 infos_cnt; | ||
| 78 | struct rb_root btfs; | ||
| 79 | u32 btfs_cnt; | ||
| 80 | } bpf_progs; | ||
| 67 | }; | 81 | }; |
| 68 | 82 | ||
| 83 | struct bpf_prog_info_node; | ||
| 84 | struct btf_node; | ||
| 85 | |||
| 69 | extern struct perf_env perf_env; | 86 | extern struct perf_env perf_env; |
| 70 | 87 | ||
| 71 | void perf_env__exit(struct perf_env *env); | 88 | void perf_env__exit(struct perf_env *env); |
| @@ -80,4 +97,11 @@ const char *perf_env__arch(struct perf_env *env); | |||
| 80 | const char *perf_env__raw_arch(struct perf_env *env); | 97 | const char *perf_env__raw_arch(struct perf_env *env); |
| 81 | int perf_env__nr_cpus_avail(struct perf_env *env); | 98 | int perf_env__nr_cpus_avail(struct perf_env *env); |
| 82 | 99 | ||
| 100 | void perf_env__init(struct perf_env *env); | ||
| 101 | void perf_env__insert_bpf_prog_info(struct perf_env *env, | ||
| 102 | struct bpf_prog_info_node *info_node); | ||
| 103 | struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, | ||
| 104 | __u32 prog_id); | ||
| 105 | void perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); | ||
| 106 | struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id); | ||
| 83 | #endif /* __PERF_ENV_H */ | 107 | #endif /* __PERF_ENV_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ed20f4379956..6689378ee577 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "debug.h" | 19 | #include "debug.h" |
| 20 | #include "units.h" | 20 | #include "units.h" |
| 21 | #include "asm/bug.h" | 21 | #include "asm/bug.h" |
| 22 | #include "bpf-event.h" | ||
| 22 | #include <signal.h> | 23 | #include <signal.h> |
| 23 | #include <unistd.h> | 24 | #include <unistd.h> |
| 24 | 25 | ||
| @@ -230,35 +231,6 @@ void perf_evlist__set_leader(struct perf_evlist *evlist) | |||
| 230 | } | 231 | } |
| 231 | } | 232 | } |
| 232 | 233 | ||
| 233 | void perf_event_attr__set_max_precise_ip(struct perf_event_attr *pattr) | ||
| 234 | { | ||
| 235 | struct perf_event_attr attr = { | ||
| 236 | .type = PERF_TYPE_HARDWARE, | ||
| 237 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
| 238 | .exclude_kernel = 1, | ||
| 239 | .precise_ip = 3, | ||
| 240 | }; | ||
| 241 | |||
| 242 | event_attr_init(&attr); | ||
| 243 | |||
| 244 | /* | ||
| 245 | * Unnamed union member, not supported as struct member named | ||
| 246 | * initializer in older compilers such as gcc 4.4.7 | ||
| 247 | */ | ||
| 248 | attr.sample_period = 1; | ||
| 249 | |||
| 250 | while (attr.precise_ip != 0) { | ||
| 251 | int fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
| 252 | if (fd != -1) { | ||
| 253 | close(fd); | ||
| 254 | break; | ||
| 255 | } | ||
| 256 | --attr.precise_ip; | ||
| 257 | } | ||
| 258 | |||
| 259 | pattr->precise_ip = attr.precise_ip; | ||
| 260 | } | ||
| 261 | |||
| 262 | int __perf_evlist__add_default(struct perf_evlist *evlist, bool precise) | 234 | int __perf_evlist__add_default(struct perf_evlist *evlist, bool precise) |
| 263 | { | 235 | { |
| 264 | struct perf_evsel *evsel = perf_evsel__new_cycles(precise); | 236 | struct perf_evsel *evsel = perf_evsel__new_cycles(precise); |
| @@ -1856,3 +1828,121 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list, | |||
| 1856 | } | 1828 | } |
| 1857 | return leader; | 1829 | return leader; |
| 1858 | } | 1830 | } |
| 1831 | |||
| 1832 | int perf_evlist__add_sb_event(struct perf_evlist **evlist, | ||
| 1833 | struct perf_event_attr *attr, | ||
| 1834 | perf_evsel__sb_cb_t cb, | ||
| 1835 | void *data) | ||
| 1836 | { | ||
| 1837 | struct perf_evsel *evsel; | ||
| 1838 | bool new_evlist = (*evlist) == NULL; | ||
| 1839 | |||
| 1840 | if (*evlist == NULL) | ||
| 1841 | *evlist = perf_evlist__new(); | ||
| 1842 | if (*evlist == NULL) | ||
| 1843 | return -1; | ||
| 1844 | |||
| 1845 | if (!attr->sample_id_all) { | ||
| 1846 | pr_warning("enabling sample_id_all for all side band events\n"); | ||
| 1847 | attr->sample_id_all = 1; | ||
| 1848 | } | ||
| 1849 | |||
| 1850 | evsel = perf_evsel__new_idx(attr, (*evlist)->nr_entries); | ||
| 1851 | if (!evsel) | ||
| 1852 | goto out_err; | ||
| 1853 | |||
| 1854 | evsel->side_band.cb = cb; | ||
| 1855 | evsel->side_band.data = data; | ||
| 1856 | perf_evlist__add(*evlist, evsel); | ||
| 1857 | return 0; | ||
| 1858 | |||
| 1859 | out_err: | ||
| 1860 | if (new_evlist) { | ||
| 1861 | perf_evlist__delete(*evlist); | ||
| 1862 | *evlist = NULL; | ||
| 1863 | } | ||
| 1864 | return -1; | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | static void *perf_evlist__poll_thread(void *arg) | ||
| 1868 | { | ||
| 1869 | struct perf_evlist *evlist = arg; | ||
| 1870 | bool draining = false; | ||
| 1871 | int i; | ||
| 1872 | |||
| 1873 | while (draining || !(evlist->thread.done)) { | ||
| 1874 | if (draining) | ||
| 1875 | draining = false; | ||
| 1876 | else if (evlist->thread.done) | ||
| 1877 | draining = true; | ||
| 1878 | |||
| 1879 | if (!draining) | ||
| 1880 | perf_evlist__poll(evlist, 1000); | ||
| 1881 | |||
| 1882 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
| 1883 | struct perf_mmap *map = &evlist->mmap[i]; | ||
| 1884 | union perf_event *event; | ||
| 1885 | |||
| 1886 | if (perf_mmap__read_init(map)) | ||
| 1887 | continue; | ||
| 1888 | while ((event = perf_mmap__read_event(map)) != NULL) { | ||
| 1889 | struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event); | ||
| 1890 | |||
| 1891 | if (evsel && evsel->side_band.cb) | ||
| 1892 | evsel->side_band.cb(event, evsel->side_band.data); | ||
| 1893 | else | ||
| 1894 | pr_warning("cannot locate proper evsel for the side band event\n"); | ||
| 1895 | |||
| 1896 | perf_mmap__consume(map); | ||
| 1897 | } | ||
| 1898 | perf_mmap__read_done(map); | ||
| 1899 | } | ||
| 1900 | } | ||
| 1901 | return NULL; | ||
| 1902 | } | ||
| 1903 | |||
| 1904 | int perf_evlist__start_sb_thread(struct perf_evlist *evlist, | ||
| 1905 | struct target *target) | ||
| 1906 | { | ||
| 1907 | struct perf_evsel *counter; | ||
| 1908 | |||
| 1909 | if (!evlist) | ||
| 1910 | return 0; | ||
| 1911 | |||
| 1912 | if (perf_evlist__create_maps(evlist, target)) | ||
| 1913 | goto out_delete_evlist; | ||
| 1914 | |||
| 1915 | evlist__for_each_entry(evlist, counter) { | ||
| 1916 | if (perf_evsel__open(counter, evlist->cpus, | ||
| 1917 | evlist->threads) < 0) | ||
| 1918 | goto out_delete_evlist; | ||
| 1919 | } | ||
| 1920 | |||
| 1921 | if (perf_evlist__mmap(evlist, UINT_MAX)) | ||
| 1922 | goto out_delete_evlist; | ||
| 1923 | |||
| 1924 | evlist__for_each_entry(evlist, counter) { | ||
| 1925 | if (perf_evsel__enable(counter)) | ||
| 1926 | goto out_delete_evlist; | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | evlist->thread.done = 0; | ||
| 1930 | if (pthread_create(&evlist->thread.th, NULL, perf_evlist__poll_thread, evlist)) | ||
| 1931 | goto out_delete_evlist; | ||
| 1932 | |||
| 1933 | return 0; | ||
| 1934 | |||
| 1935 | out_delete_evlist: | ||
| 1936 | perf_evlist__delete(evlist); | ||
| 1937 | evlist = NULL; | ||
| 1938 | return -1; | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | void perf_evlist__stop_sb_thread(struct perf_evlist *evlist) | ||
| 1942 | { | ||
| 1943 | if (!evlist) | ||
| 1944 | return; | ||
| 1945 | evlist->thread.done = 1; | ||
| 1946 | pthread_join(evlist->thread.th, NULL); | ||
| 1947 | perf_evlist__delete(evlist); | ||
| 1948 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 744906dd4887..6a94785b9100 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -54,6 +54,10 @@ struct perf_evlist { | |||
| 54 | struct perf_sample *sample); | 54 | struct perf_sample *sample); |
| 55 | u64 first_sample_time; | 55 | u64 first_sample_time; |
| 56 | u64 last_sample_time; | 56 | u64 last_sample_time; |
| 57 | struct { | ||
| 58 | pthread_t th; | ||
| 59 | volatile int done; | ||
| 60 | } thread; | ||
| 57 | }; | 61 | }; |
| 58 | 62 | ||
| 59 | struct perf_evsel_str_handler { | 63 | struct perf_evsel_str_handler { |
| @@ -87,6 +91,14 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, | |||
| 87 | 91 | ||
| 88 | int perf_evlist__add_dummy(struct perf_evlist *evlist); | 92 | int perf_evlist__add_dummy(struct perf_evlist *evlist); |
| 89 | 93 | ||
| 94 | int perf_evlist__add_sb_event(struct perf_evlist **evlist, | ||
| 95 | struct perf_event_attr *attr, | ||
| 96 | perf_evsel__sb_cb_t cb, | ||
| 97 | void *data); | ||
| 98 | int perf_evlist__start_sb_thread(struct perf_evlist *evlist, | ||
| 99 | struct target *target); | ||
| 100 | void perf_evlist__stop_sb_thread(struct perf_evlist *evlist); | ||
| 101 | |||
| 90 | int perf_evlist__add_newtp(struct perf_evlist *evlist, | 102 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
| 91 | const char *sys, const char *name, void *handler); | 103 | const char *sys, const char *name, void *handler); |
| 92 | 104 | ||
| @@ -303,8 +315,6 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
| 303 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | 315 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, |
| 304 | struct perf_evsel *tracking_evsel); | 316 | struct perf_evsel *tracking_evsel); |
| 305 | 317 | ||
| 306 | void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr); | ||
| 307 | |||
| 308 | struct perf_evsel * | 318 | struct perf_evsel * |
| 309 | perf_evlist__find_evsel_by_str(struct perf_evlist *evlist, const char *str); | 319 | perf_evlist__find_evsel_by_str(struct perf_evlist *evlist, const char *str); |
| 310 | 320 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3bbf73e979c0..66d066f18b5b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -295,7 +295,6 @@ struct perf_evsel *perf_evsel__new_cycles(bool precise) | |||
| 295 | if (!precise) | 295 | if (!precise) |
| 296 | goto new_event; | 296 | goto new_event; |
| 297 | 297 | ||
| 298 | perf_event_attr__set_max_precise_ip(&attr); | ||
| 299 | /* | 298 | /* |
| 300 | * Now let the usual logic to set up the perf_event_attr defaults | 299 | * Now let the usual logic to set up the perf_event_attr defaults |
| 301 | * to kick in when we return and before perf_evsel__open() is called. | 300 | * to kick in when we return and before perf_evsel__open() is called. |
| @@ -305,6 +304,8 @@ new_event: | |||
| 305 | if (evsel == NULL) | 304 | if (evsel == NULL) |
| 306 | goto out; | 305 | goto out; |
| 307 | 306 | ||
| 307 | evsel->precise_max = true; | ||
| 308 | |||
| 308 | /* use asprintf() because free(evsel) assumes name is allocated */ | 309 | /* use asprintf() because free(evsel) assumes name is allocated */ |
| 309 | if (asprintf(&evsel->name, "cycles%s%s%.*s", | 310 | if (asprintf(&evsel->name, "cycles%s%s%.*s", |
| 310 | (attr.precise_ip || attr.exclude_kernel) ? ":" : "", | 311 | (attr.precise_ip || attr.exclude_kernel) ? ":" : "", |
| @@ -1036,7 +1037,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
| 1036 | attr->mmap2 = track && !perf_missing_features.mmap2; | 1037 | attr->mmap2 = track && !perf_missing_features.mmap2; |
| 1037 | attr->comm = track; | 1038 | attr->comm = track; |
| 1038 | attr->ksymbol = track && !perf_missing_features.ksymbol; | 1039 | attr->ksymbol = track && !perf_missing_features.ksymbol; |
| 1039 | attr->bpf_event = track && opts->bpf_event && | 1040 | attr->bpf_event = track && !opts->no_bpf_event && |
| 1040 | !perf_missing_features.bpf_event; | 1041 | !perf_missing_features.bpf_event; |
| 1041 | 1042 | ||
| 1042 | if (opts->record_namespaces) | 1043 | if (opts->record_namespaces) |
| @@ -1083,7 +1084,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
| 1083 | } | 1084 | } |
| 1084 | 1085 | ||
| 1085 | if (evsel->precise_max) | 1086 | if (evsel->precise_max) |
| 1086 | perf_event_attr__set_max_precise_ip(attr); | 1087 | attr->precise_ip = 3; |
| 1087 | 1088 | ||
| 1088 | if (opts->all_user) { | 1089 | if (opts->all_user) { |
| 1089 | attr->exclude_kernel = 1; | 1090 | attr->exclude_kernel = 1; |
| @@ -1292,6 +1293,7 @@ void perf_evsel__exit(struct perf_evsel *evsel) | |||
| 1292 | { | 1293 | { |
| 1293 | assert(list_empty(&evsel->node)); | 1294 | assert(list_empty(&evsel->node)); |
| 1294 | assert(evsel->evlist == NULL); | 1295 | assert(evsel->evlist == NULL); |
| 1296 | perf_evsel__free_counts(evsel); | ||
| 1295 | perf_evsel__free_fd(evsel); | 1297 | perf_evsel__free_fd(evsel); |
| 1296 | perf_evsel__free_id(evsel); | 1298 | perf_evsel__free_id(evsel); |
| 1297 | perf_evsel__free_config_terms(evsel); | 1299 | perf_evsel__free_config_terms(evsel); |
| @@ -1342,10 +1344,9 @@ void perf_counts_values__scale(struct perf_counts_values *count, | |||
| 1342 | count->val = 0; | 1344 | count->val = 0; |
| 1343 | } else if (count->run < count->ena) { | 1345 | } else if (count->run < count->ena) { |
| 1344 | scaled = 1; | 1346 | scaled = 1; |
| 1345 | count->val = (u64)((double) count->val * count->ena / count->run + 0.5); | 1347 | count->val = (u64)((double) count->val * count->ena / count->run); |
| 1346 | } | 1348 | } |
| 1347 | } else | 1349 | } |
| 1348 | count->ena = count->run = 0; | ||
| 1349 | 1350 | ||
| 1350 | if (pscaled) | 1351 | if (pscaled) |
| 1351 | *pscaled = scaled; | 1352 | *pscaled = scaled; |
| @@ -1749,6 +1750,59 @@ static bool ignore_missing_thread(struct perf_evsel *evsel, | |||
| 1749 | return true; | 1750 | return true; |
| 1750 | } | 1751 | } |
| 1751 | 1752 | ||
| 1753 | static void display_attr(struct perf_event_attr *attr) | ||
| 1754 | { | ||
| 1755 | if (verbose >= 2) { | ||
| 1756 | fprintf(stderr, "%.60s\n", graph_dotted_line); | ||
| 1757 | fprintf(stderr, "perf_event_attr:\n"); | ||
| 1758 | perf_event_attr__fprintf(stderr, attr, __open_attr__fprintf, NULL); | ||
| 1759 | fprintf(stderr, "%.60s\n", graph_dotted_line); | ||
| 1760 | } | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | static int perf_event_open(struct perf_evsel *evsel, | ||
| 1764 | pid_t pid, int cpu, int group_fd, | ||
| 1765 | unsigned long flags) | ||
| 1766 | { | ||
| 1767 | int precise_ip = evsel->attr.precise_ip; | ||
| 1768 | int fd; | ||
| 1769 | |||
| 1770 | while (1) { | ||
| 1771 | pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx", | ||
| 1772 | pid, cpu, group_fd, flags); | ||
| 1773 | |||
| 1774 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, group_fd, flags); | ||
| 1775 | if (fd >= 0) | ||
| 1776 | break; | ||
| 1777 | |||
| 1778 | /* | ||
| 1779 | * Do quick precise_ip fallback if: | ||
| 1780 | * - there is precise_ip set in perf_event_attr | ||
| 1781 | * - maximum precise is requested | ||
| 1782 | * - sys_perf_event_open failed with ENOTSUP error, | ||
| 1783 | * which is associated with wrong precise_ip | ||
| 1784 | */ | ||
| 1785 | if (!precise_ip || !evsel->precise_max || (errno != ENOTSUP)) | ||
| 1786 | break; | ||
| 1787 | |||
| 1788 | /* | ||
| 1789 | * We tried all the precise_ip values, and it's | ||
| 1790 | * still failing, so leave it to standard fallback. | ||
| 1791 | */ | ||
| 1792 | if (!evsel->attr.precise_ip) { | ||
| 1793 | evsel->attr.precise_ip = precise_ip; | ||
| 1794 | break; | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | pr_debug2("\nsys_perf_event_open failed, error %d\n", -ENOTSUP); | ||
| 1798 | evsel->attr.precise_ip--; | ||
| 1799 | pr_debug2("decreasing precise_ip by one (%d)\n", evsel->attr.precise_ip); | ||
| 1800 | display_attr(&evsel->attr); | ||
| 1801 | } | ||
| 1802 | |||
| 1803 | return fd; | ||
| 1804 | } | ||
| 1805 | |||
| 1752 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 1806 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 1753 | struct thread_map *threads) | 1807 | struct thread_map *threads) |
| 1754 | { | 1808 | { |
| @@ -1824,12 +1878,7 @@ retry_sample_id: | |||
| 1824 | if (perf_missing_features.sample_id_all) | 1878 | if (perf_missing_features.sample_id_all) |
| 1825 | evsel->attr.sample_id_all = 0; | 1879 | evsel->attr.sample_id_all = 0; |
| 1826 | 1880 | ||
| 1827 | if (verbose >= 2) { | 1881 | display_attr(&evsel->attr); |
| 1828 | fprintf(stderr, "%.60s\n", graph_dotted_line); | ||
| 1829 | fprintf(stderr, "perf_event_attr:\n"); | ||
| 1830 | perf_event_attr__fprintf(stderr, &evsel->attr, __open_attr__fprintf, NULL); | ||
| 1831 | fprintf(stderr, "%.60s\n", graph_dotted_line); | ||
| 1832 | } | ||
| 1833 | 1882 | ||
| 1834 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1883 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
| 1835 | 1884 | ||
| @@ -1841,13 +1890,10 @@ retry_sample_id: | |||
| 1841 | 1890 | ||
| 1842 | group_fd = get_group_fd(evsel, cpu, thread); | 1891 | group_fd = get_group_fd(evsel, cpu, thread); |
| 1843 | retry_open: | 1892 | retry_open: |
| 1844 | pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx", | ||
| 1845 | pid, cpus->map[cpu], group_fd, flags); | ||
| 1846 | |||
| 1847 | test_attr__ready(); | 1893 | test_attr__ready(); |
| 1848 | 1894 | ||
| 1849 | fd = sys_perf_event_open(&evsel->attr, pid, cpus->map[cpu], | 1895 | fd = perf_event_open(evsel, pid, cpus->map[cpu], |
| 1850 | group_fd, flags); | 1896 | group_fd, flags); |
| 1851 | 1897 | ||
| 1852 | FD(evsel, cpu, thread) = fd; | 1898 | FD(evsel, cpu, thread) = fd; |
| 1853 | 1899 | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index cc578e02e08f..0f2c6c93d721 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -73,6 +73,8 @@ struct perf_evsel_config_term { | |||
| 73 | 73 | ||
| 74 | struct perf_stat_evsel; | 74 | struct perf_stat_evsel; |
| 75 | 75 | ||
| 76 | typedef int (perf_evsel__sb_cb_t)(union perf_event *event, void *data); | ||
| 77 | |||
| 76 | /** struct perf_evsel - event selector | 78 | /** struct perf_evsel - event selector |
| 77 | * | 79 | * |
| 78 | * @evlist - evlist this evsel is in, if it is in one. | 80 | * @evlist - evlist this evsel is in, if it is in one. |
| @@ -151,6 +153,10 @@ struct perf_evsel { | |||
| 151 | bool collect_stat; | 153 | bool collect_stat; |
| 152 | bool weak_group; | 154 | bool weak_group; |
| 153 | const char *pmu_name; | 155 | const char *pmu_name; |
| 156 | struct { | ||
| 157 | perf_evsel__sb_cb_t *cb; | ||
| 158 | void *data; | ||
| 159 | } side_band; | ||
| 154 | }; | 160 | }; |
| 155 | 161 | ||
| 156 | union u64_swap { | 162 | union u64_swap { |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 01b324c275b9..b9e693825873 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <sys/utsname.h> | 18 | #include <sys/utsname.h> |
| 19 | #include <linux/time64.h> | 19 | #include <linux/time64.h> |
| 20 | #include <dirent.h> | 20 | #include <dirent.h> |
| 21 | #include <bpf/libbpf.h> | ||
| 21 | 22 | ||
| 22 | #include "evlist.h" | 23 | #include "evlist.h" |
| 23 | #include "evsel.h" | 24 | #include "evsel.h" |
| @@ -40,6 +41,7 @@ | |||
| 40 | #include "time-utils.h" | 41 | #include "time-utils.h" |
| 41 | #include "units.h" | 42 | #include "units.h" |
| 42 | #include "cputopo.h" | 43 | #include "cputopo.h" |
| 44 | #include "bpf-event.h" | ||
| 43 | 45 | ||
| 44 | #include "sane_ctype.h" | 46 | #include "sane_ctype.h" |
| 45 | 47 | ||
| @@ -861,6 +863,104 @@ static int write_clockid(struct feat_fd *ff, | |||
| 861 | sizeof(ff->ph->env.clockid_res_ns)); | 863 | sizeof(ff->ph->env.clockid_res_ns)); |
| 862 | } | 864 | } |
| 863 | 865 | ||
| 866 | static int write_dir_format(struct feat_fd *ff, | ||
| 867 | struct perf_evlist *evlist __maybe_unused) | ||
| 868 | { | ||
| 869 | struct perf_session *session; | ||
| 870 | struct perf_data *data; | ||
| 871 | |||
| 872 | session = container_of(ff->ph, struct perf_session, header); | ||
| 873 | data = session->data; | ||
| 874 | |||
| 875 | if (WARN_ON(!perf_data__is_dir(data))) | ||
| 876 | return -1; | ||
| 877 | |||
| 878 | return do_write(ff, &data->dir.version, sizeof(data->dir.version)); | ||
| 879 | } | ||
| 880 | |||
| 881 | #ifdef HAVE_LIBBPF_SUPPORT | ||
| 882 | static int write_bpf_prog_info(struct feat_fd *ff, | ||
| 883 | struct perf_evlist *evlist __maybe_unused) | ||
| 884 | { | ||
| 885 | struct perf_env *env = &ff->ph->env; | ||
| 886 | struct rb_root *root; | ||
| 887 | struct rb_node *next; | ||
| 888 | int ret; | ||
| 889 | |||
| 890 | down_read(&env->bpf_progs.lock); | ||
| 891 | |||
| 892 | ret = do_write(ff, &env->bpf_progs.infos_cnt, | ||
| 893 | sizeof(env->bpf_progs.infos_cnt)); | ||
| 894 | if (ret < 0) | ||
| 895 | goto out; | ||
| 896 | |||
| 897 | root = &env->bpf_progs.infos; | ||
| 898 | next = rb_first(root); | ||
| 899 | while (next) { | ||
| 900 | struct bpf_prog_info_node *node; | ||
| 901 | size_t len; | ||
| 902 | |||
| 903 | node = rb_entry(next, struct bpf_prog_info_node, rb_node); | ||
| 904 | next = rb_next(&node->rb_node); | ||
| 905 | len = sizeof(struct bpf_prog_info_linear) + | ||
| 906 | node->info_linear->data_len; | ||
| 907 | |||
| 908 | /* before writing to file, translate address to offset */ | ||
| 909 | bpf_program__bpil_addr_to_offs(node->info_linear); | ||
| 910 | ret = do_write(ff, node->info_linear, len); | ||
| 911 | /* | ||
| 912 | * translate back to address even when do_write() fails, | ||
| 913 | * so that this function never changes the data. | ||
| 914 | */ | ||
| 915 | bpf_program__bpil_offs_to_addr(node->info_linear); | ||
| 916 | if (ret < 0) | ||
| 917 | goto out; | ||
| 918 | } | ||
| 919 | out: | ||
| 920 | up_read(&env->bpf_progs.lock); | ||
| 921 | return ret; | ||
| 922 | } | ||
| 923 | #else // HAVE_LIBBPF_SUPPORT | ||
| 924 | static int write_bpf_prog_info(struct feat_fd *ff __maybe_unused, | ||
| 925 | struct perf_evlist *evlist __maybe_unused) | ||
| 926 | { | ||
| 927 | return 0; | ||
| 928 | } | ||
| 929 | #endif // HAVE_LIBBPF_SUPPORT | ||
| 930 | |||
| 931 | static int write_bpf_btf(struct feat_fd *ff, | ||
| 932 | struct perf_evlist *evlist __maybe_unused) | ||
| 933 | { | ||
| 934 | struct perf_env *env = &ff->ph->env; | ||
| 935 | struct rb_root *root; | ||
| 936 | struct rb_node *next; | ||
| 937 | int ret; | ||
| 938 | |||
| 939 | down_read(&env->bpf_progs.lock); | ||
| 940 | |||
| 941 | ret = do_write(ff, &env->bpf_progs.btfs_cnt, | ||
| 942 | sizeof(env->bpf_progs.btfs_cnt)); | ||
| 943 | |||
| 944 | if (ret < 0) | ||
| 945 | goto out; | ||
| 946 | |||
| 947 | root = &env->bpf_progs.btfs; | ||
| 948 | next = rb_first(root); | ||
| 949 | while (next) { | ||
| 950 | struct btf_node *node; | ||
| 951 | |||
| 952 | node = rb_entry(next, struct btf_node, rb_node); | ||
| 953 | next = rb_next(&node->rb_node); | ||
| 954 | ret = do_write(ff, &node->id, | ||
| 955 | sizeof(u32) * 2 + node->data_size); | ||
| 956 | if (ret < 0) | ||
| 957 | goto out; | ||
| 958 | } | ||
| 959 | out: | ||
| 960 | up_read(&env->bpf_progs.lock); | ||
| 961 | return ret; | ||
| 962 | } | ||
| 963 | |||
| 864 | static int cpu_cache_level__sort(const void *a, const void *b) | 964 | static int cpu_cache_level__sort(const void *a, const void *b) |
| 865 | { | 965 | { |
| 866 | struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; | 966 | struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; |
| @@ -1341,6 +1441,63 @@ static void print_clockid(struct feat_fd *ff, FILE *fp) | |||
| 1341 | ff->ph->env.clockid_res_ns * 1000); | 1441 | ff->ph->env.clockid_res_ns * 1000); |
| 1342 | } | 1442 | } |
| 1343 | 1443 | ||
| 1444 | static void print_dir_format(struct feat_fd *ff, FILE *fp) | ||
| 1445 | { | ||
| 1446 | struct perf_session *session; | ||
| 1447 | struct perf_data *data; | ||
| 1448 | |||
| 1449 | session = container_of(ff->ph, struct perf_session, header); | ||
| 1450 | data = session->data; | ||
| 1451 | |||
| 1452 | fprintf(fp, "# directory data version : %"PRIu64"\n", data->dir.version); | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) | ||
| 1456 | { | ||
| 1457 | struct perf_env *env = &ff->ph->env; | ||
| 1458 | struct rb_root *root; | ||
| 1459 | struct rb_node *next; | ||
| 1460 | |||
| 1461 | down_read(&env->bpf_progs.lock); | ||
| 1462 | |||
| 1463 | root = &env->bpf_progs.infos; | ||
| 1464 | next = rb_first(root); | ||
| 1465 | |||
| 1466 | while (next) { | ||
| 1467 | struct bpf_prog_info_node *node; | ||
| 1468 | |||
| 1469 | node = rb_entry(next, struct bpf_prog_info_node, rb_node); | ||
| 1470 | next = rb_next(&node->rb_node); | ||
| 1471 | |||
| 1472 | bpf_event__print_bpf_prog_info(&node->info_linear->info, | ||
| 1473 | env, fp); | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | up_read(&env->bpf_progs.lock); | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | static void print_bpf_btf(struct feat_fd *ff, FILE *fp) | ||
| 1480 | { | ||
| 1481 | struct perf_env *env = &ff->ph->env; | ||
| 1482 | struct rb_root *root; | ||
| 1483 | struct rb_node *next; | ||
| 1484 | |||
| 1485 | down_read(&env->bpf_progs.lock); | ||
| 1486 | |||
| 1487 | root = &env->bpf_progs.btfs; | ||
| 1488 | next = rb_first(root); | ||
| 1489 | |||
| 1490 | while (next) { | ||
| 1491 | struct btf_node *node; | ||
| 1492 | |||
| 1493 | node = rb_entry(next, struct btf_node, rb_node); | ||
| 1494 | next = rb_next(&node->rb_node); | ||
| 1495 | fprintf(fp, "# btf info of id %u\n", node->id); | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | up_read(&env->bpf_progs.lock); | ||
| 1499 | } | ||
| 1500 | |||
| 1344 | static void free_event_desc(struct perf_evsel *events) | 1501 | static void free_event_desc(struct perf_evsel *events) |
| 1345 | { | 1502 | { |
| 1346 | struct perf_evsel *evsel; | 1503 | struct perf_evsel *evsel; |
| @@ -2373,6 +2530,139 @@ static int process_clockid(struct feat_fd *ff, | |||
| 2373 | return 0; | 2530 | return 0; |
| 2374 | } | 2531 | } |
| 2375 | 2532 | ||
| 2533 | static int process_dir_format(struct feat_fd *ff, | ||
| 2534 | void *_data __maybe_unused) | ||
| 2535 | { | ||
| 2536 | struct perf_session *session; | ||
| 2537 | struct perf_data *data; | ||
| 2538 | |||
| 2539 | session = container_of(ff->ph, struct perf_session, header); | ||
| 2540 | data = session->data; | ||
| 2541 | |||
| 2542 | if (WARN_ON(!perf_data__is_dir(data))) | ||
| 2543 | return -1; | ||
| 2544 | |||
| 2545 | return do_read_u64(ff, &data->dir.version); | ||
| 2546 | } | ||
| 2547 | |||
| 2548 | #ifdef HAVE_LIBBPF_SUPPORT | ||
| 2549 | static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) | ||
| 2550 | { | ||
| 2551 | struct bpf_prog_info_linear *info_linear; | ||
| 2552 | struct bpf_prog_info_node *info_node; | ||
| 2553 | struct perf_env *env = &ff->ph->env; | ||
| 2554 | u32 count, i; | ||
| 2555 | int err = -1; | ||
| 2556 | |||
| 2557 | if (ff->ph->needs_swap) { | ||
| 2558 | pr_warning("interpreting bpf_prog_info from systems with endianity is not yet supported\n"); | ||
| 2559 | return 0; | ||
| 2560 | } | ||
| 2561 | |||
| 2562 | if (do_read_u32(ff, &count)) | ||
| 2563 | return -1; | ||
| 2564 | |||
| 2565 | down_write(&env->bpf_progs.lock); | ||
| 2566 | |||
| 2567 | for (i = 0; i < count; ++i) { | ||
| 2568 | u32 info_len, data_len; | ||
| 2569 | |||
| 2570 | info_linear = NULL; | ||
| 2571 | info_node = NULL; | ||
| 2572 | if (do_read_u32(ff, &info_len)) | ||
| 2573 | goto out; | ||
| 2574 | if (do_read_u32(ff, &data_len)) | ||
| 2575 | goto out; | ||
| 2576 | |||
| 2577 | if (info_len > sizeof(struct bpf_prog_info)) { | ||
| 2578 | pr_warning("detected invalid bpf_prog_info\n"); | ||
| 2579 | goto out; | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | info_linear = malloc(sizeof(struct bpf_prog_info_linear) + | ||
| 2583 | data_len); | ||
| 2584 | if (!info_linear) | ||
| 2585 | goto out; | ||
| 2586 | info_linear->info_len = sizeof(struct bpf_prog_info); | ||
| 2587 | info_linear->data_len = data_len; | ||
| 2588 | if (do_read_u64(ff, (u64 *)(&info_linear->arrays))) | ||
| 2589 | goto out; | ||
| 2590 | if (__do_read(ff, &info_linear->info, info_len)) | ||
| 2591 | goto out; | ||
| 2592 | if (info_len < sizeof(struct bpf_prog_info)) | ||
| 2593 | memset(((void *)(&info_linear->info)) + info_len, 0, | ||
| 2594 | sizeof(struct bpf_prog_info) - info_len); | ||
| 2595 | |||
| 2596 | if (__do_read(ff, info_linear->data, data_len)) | ||
| 2597 | goto out; | ||
| 2598 | |||
| 2599 | info_node = malloc(sizeof(struct bpf_prog_info_node)); | ||
| 2600 | if (!info_node) | ||
| 2601 | goto out; | ||
| 2602 | |||
| 2603 | /* after reading from file, translate offset to address */ | ||
| 2604 | bpf_program__bpil_offs_to_addr(info_linear); | ||
| 2605 | info_node->info_linear = info_linear; | ||
| 2606 | perf_env__insert_bpf_prog_info(env, info_node); | ||
| 2607 | } | ||
| 2608 | |||
| 2609 | return 0; | ||
| 2610 | out: | ||
| 2611 | free(info_linear); | ||
| 2612 | free(info_node); | ||
| 2613 | up_write(&env->bpf_progs.lock); | ||
| 2614 | return err; | ||
| 2615 | } | ||
| 2616 | #else // HAVE_LIBBPF_SUPPORT | ||
| 2617 | static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data __maybe_unused) | ||
| 2618 | { | ||
| 2619 | return 0; | ||
| 2620 | } | ||
| 2621 | #endif // HAVE_LIBBPF_SUPPORT | ||
| 2622 | |||
| 2623 | static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused) | ||
| 2624 | { | ||
| 2625 | struct perf_env *env = &ff->ph->env; | ||
| 2626 | u32 count, i; | ||
| 2627 | |||
| 2628 | if (ff->ph->needs_swap) { | ||
| 2629 | pr_warning("interpreting btf from systems with endianity is not yet supported\n"); | ||
| 2630 | return 0; | ||
| 2631 | } | ||
| 2632 | |||
| 2633 | if (do_read_u32(ff, &count)) | ||
| 2634 | return -1; | ||
| 2635 | |||
| 2636 | down_write(&env->bpf_progs.lock); | ||
| 2637 | |||
| 2638 | for (i = 0; i < count; ++i) { | ||
| 2639 | struct btf_node *node; | ||
| 2640 | u32 id, data_size; | ||
| 2641 | |||
| 2642 | if (do_read_u32(ff, &id)) | ||
| 2643 | return -1; | ||
| 2644 | if (do_read_u32(ff, &data_size)) | ||
| 2645 | return -1; | ||
| 2646 | |||
| 2647 | node = malloc(sizeof(struct btf_node) + data_size); | ||
| 2648 | if (!node) | ||
| 2649 | return -1; | ||
| 2650 | |||
| 2651 | node->id = id; | ||
| 2652 | node->data_size = data_size; | ||
| 2653 | |||
| 2654 | if (__do_read(ff, node->data, data_size)) { | ||
| 2655 | free(node); | ||
| 2656 | return -1; | ||
| 2657 | } | ||
| 2658 | |||
| 2659 | perf_env__insert_btf(env, node); | ||
| 2660 | } | ||
| 2661 | |||
| 2662 | up_write(&env->bpf_progs.lock); | ||
| 2663 | return 0; | ||
| 2664 | } | ||
| 2665 | |||
| 2376 | struct feature_ops { | 2666 | struct feature_ops { |
| 2377 | int (*write)(struct feat_fd *ff, struct perf_evlist *evlist); | 2667 | int (*write)(struct feat_fd *ff, struct perf_evlist *evlist); |
| 2378 | void (*print)(struct feat_fd *ff, FILE *fp); | 2668 | void (*print)(struct feat_fd *ff, FILE *fp); |
| @@ -2432,7 +2722,10 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
| 2432 | FEAT_OPN(CACHE, cache, true), | 2722 | FEAT_OPN(CACHE, cache, true), |
| 2433 | FEAT_OPR(SAMPLE_TIME, sample_time, false), | 2723 | FEAT_OPR(SAMPLE_TIME, sample_time, false), |
| 2434 | FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), | 2724 | FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), |
| 2435 | FEAT_OPR(CLOCKID, clockid, false) | 2725 | FEAT_OPR(CLOCKID, clockid, false), |
| 2726 | FEAT_OPN(DIR_FORMAT, dir_format, false), | ||
| 2727 | FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false), | ||
| 2728 | FEAT_OPR(BPF_BTF, bpf_btf, false), | ||
| 2436 | }; | 2729 | }; |
| 2437 | 2730 | ||
| 2438 | struct header_print_data { | 2731 | struct header_print_data { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 0d553ddca0a3..386da49e1bfa 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -39,6 +39,9 @@ enum { | |||
| 39 | HEADER_SAMPLE_TIME, | 39 | HEADER_SAMPLE_TIME, |
| 40 | HEADER_MEM_TOPOLOGY, | 40 | HEADER_MEM_TOPOLOGY, |
| 41 | HEADER_CLOCKID, | 41 | HEADER_CLOCKID, |
| 42 | HEADER_DIR_FORMAT, | ||
| 43 | HEADER_BPF_PROG_INFO, | ||
| 44 | HEADER_BPF_BTF, | ||
| 42 | HEADER_LAST_FEATURE, | 45 | HEADER_LAST_FEATURE, |
| 43 | HEADER_FEAT_BITS = 256, | 46 | HEADER_FEAT_BITS = 256, |
| 44 | }; | 47 | }; |
| @@ -48,6 +51,10 @@ enum perf_header_version { | |||
| 48 | PERF_HEADER_VERSION_2, | 51 | PERF_HEADER_VERSION_2, |
| 49 | }; | 52 | }; |
| 50 | 53 | ||
| 54 | enum perf_dir_version { | ||
| 55 | PERF_DIR_VERSION = 1, | ||
| 56 | }; | ||
| 57 | |||
| 51 | struct perf_file_section { | 58 | struct perf_file_section { |
| 52 | u64 offset; | 59 | u64 offset; |
| 53 | u64 size; | 60 | u64 size; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f9eb95bf3938..7ace7a10054d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <math.h> | 19 | #include <math.h> |
| 20 | #include <inttypes.h> | 20 | #include <inttypes.h> |
| 21 | #include <sys/param.h> | 21 | #include <sys/param.h> |
| 22 | #include <linux/time64.h> | ||
| 22 | 23 | ||
| 23 | static bool hists__filter_entry_by_dso(struct hists *hists, | 24 | static bool hists__filter_entry_by_dso(struct hists *hists, |
| 24 | struct hist_entry *he); | 25 | struct hist_entry *he); |
| @@ -192,6 +193,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
| 192 | hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); | 193 | hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); |
| 193 | hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); | 194 | hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); |
| 194 | hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); | 195 | hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); |
| 196 | hists__new_col_len(hists, HISTC_TIME, 12); | ||
| 195 | 197 | ||
| 196 | if (h->srcline) { | 198 | if (h->srcline) { |
| 197 | len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header)); | 199 | len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header)); |
| @@ -246,6 +248,14 @@ static void he_stat__add_cpumode_period(struct he_stat *he_stat, | |||
| 246 | } | 248 | } |
| 247 | } | 249 | } |
| 248 | 250 | ||
| 251 | static long hist_time(unsigned long htime) | ||
| 252 | { | ||
| 253 | unsigned long time_quantum = symbol_conf.time_quantum; | ||
| 254 | if (time_quantum) | ||
| 255 | return (htime / time_quantum) * time_quantum; | ||
| 256 | return htime; | ||
| 257 | } | ||
| 258 | |||
| 249 | static void he_stat__add_period(struct he_stat *he_stat, u64 period, | 259 | static void he_stat__add_period(struct he_stat *he_stat, u64 period, |
| 250 | u64 weight) | 260 | u64 weight) |
| 251 | { | 261 | { |
| @@ -426,6 +436,13 @@ static int hist_entry__init(struct hist_entry *he, | |||
| 426 | goto err_rawdata; | 436 | goto err_rawdata; |
| 427 | } | 437 | } |
| 428 | 438 | ||
| 439 | if (symbol_conf.res_sample) { | ||
| 440 | he->res_samples = calloc(sizeof(struct res_sample), | ||
| 441 | symbol_conf.res_sample); | ||
| 442 | if (!he->res_samples) | ||
| 443 | goto err_srcline; | ||
| 444 | } | ||
| 445 | |||
| 429 | INIT_LIST_HEAD(&he->pairs.node); | 446 | INIT_LIST_HEAD(&he->pairs.node); |
| 430 | thread__get(he->thread); | 447 | thread__get(he->thread); |
| 431 | he->hroot_in = RB_ROOT_CACHED; | 448 | he->hroot_in = RB_ROOT_CACHED; |
| @@ -436,6 +453,9 @@ static int hist_entry__init(struct hist_entry *he, | |||
| 436 | 453 | ||
| 437 | return 0; | 454 | return 0; |
| 438 | 455 | ||
| 456 | err_srcline: | ||
| 457 | free(he->srcline); | ||
| 458 | |||
| 439 | err_rawdata: | 459 | err_rawdata: |
| 440 | free(he->raw_data); | 460 | free(he->raw_data); |
| 441 | 461 | ||
| @@ -593,6 +613,32 @@ out: | |||
| 593 | return he; | 613 | return he; |
| 594 | } | 614 | } |
| 595 | 615 | ||
| 616 | static unsigned random_max(unsigned high) | ||
| 617 | { | ||
| 618 | unsigned thresh = -high % high; | ||
| 619 | for (;;) { | ||
| 620 | unsigned r = random(); | ||
| 621 | if (r >= thresh) | ||
| 622 | return r % high; | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | static void hists__res_sample(struct hist_entry *he, struct perf_sample *sample) | ||
| 627 | { | ||
| 628 | struct res_sample *r; | ||
| 629 | int j; | ||
| 630 | |||
| 631 | if (he->num_res < symbol_conf.res_sample) { | ||
| 632 | j = he->num_res++; | ||
| 633 | } else { | ||
| 634 | j = random_max(symbol_conf.res_sample); | ||
| 635 | } | ||
| 636 | r = &he->res_samples[j]; | ||
| 637 | r->time = sample->time; | ||
| 638 | r->cpu = sample->cpu; | ||
| 639 | r->tid = sample->tid; | ||
| 640 | } | ||
| 641 | |||
| 596 | static struct hist_entry* | 642 | static struct hist_entry* |
| 597 | __hists__add_entry(struct hists *hists, | 643 | __hists__add_entry(struct hists *hists, |
| 598 | struct addr_location *al, | 644 | struct addr_location *al, |
| @@ -635,10 +681,13 @@ __hists__add_entry(struct hists *hists, | |||
| 635 | .raw_data = sample->raw_data, | 681 | .raw_data = sample->raw_data, |
| 636 | .raw_size = sample->raw_size, | 682 | .raw_size = sample->raw_size, |
| 637 | .ops = ops, | 683 | .ops = ops, |
| 684 | .time = hist_time(sample->time), | ||
| 638 | }, *he = hists__findnew_entry(hists, &entry, al, sample_self); | 685 | }, *he = hists__findnew_entry(hists, &entry, al, sample_self); |
| 639 | 686 | ||
| 640 | if (!hists->has_callchains && he && he->callchain_size != 0) | 687 | if (!hists->has_callchains && he && he->callchain_size != 0) |
| 641 | hists->has_callchains = true; | 688 | hists->has_callchains = true; |
| 689 | if (he && symbol_conf.res_sample) | ||
| 690 | hists__res_sample(he, sample); | ||
| 642 | return he; | 691 | return he; |
| 643 | } | 692 | } |
| 644 | 693 | ||
| @@ -1062,8 +1111,10 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, | |||
| 1062 | 1111 | ||
| 1063 | err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent, | 1112 | err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent, |
| 1064 | iter->evsel, al, max_stack_depth); | 1113 | iter->evsel, al, max_stack_depth); |
| 1065 | if (err) | 1114 | if (err) { |
| 1115 | map__put(alm); | ||
| 1066 | return err; | 1116 | return err; |
| 1117 | } | ||
| 1067 | 1118 | ||
| 1068 | err = iter->ops->prepare_entry(iter, al); | 1119 | err = iter->ops->prepare_entry(iter, al); |
| 1069 | if (err) | 1120 | if (err) |
| @@ -1162,6 +1213,7 @@ void hist_entry__delete(struct hist_entry *he) | |||
| 1162 | mem_info__zput(he->mem_info); | 1213 | mem_info__zput(he->mem_info); |
| 1163 | } | 1214 | } |
| 1164 | 1215 | ||
| 1216 | zfree(&he->res_samples); | ||
| 1165 | zfree(&he->stat_acc); | 1217 | zfree(&he->stat_acc); |
| 1166 | free_srcline(he->srcline); | 1218 | free_srcline(he->srcline); |
| 1167 | if (he->srcfile && he->srcfile[0]) | 1219 | if (he->srcfile && he->srcfile[0]) |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 4af27fbab24f..76ff6c6d03b8 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -31,6 +31,7 @@ enum hist_filter { | |||
| 31 | 31 | ||
| 32 | enum hist_column { | 32 | enum hist_column { |
| 33 | HISTC_SYMBOL, | 33 | HISTC_SYMBOL, |
| 34 | HISTC_TIME, | ||
| 34 | HISTC_DSO, | 35 | HISTC_DSO, |
| 35 | HISTC_THREAD, | 36 | HISTC_THREAD, |
| 36 | HISTC_COMM, | 37 | HISTC_COMM, |
| @@ -432,9 +433,18 @@ struct hist_browser_timer { | |||
| 432 | }; | 433 | }; |
| 433 | 434 | ||
| 434 | struct annotation_options; | 435 | struct annotation_options; |
| 436 | struct res_sample; | ||
| 437 | |||
| 438 | enum rstype { | ||
| 439 | A_NORMAL, | ||
| 440 | A_ASM, | ||
| 441 | A_SOURCE | ||
| 442 | }; | ||
| 435 | 443 | ||
| 436 | #ifdef HAVE_SLANG_SUPPORT | 444 | #ifdef HAVE_SLANG_SUPPORT |
| 437 | #include "../ui/keysyms.h" | 445 | #include "../ui/keysyms.h" |
| 446 | void attr_to_script(char *buf, struct perf_event_attr *attr); | ||
| 447 | |||
| 438 | int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, | 448 | int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, |
| 439 | struct hist_browser_timer *hbt, | 449 | struct hist_browser_timer *hbt, |
| 440 | struct annotation_options *annotation_opts); | 450 | struct annotation_options *annotation_opts); |
| @@ -449,7 +459,13 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | |||
| 449 | struct perf_env *env, | 459 | struct perf_env *env, |
| 450 | bool warn_lost_event, | 460 | bool warn_lost_event, |
| 451 | struct annotation_options *annotation_options); | 461 | struct annotation_options *annotation_options); |
| 452 | int script_browse(const char *script_opt); | 462 | |
| 463 | int script_browse(const char *script_opt, struct perf_evsel *evsel); | ||
| 464 | |||
| 465 | void run_script(char *cmd); | ||
| 466 | int res_sample_browse(struct res_sample *res_samples, int num_res, | ||
| 467 | struct perf_evsel *evsel, enum rstype rstype); | ||
| 468 | void res_sample_init(void); | ||
| 453 | #else | 469 | #else |
| 454 | static inline | 470 | static inline |
| 455 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | 471 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, |
| @@ -478,11 +494,22 @@ static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, | |||
| 478 | return 0; | 494 | return 0; |
| 479 | } | 495 | } |
| 480 | 496 | ||
| 481 | static inline int script_browse(const char *script_opt __maybe_unused) | 497 | static inline int script_browse(const char *script_opt __maybe_unused, |
| 498 | struct perf_evsel *evsel __maybe_unused) | ||
| 482 | { | 499 | { |
| 483 | return 0; | 500 | return 0; |
| 484 | } | 501 | } |
| 485 | 502 | ||
| 503 | static inline int res_sample_browse(struct res_sample *res_samples __maybe_unused, | ||
| 504 | int num_res __maybe_unused, | ||
| 505 | struct perf_evsel *evsel __maybe_unused, | ||
| 506 | enum rstype rstype __maybe_unused) | ||
| 507 | { | ||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | static inline void res_sample_init(void) {} | ||
| 512 | |||
| 486 | #define K_LEFT -1000 | 513 | #define K_LEFT -1000 |
| 487 | #define K_RIGHT -2000 | 514 | #define K_RIGHT -2000 |
| 488 | #define K_SWITCH_INPUT_DATA -3000 | 515 | #define K_SWITCH_INPUT_DATA -3000 |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 6e03db142091..872fab163585 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
| @@ -251,19 +251,15 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params) | |||
| 251 | if (!(decoder->tsc_ctc_ratio_n % decoder->tsc_ctc_ratio_d)) | 251 | if (!(decoder->tsc_ctc_ratio_n % decoder->tsc_ctc_ratio_d)) |
| 252 | decoder->tsc_ctc_mult = decoder->tsc_ctc_ratio_n / | 252 | decoder->tsc_ctc_mult = decoder->tsc_ctc_ratio_n / |
| 253 | decoder->tsc_ctc_ratio_d; | 253 | decoder->tsc_ctc_ratio_d; |
| 254 | |||
| 255 | /* | ||
| 256 | * Allow for timestamps appearing to backwards because a TSC | ||
| 257 | * packet has slipped past a MTC packet, so allow 2 MTC ticks | ||
| 258 | * or ... | ||
| 259 | */ | ||
| 260 | decoder->tsc_slip = multdiv(2 << decoder->mtc_shift, | ||
| 261 | decoder->tsc_ctc_ratio_n, | ||
| 262 | decoder->tsc_ctc_ratio_d); | ||
| 263 | } | 254 | } |
| 264 | /* ... or 0x100 paranoia */ | 255 | |
| 265 | if (decoder->tsc_slip < 0x100) | 256 | /* |
| 266 | decoder->tsc_slip = 0x100; | 257 | * A TSC packet can slip past MTC packets so that the timestamp appears |
| 258 | * to go backwards. One estimate is that can be up to about 40 CPU | ||
| 259 | * cycles, which is certainly less than 0x1000 TSC ticks, but accept | ||
| 260 | * slippage an order of magnitude more to be on the safe side. | ||
| 261 | */ | ||
| 262 | decoder->tsc_slip = 0x10000; | ||
| 267 | 263 | ||
| 268 | intel_pt_log("timestamp: mtc_shift %u\n", decoder->mtc_shift); | 264 | intel_pt_log("timestamp: mtc_shift %u\n", decoder->mtc_shift); |
| 269 | intel_pt_log("timestamp: tsc_ctc_ratio_n %u\n", decoder->tsc_ctc_ratio_n); | 265 | intel_pt_log("timestamp: tsc_ctc_ratio_n %u\n", decoder->tsc_ctc_ratio_n); |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 61959aba7e27..3c520baa198c 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -1421,6 +1421,20 @@ static void machine__set_kernel_mmap(struct machine *machine, | |||
| 1421 | machine->vmlinux_map->end = ~0ULL; | 1421 | machine->vmlinux_map->end = ~0ULL; |
| 1422 | } | 1422 | } |
| 1423 | 1423 | ||
| 1424 | static void machine__update_kernel_mmap(struct machine *machine, | ||
| 1425 | u64 start, u64 end) | ||
| 1426 | { | ||
| 1427 | struct map *map = machine__kernel_map(machine); | ||
| 1428 | |||
| 1429 | map__get(map); | ||
| 1430 | map_groups__remove(&machine->kmaps, map); | ||
| 1431 | |||
| 1432 | machine__set_kernel_mmap(machine, start, end); | ||
| 1433 | |||
| 1434 | map_groups__insert(&machine->kmaps, map); | ||
| 1435 | map__put(map); | ||
| 1436 | } | ||
| 1437 | |||
| 1424 | int machine__create_kernel_maps(struct machine *machine) | 1438 | int machine__create_kernel_maps(struct machine *machine) |
| 1425 | { | 1439 | { |
| 1426 | struct dso *kernel = machine__get_kernel(machine); | 1440 | struct dso *kernel = machine__get_kernel(machine); |
| @@ -1453,17 +1467,11 @@ int machine__create_kernel_maps(struct machine *machine) | |||
| 1453 | goto out_put; | 1467 | goto out_put; |
| 1454 | } | 1468 | } |
| 1455 | 1469 | ||
| 1456 | /* we have a real start address now, so re-order the kmaps */ | 1470 | /* |
| 1457 | map = machine__kernel_map(machine); | 1471 | * we have a real start address now, so re-order the kmaps |
| 1458 | 1472 | * assume it's the last in the kmaps | |
| 1459 | map__get(map); | 1473 | */ |
| 1460 | map_groups__remove(&machine->kmaps, map); | 1474 | machine__update_kernel_mmap(machine, addr, ~0ULL); |
| 1461 | |||
| 1462 | /* assume it's the last in the kmaps */ | ||
| 1463 | machine__set_kernel_mmap(machine, addr, ~0ULL); | ||
| 1464 | |||
| 1465 | map_groups__insert(&machine->kmaps, map); | ||
| 1466 | map__put(map); | ||
| 1467 | } | 1475 | } |
| 1468 | 1476 | ||
| 1469 | if (machine__create_extra_kernel_maps(machine, kernel)) | 1477 | if (machine__create_extra_kernel_maps(machine, kernel)) |
| @@ -1599,7 +1607,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1599 | if (strstr(kernel->long_name, "vmlinux")) | 1607 | if (strstr(kernel->long_name, "vmlinux")) |
| 1600 | dso__set_short_name(kernel, "[kernel.vmlinux]", false); | 1608 | dso__set_short_name(kernel, "[kernel.vmlinux]", false); |
| 1601 | 1609 | ||
| 1602 | machine__set_kernel_mmap(machine, event->mmap.start, | 1610 | machine__update_kernel_mmap(machine, event->mmap.start, |
| 1603 | event->mmap.start + event->mmap.len); | 1611 | event->mmap.start + event->mmap.len); |
| 1604 | 1612 | ||
| 1605 | /* | 1613 | /* |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index fbeb0c6efaa6..e32628cd20a7 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -577,10 +577,25 @@ static void __maps__purge(struct maps *maps) | |||
| 577 | } | 577 | } |
| 578 | } | 578 | } |
| 579 | 579 | ||
| 580 | static void __maps__purge_names(struct maps *maps) | ||
| 581 | { | ||
| 582 | struct rb_root *root = &maps->names; | ||
| 583 | struct rb_node *next = rb_first(root); | ||
| 584 | |||
| 585 | while (next) { | ||
| 586 | struct map *pos = rb_entry(next, struct map, rb_node_name); | ||
| 587 | |||
| 588 | next = rb_next(&pos->rb_node_name); | ||
| 589 | rb_erase_init(&pos->rb_node_name, root); | ||
| 590 | map__put(pos); | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 580 | static void maps__exit(struct maps *maps) | 594 | static void maps__exit(struct maps *maps) |
| 581 | { | 595 | { |
| 582 | down_write(&maps->lock); | 596 | down_write(&maps->lock); |
| 583 | __maps__purge(maps); | 597 | __maps__purge(maps); |
| 598 | __maps__purge_names(maps); | ||
| 584 | up_write(&maps->lock); | 599 | up_write(&maps->lock); |
| 585 | } | 600 | } |
| 586 | 601 | ||
| @@ -917,6 +932,9 @@ static void __maps__remove(struct maps *maps, struct map *map) | |||
| 917 | { | 932 | { |
| 918 | rb_erase_init(&map->rb_node, &maps->entries); | 933 | rb_erase_init(&map->rb_node, &maps->entries); |
| 919 | map__put(map); | 934 | map__put(map); |
| 935 | |||
| 936 | rb_erase_init(&map->rb_node_name, &maps->names); | ||
| 937 | map__put(map); | ||
| 920 | } | 938 | } |
| 921 | 939 | ||
| 922 | void maps__remove(struct maps *maps, struct map *map) | 940 | void maps__remove(struct maps *maps, struct map *map) |
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c index ea523d3b248f..989fed6f43b5 100644 --- a/tools/perf/util/ordered-events.c +++ b/tools/perf/util/ordered-events.c | |||
| @@ -270,6 +270,8 @@ static int __ordered_events__flush(struct ordered_events *oe, enum oe_flush how, | |||
| 270 | "FINAL", | 270 | "FINAL", |
| 271 | "ROUND", | 271 | "ROUND", |
| 272 | "HALF ", | 272 | "HALF ", |
| 273 | "TOP ", | ||
| 274 | "TIME ", | ||
| 273 | }; | 275 | }; |
| 274 | int err; | 276 | int err; |
| 275 | bool show_progress = false; | 277 | bool show_progress = false; |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4dcc01b2532c..5ef4939408f2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -2271,6 +2271,7 @@ static bool is_event_supported(u8 type, unsigned config) | |||
| 2271 | perf_evsel__delete(evsel); | 2271 | perf_evsel__delete(evsel); |
| 2272 | } | 2272 | } |
| 2273 | 2273 | ||
| 2274 | thread_map__put(tmap); | ||
| 2274 | return ret; | 2275 | return ret; |
| 2275 | } | 2276 | } |
| 2276 | 2277 | ||
| @@ -2341,6 +2342,7 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob, | |||
| 2341 | printf(" %-50s [%s]\n", buf, "SDT event"); | 2342 | printf(" %-50s [%s]\n", buf, "SDT event"); |
| 2342 | free(buf); | 2343 | free(buf); |
| 2343 | } | 2344 | } |
| 2345 | free(path); | ||
| 2344 | } else | 2346 | } else |
| 2345 | printf(" %-50s [%s]\n", nd->s, "SDT event"); | 2347 | printf(" %-50s [%s]\n", nd->s, "SDT event"); |
| 2346 | if (nd2) { | 2348 | if (nd2) { |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 6199a3174ab9..e0429f4ef335 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
| @@ -732,10 +732,20 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) | |||
| 732 | 732 | ||
| 733 | if (!is_arm_pmu_core(name)) { | 733 | if (!is_arm_pmu_core(name)) { |
| 734 | pname = pe->pmu ? pe->pmu : "cpu"; | 734 | pname = pe->pmu ? pe->pmu : "cpu"; |
| 735 | |||
| 736 | /* | ||
| 737 | * uncore alias may be from different PMU | ||
| 738 | * with common prefix | ||
| 739 | */ | ||
| 740 | if (pmu_is_uncore(name) && | ||
| 741 | !strncmp(pname, name, strlen(pname))) | ||
| 742 | goto new_alias; | ||
| 743 | |||
| 735 | if (strcmp(pname, name)) | 744 | if (strcmp(pname, name)) |
| 736 | continue; | 745 | continue; |
| 737 | } | 746 | } |
| 738 | 747 | ||
| 748 | new_alias: | ||
| 739 | /* need type casts to override 'const' */ | 749 | /* need type casts to override 'const' */ |
| 740 | __perf_pmu__new_alias(head, NULL, (char *)pe->name, | 750 | __perf_pmu__new_alias(head, NULL, (char *)pe->name, |
| 741 | (char *)pe->desc, (char *)pe->event, | 751 | (char *)pe->desc, (char *)pe->event, |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index a1b8d9649ca7..198e09ff611e 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -160,8 +160,10 @@ static struct map *kernel_get_module_map(const char *module) | |||
| 160 | if (module && strchr(module, '/')) | 160 | if (module && strchr(module, '/')) |
| 161 | return dso__new_map(module); | 161 | return dso__new_map(module); |
| 162 | 162 | ||
| 163 | if (!module) | 163 | if (!module) { |
| 164 | module = "kernel"; | 164 | pos = machine__kernel_map(host_machine); |
| 165 | return map__get(pos); | ||
| 166 | } | ||
| 165 | 167 | ||
| 166 | for (pos = maps__first(maps); pos; pos = map__next(pos)) { | 168 | for (pos = maps__first(maps); pos; pos = map__next(pos)) { |
| 167 | /* short_name is "[module]" */ | 169 | /* short_name is "[module]" */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index db643f3c2b95..b17f1c9bc965 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -132,6 +132,7 @@ struct perf_session *perf_session__new(struct perf_data *data, | |||
| 132 | ordered_events__init(&session->ordered_events, | 132 | ordered_events__init(&session->ordered_events, |
| 133 | ordered_events__deliver_event, NULL); | 133 | ordered_events__deliver_event, NULL); |
| 134 | 134 | ||
| 135 | perf_env__init(&session->header.env); | ||
| 135 | if (data) { | 136 | if (data) { |
| 136 | if (perf_data__open(data)) | 137 | if (perf_data__open(data)) |
| 137 | goto out_delete; | 138 | goto out_delete; |
| @@ -152,6 +153,10 @@ struct perf_session *perf_session__new(struct perf_data *data, | |||
| 152 | } | 153 | } |
| 153 | 154 | ||
| 154 | perf_evlist__init_trace_event_sample_raw(session->evlist); | 155 | perf_evlist__init_trace_event_sample_raw(session->evlist); |
| 156 | |||
| 157 | /* Open the directory data. */ | ||
| 158 | if (data->is_dir && perf_data__open_dir(data)) | ||
| 159 | goto out_delete; | ||
| 155 | } | 160 | } |
| 156 | } else { | 161 | } else { |
| 157 | session->machines.host.env = &perf_env; | 162 | session->machines.host.env = &perf_env; |
| @@ -1843,10 +1848,17 @@ fetch_mmaped_event(struct perf_session *session, | |||
| 1843 | #define NUM_MMAPS 128 | 1848 | #define NUM_MMAPS 128 |
| 1844 | #endif | 1849 | #endif |
| 1845 | 1850 | ||
| 1851 | struct reader; | ||
| 1852 | |||
| 1853 | typedef s64 (*reader_cb_t)(struct perf_session *session, | ||
| 1854 | union perf_event *event, | ||
| 1855 | u64 file_offset); | ||
| 1856 | |||
| 1846 | struct reader { | 1857 | struct reader { |
| 1847 | int fd; | 1858 | int fd; |
| 1848 | u64 data_size; | 1859 | u64 data_size; |
| 1849 | u64 data_offset; | 1860 | u64 data_offset; |
| 1861 | reader_cb_t process; | ||
| 1850 | }; | 1862 | }; |
| 1851 | 1863 | ||
| 1852 | static int | 1864 | static int |
| @@ -1917,7 +1929,7 @@ more: | |||
| 1917 | size = event->header.size; | 1929 | size = event->header.size; |
| 1918 | 1930 | ||
| 1919 | if (size < sizeof(struct perf_event_header) || | 1931 | if (size < sizeof(struct perf_event_header) || |
| 1920 | (skip = perf_session__process_event(session, event, file_pos)) < 0) { | 1932 | (skip = rd->process(session, event, file_pos)) < 0) { |
| 1921 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", | 1933 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", |
| 1922 | file_offset + head, event->header.size, | 1934 | file_offset + head, event->header.size, |
| 1923 | event->header.type); | 1935 | event->header.type); |
| @@ -1943,12 +1955,20 @@ out: | |||
| 1943 | return err; | 1955 | return err; |
| 1944 | } | 1956 | } |
| 1945 | 1957 | ||
| 1958 | static s64 process_simple(struct perf_session *session, | ||
| 1959 | union perf_event *event, | ||
| 1960 | u64 file_offset) | ||
| 1961 | { | ||
| 1962 | return perf_session__process_event(session, event, file_offset); | ||
| 1963 | } | ||
| 1964 | |||
| 1946 | static int __perf_session__process_events(struct perf_session *session) | 1965 | static int __perf_session__process_events(struct perf_session *session) |
| 1947 | { | 1966 | { |
| 1948 | struct reader rd = { | 1967 | struct reader rd = { |
| 1949 | .fd = perf_data__fd(session->data), | 1968 | .fd = perf_data__fd(session->data), |
| 1950 | .data_size = session->header.data_size, | 1969 | .data_size = session->header.data_size, |
| 1951 | .data_offset = session->header.data_offset, | 1970 | .data_offset = session->header.data_offset, |
| 1971 | .process = process_simple, | ||
| 1952 | }; | 1972 | }; |
| 1953 | struct ordered_events *oe = &session->ordered_events; | 1973 | struct ordered_events *oe = &session->ordered_events; |
| 1954 | struct perf_tool *tool = session->tool; | 1974 | struct perf_tool *tool = session->tool; |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d2299e912e59..5d2518e89fc4 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <inttypes.h> | 3 | #include <inttypes.h> |
| 4 | #include <regex.h> | 4 | #include <regex.h> |
| 5 | #include <linux/mman.h> | 5 | #include <linux/mman.h> |
| 6 | #include <linux/time64.h> | ||
| 6 | #include "sort.h" | 7 | #include "sort.h" |
| 7 | #include "hist.h" | 8 | #include "hist.h" |
| 8 | #include "comm.h" | 9 | #include "comm.h" |
| @@ -12,9 +13,11 @@ | |||
| 12 | #include "evsel.h" | 13 | #include "evsel.h" |
| 13 | #include "evlist.h" | 14 | #include "evlist.h" |
| 14 | #include "strlist.h" | 15 | #include "strlist.h" |
| 16 | #include "strbuf.h" | ||
| 15 | #include <traceevent/event-parse.h> | 17 | #include <traceevent/event-parse.h> |
| 16 | #include "mem-events.h" | 18 | #include "mem-events.h" |
| 17 | #include "annotate.h" | 19 | #include "annotate.h" |
| 20 | #include "time-utils.h" | ||
| 18 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
| 19 | 22 | ||
| 20 | regex_t parent_regex; | 23 | regex_t parent_regex; |
| @@ -654,6 +657,42 @@ struct sort_entry sort_socket = { | |||
| 654 | .se_width_idx = HISTC_SOCKET, | 657 | .se_width_idx = HISTC_SOCKET, |
| 655 | }; | 658 | }; |
| 656 | 659 | ||
| 660 | /* --sort time */ | ||
| 661 | |||
| 662 | static int64_t | ||
| 663 | sort__time_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 664 | { | ||
| 665 | return right->time - left->time; | ||
| 666 | } | ||
| 667 | |||
| 668 | static int hist_entry__time_snprintf(struct hist_entry *he, char *bf, | ||
| 669 | size_t size, unsigned int width) | ||
| 670 | { | ||
| 671 | unsigned long secs; | ||
| 672 | unsigned long long nsecs; | ||
| 673 | char he_time[32]; | ||
| 674 | |||
| 675 | nsecs = he->time; | ||
| 676 | secs = nsecs / NSEC_PER_SEC; | ||
| 677 | nsecs -= secs * NSEC_PER_SEC; | ||
| 678 | |||
| 679 | if (symbol_conf.nanosecs) | ||
| 680 | snprintf(he_time, sizeof he_time, "%5lu.%09llu: ", | ||
| 681 | secs, nsecs); | ||
| 682 | else | ||
| 683 | timestamp__scnprintf_usec(he->time, he_time, | ||
| 684 | sizeof(he_time)); | ||
| 685 | |||
| 686 | return repsep_snprintf(bf, size, "%-.*s", width, he_time); | ||
| 687 | } | ||
| 688 | |||
| 689 | struct sort_entry sort_time = { | ||
| 690 | .se_header = "Time", | ||
| 691 | .se_cmp = sort__time_cmp, | ||
| 692 | .se_snprintf = hist_entry__time_snprintf, | ||
| 693 | .se_width_idx = HISTC_TIME, | ||
| 694 | }; | ||
| 695 | |||
| 657 | /* --sort trace */ | 696 | /* --sort trace */ |
| 658 | 697 | ||
| 659 | static char *get_trace_output(struct hist_entry *he) | 698 | static char *get_trace_output(struct hist_entry *he) |
| @@ -1634,6 +1673,7 @@ static struct sort_dimension common_sort_dimensions[] = { | |||
| 1634 | DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size), | 1673 | DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size), |
| 1635 | DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id), | 1674 | DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id), |
| 1636 | DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null), | 1675 | DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null), |
| 1676 | DIM(SORT_TIME, "time", sort_time), | ||
| 1637 | }; | 1677 | }; |
| 1638 | 1678 | ||
| 1639 | #undef DIM | 1679 | #undef DIM |
| @@ -3068,3 +3108,54 @@ void reset_output_field(void) | |||
| 3068 | reset_dimensions(); | 3108 | reset_dimensions(); |
| 3069 | perf_hpp__reset_output_field(&perf_hpp_list); | 3109 | perf_hpp__reset_output_field(&perf_hpp_list); |
| 3070 | } | 3110 | } |
| 3111 | |||
| 3112 | #define INDENT (3*8 + 1) | ||
| 3113 | |||
| 3114 | static void add_key(struct strbuf *sb, const char *str, int *llen) | ||
| 3115 | { | ||
| 3116 | if (*llen >= 75) { | ||
| 3117 | strbuf_addstr(sb, "\n\t\t\t "); | ||
| 3118 | *llen = INDENT; | ||
| 3119 | } | ||
| 3120 | strbuf_addf(sb, " %s", str); | ||
| 3121 | *llen += strlen(str) + 1; | ||
| 3122 | } | ||
| 3123 | |||
| 3124 | static void add_sort_string(struct strbuf *sb, struct sort_dimension *s, int n, | ||
| 3125 | int *llen) | ||
| 3126 | { | ||
| 3127 | int i; | ||
| 3128 | |||
| 3129 | for (i = 0; i < n; i++) | ||
| 3130 | add_key(sb, s[i].name, llen); | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | static void add_hpp_sort_string(struct strbuf *sb, struct hpp_dimension *s, int n, | ||
| 3134 | int *llen) | ||
| 3135 | { | ||
| 3136 | int i; | ||
| 3137 | |||
| 3138 | for (i = 0; i < n; i++) | ||
| 3139 | add_key(sb, s[i].name, llen); | ||
| 3140 | } | ||
| 3141 | |||
| 3142 | const char *sort_help(const char *prefix) | ||
| 3143 | { | ||
| 3144 | struct strbuf sb; | ||
| 3145 | char *s; | ||
| 3146 | int len = strlen(prefix) + INDENT; | ||
| 3147 | |||
| 3148 | strbuf_init(&sb, 300); | ||
| 3149 | strbuf_addstr(&sb, prefix); | ||
| 3150 | add_hpp_sort_string(&sb, hpp_sort_dimensions, | ||
| 3151 | ARRAY_SIZE(hpp_sort_dimensions), &len); | ||
| 3152 | add_sort_string(&sb, common_sort_dimensions, | ||
| 3153 | ARRAY_SIZE(common_sort_dimensions), &len); | ||
| 3154 | add_sort_string(&sb, bstack_sort_dimensions, | ||
| 3155 | ARRAY_SIZE(bstack_sort_dimensions), &len); | ||
| 3156 | add_sort_string(&sb, memory_sort_dimensions, | ||
| 3157 | ARRAY_SIZE(memory_sort_dimensions), &len); | ||
| 3158 | s = strbuf_detach(&sb, NULL); | ||
| 3159 | strbuf_release(&sb); | ||
| 3160 | return s; | ||
| 3161 | } | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 2fbee0b1011c..ce376a73f964 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -47,6 +47,12 @@ extern struct sort_entry sort_srcline; | |||
| 47 | extern enum sort_type sort__first_dimension; | 47 | extern enum sort_type sort__first_dimension; |
| 48 | extern const char default_mem_sort_order[]; | 48 | extern const char default_mem_sort_order[]; |
| 49 | 49 | ||
| 50 | struct res_sample { | ||
| 51 | u64 time; | ||
| 52 | int cpu; | ||
| 53 | int tid; | ||
| 54 | }; | ||
| 55 | |||
| 50 | struct he_stat { | 56 | struct he_stat { |
| 51 | u64 period; | 57 | u64 period; |
| 52 | u64 period_sys; | 58 | u64 period_sys; |
| @@ -135,10 +141,13 @@ struct hist_entry { | |||
| 135 | char *srcfile; | 141 | char *srcfile; |
| 136 | struct symbol *parent; | 142 | struct symbol *parent; |
| 137 | struct branch_info *branch_info; | 143 | struct branch_info *branch_info; |
| 144 | long time; | ||
| 138 | struct hists *hists; | 145 | struct hists *hists; |
| 139 | struct mem_info *mem_info; | 146 | struct mem_info *mem_info; |
| 140 | void *raw_data; | 147 | void *raw_data; |
| 141 | u32 raw_size; | 148 | u32 raw_size; |
| 149 | int num_res; | ||
| 150 | struct res_sample *res_samples; | ||
| 142 | void *trace_output; | 151 | void *trace_output; |
| 143 | struct perf_hpp_list *hpp_list; | 152 | struct perf_hpp_list *hpp_list; |
| 144 | struct hist_entry *parent_he; | 153 | struct hist_entry *parent_he; |
| @@ -231,6 +240,7 @@ enum sort_type { | |||
| 231 | SORT_DSO_SIZE, | 240 | SORT_DSO_SIZE, |
| 232 | SORT_CGROUP_ID, | 241 | SORT_CGROUP_ID, |
| 233 | SORT_SYM_IPC_NULL, | 242 | SORT_SYM_IPC_NULL, |
| 243 | SORT_TIME, | ||
| 234 | 244 | ||
| 235 | /* branch stack specific sort keys */ | 245 | /* branch stack specific sort keys */ |
| 236 | __SORT_BRANCH_STACK, | 246 | __SORT_BRANCH_STACK, |
| @@ -286,6 +296,8 @@ void reset_output_field(void); | |||
| 286 | void sort__setup_elide(FILE *fp); | 296 | void sort__setup_elide(FILE *fp); |
| 287 | void perf_hpp__set_elide(int idx, bool elide); | 297 | void perf_hpp__set_elide(int idx, bool elide); |
| 288 | 298 | ||
| 299 | const char *sort_help(const char *prefix); | ||
| 300 | |||
| 289 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); | 301 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); |
| 290 | 302 | ||
| 291 | bool is_strict_order(const char *order); | 303 | bool is_strict_order(const char *order); |
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 4d40515307b8..2856cc9d5a31 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
| @@ -291,10 +291,8 @@ process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel | |||
| 291 | break; | 291 | break; |
| 292 | case AGGR_GLOBAL: | 292 | case AGGR_GLOBAL: |
| 293 | aggr->val += count->val; | 293 | aggr->val += count->val; |
| 294 | if (config->scale) { | 294 | aggr->ena += count->ena; |
| 295 | aggr->ena += count->ena; | 295 | aggr->run += count->run; |
| 296 | aggr->run += count->run; | ||
| 297 | } | ||
| 298 | case AGGR_UNSET: | 296 | case AGGR_UNSET: |
| 299 | default: | 297 | default: |
| 300 | break; | 298 | break; |
| @@ -442,10 +440,8 @@ int create_perf_stat_counter(struct perf_evsel *evsel, | |||
| 442 | struct perf_event_attr *attr = &evsel->attr; | 440 | struct perf_event_attr *attr = &evsel->attr; |
| 443 | struct perf_evsel *leader = evsel->leader; | 441 | struct perf_evsel *leader = evsel->leader; |
| 444 | 442 | ||
| 445 | if (config->scale) { | 443 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
| 446 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 444 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
| 447 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
| 448 | } | ||
| 449 | 445 | ||
| 450 | /* | 446 | /* |
| 451 | * The event is part of non trivial group, let's enable | 447 | * The event is part of non trivial group, let's enable |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 758bf5f74e6e..5cbad55cd99d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <string.h> | 6 | #include <string.h> |
| 7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
| 8 | #include <linux/mman.h> | 8 | #include <linux/mman.h> |
| 9 | #include <linux/time64.h> | ||
| 9 | #include <sys/types.h> | 10 | #include <sys/types.h> |
| 10 | #include <sys/stat.h> | 11 | #include <sys/stat.h> |
| 11 | #include <sys/param.h> | 12 | #include <sys/param.h> |
| @@ -39,15 +40,18 @@ int vmlinux_path__nr_entries; | |||
| 39 | char **vmlinux_path; | 40 | char **vmlinux_path; |
| 40 | 41 | ||
| 41 | struct symbol_conf symbol_conf = { | 42 | struct symbol_conf symbol_conf = { |
| 43 | .nanosecs = false, | ||
| 42 | .use_modules = true, | 44 | .use_modules = true, |
| 43 | .try_vmlinux_path = true, | 45 | .try_vmlinux_path = true, |
| 44 | .demangle = true, | 46 | .demangle = true, |
| 45 | .demangle_kernel = false, | 47 | .demangle_kernel = false, |
| 46 | .cumulate_callchain = true, | 48 | .cumulate_callchain = true, |
| 49 | .time_quantum = 100 * NSEC_PER_MSEC, /* 100ms */ | ||
| 47 | .show_hist_headers = true, | 50 | .show_hist_headers = true, |
| 48 | .symfs = "", | 51 | .symfs = "", |
| 49 | .event_group = true, | 52 | .event_group = true, |
| 50 | .inline_name = true, | 53 | .inline_name = true, |
| 54 | .res_sample = 0, | ||
| 51 | }; | 55 | }; |
| 52 | 56 | ||
| 53 | static enum dso_binary_type binary_type_symtab[] = { | 57 | static enum dso_binary_type binary_type_symtab[] = { |
| @@ -1451,6 +1455,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, | |||
| 1451 | case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO: | 1455 | case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO: |
| 1452 | return true; | 1456 | return true; |
| 1453 | 1457 | ||
| 1458 | case DSO_BINARY_TYPE__BPF_PROG_INFO: | ||
| 1454 | case DSO_BINARY_TYPE__NOT_FOUND: | 1459 | case DSO_BINARY_TYPE__NOT_FOUND: |
| 1455 | default: | 1460 | default: |
| 1456 | return false; | 1461 | return false; |
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index fffea68c1203..6c55fa6fccec 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h | |||
| @@ -8,6 +8,7 @@ struct strlist; | |||
| 8 | struct intlist; | 8 | struct intlist; |
| 9 | 9 | ||
| 10 | struct symbol_conf { | 10 | struct symbol_conf { |
| 11 | bool nanosecs; | ||
| 11 | unsigned short priv_size; | 12 | unsigned short priv_size; |
| 12 | bool try_vmlinux_path, | 13 | bool try_vmlinux_path, |
| 13 | init_annotation, | 14 | init_annotation, |
| @@ -55,6 +56,7 @@ struct symbol_conf { | |||
| 55 | *sym_list_str, | 56 | *sym_list_str, |
| 56 | *col_width_list_str, | 57 | *col_width_list_str, |
| 57 | *bt_stop_list_str; | 58 | *bt_stop_list_str; |
| 59 | unsigned long time_quantum; | ||
| 58 | struct strlist *dso_list, | 60 | struct strlist *dso_list, |
| 59 | *comm_list, | 61 | *comm_list, |
| 60 | *sym_list, | 62 | *sym_list, |
| @@ -66,6 +68,7 @@ struct symbol_conf { | |||
| 66 | struct intlist *pid_list, | 68 | struct intlist *pid_list, |
| 67 | *tid_list; | 69 | *tid_list; |
| 68 | const char *symfs; | 70 | const char *symfs; |
| 71 | int res_sample; | ||
| 69 | }; | 72 | }; |
| 70 | 73 | ||
| 71 | extern struct symbol_conf symbol_conf; | 74 | extern struct symbol_conf symbol_conf; |
diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 0f53baec660e..20663a460df3 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c | |||
| @@ -453,6 +453,14 @@ int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) | |||
| 453 | return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); | 453 | return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); |
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz) | ||
| 457 | { | ||
| 458 | u64 sec = timestamp / NSEC_PER_SEC, | ||
| 459 | nsec = timestamp % NSEC_PER_SEC; | ||
| 460 | |||
| 461 | return scnprintf(buf, sz, "%" PRIu64 ".%09" PRIu64, sec, nsec); | ||
| 462 | } | ||
| 463 | |||
| 456 | int fetch_current_timestamp(char *buf, size_t sz) | 464 | int fetch_current_timestamp(char *buf, size_t sz) |
| 457 | { | 465 | { |
| 458 | struct timeval tv; | 466 | struct timeval tv; |
diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h index b923de44e36f..72a42ea1d513 100644 --- a/tools/perf/util/time-utils.h +++ b/tools/perf/util/time-utils.h | |||
| @@ -30,6 +30,7 @@ int perf_time__parse_for_ranges(const char *str, struct perf_session *session, | |||
| 30 | int *range_size, int *range_num); | 30 | int *range_size, int *range_num); |
| 31 | 31 | ||
| 32 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); | 32 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); |
| 33 | int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz); | ||
| 33 | 34 | ||
| 34 | int fetch_current_timestamp(char *buf, size_t sz); | 35 | int fetch_current_timestamp(char *buf, size_t sz); |
| 35 | 36 | ||
