diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 142 |
1 files changed, 97 insertions, 45 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ebde59e61133..adbf28183560 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -60,6 +60,7 @@ | |||
60 | #include <linux/stringify.h> | 60 | #include <linux/stringify.h> |
61 | #include <linux/time64.h> | 61 | #include <linux/time64.h> |
62 | #include <fcntl.h> | 62 | #include <fcntl.h> |
63 | #include <sys/sysmacros.h> | ||
63 | 64 | ||
64 | #include "sane_ctype.h" | 65 | #include "sane_ctype.h" |
65 | 66 | ||
@@ -112,8 +113,9 @@ struct trace { | |||
112 | } stats; | 113 | } stats; |
113 | unsigned int max_stack; | 114 | unsigned int max_stack; |
114 | unsigned int min_stack; | 115 | unsigned int min_stack; |
115 | bool sort_events; | 116 | int raw_augmented_syscalls_args_size; |
116 | bool raw_augmented_syscalls; | 117 | bool raw_augmented_syscalls; |
118 | bool sort_events; | ||
117 | bool not_ev_qualifier; | 119 | bool not_ev_qualifier; |
118 | bool live; | 120 | bool live; |
119 | bool full_time; | 121 | bool full_time; |
@@ -283,12 +285,17 @@ out_delete: | |||
283 | return -ENOENT; | 285 | return -ENOENT; |
284 | } | 286 | } |
285 | 287 | ||
286 | static int perf_evsel__init_augmented_syscall_tp(struct perf_evsel *evsel) | 288 | static int perf_evsel__init_augmented_syscall_tp(struct perf_evsel *evsel, struct perf_evsel *tp) |
287 | { | 289 | { |
288 | struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); | 290 | struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); |
289 | 291 | ||
290 | if (evsel->priv != NULL) { /* field, sizeof_field, offsetof_field */ | 292 | if (evsel->priv != NULL) { |
291 | if (__tp_field__init_uint(&sc->id, sizeof(long), sizeof(long long), evsel->needs_swap)) | 293 | struct tep_format_field *syscall_id = perf_evsel__field(tp, "id"); |
294 | if (syscall_id == NULL) | ||
295 | syscall_id = perf_evsel__field(tp, "__syscall_nr"); | ||
296 | if (syscall_id == NULL) | ||
297 | goto out_delete; | ||
298 | if (__tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap)) | ||
292 | goto out_delete; | 299 | goto out_delete; |
293 | 300 | ||
294 | return 0; | 301 | return 0; |
@@ -974,9 +981,9 @@ struct thread_trace { | |||
974 | char *name; | 981 | char *name; |
975 | } filename; | 982 | } filename; |
976 | struct { | 983 | struct { |
977 | int max; | 984 | int max; |
978 | char **table; | 985 | struct file *table; |
979 | } paths; | 986 | } files; |
980 | 987 | ||
981 | struct intlist *syscall_stats; | 988 | struct intlist *syscall_stats; |
982 | }; | 989 | }; |
@@ -986,7 +993,7 @@ static struct thread_trace *thread_trace__new(void) | |||
986 | struct thread_trace *ttrace = zalloc(sizeof(struct thread_trace)); | 993 | struct thread_trace *ttrace = zalloc(sizeof(struct thread_trace)); |
987 | 994 | ||
988 | if (ttrace) | 995 | if (ttrace) |
989 | ttrace->paths.max = -1; | 996 | ttrace->files.max = -1; |
990 | 997 | ||
991 | ttrace->syscall_stats = intlist__new(NULL); | 998 | ttrace->syscall_stats = intlist__new(NULL); |
992 | 999 | ||
@@ -1030,30 +1037,48 @@ void syscall_arg__set_ret_scnprintf(struct syscall_arg *arg, | |||
1030 | 1037 | ||
1031 | static const size_t trace__entry_str_size = 2048; | 1038 | static const size_t trace__entry_str_size = 2048; |
1032 | 1039 | ||
1033 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) | 1040 | static struct file *thread_trace__files_entry(struct thread_trace *ttrace, int fd) |
1034 | { | 1041 | { |
1035 | struct thread_trace *ttrace = thread__priv(thread); | 1042 | if (fd > ttrace->files.max) { |
1036 | 1043 | struct file *nfiles = realloc(ttrace->files.table, (fd + 1) * sizeof(struct file)); | |
1037 | if (fd > ttrace->paths.max) { | ||
1038 | char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); | ||
1039 | 1044 | ||
1040 | if (npath == NULL) | 1045 | if (nfiles == NULL) |
1041 | return -1; | 1046 | return NULL; |
1042 | 1047 | ||
1043 | if (ttrace->paths.max != -1) { | 1048 | if (ttrace->files.max != -1) { |
1044 | memset(npath + ttrace->paths.max + 1, 0, | 1049 | memset(nfiles + ttrace->files.max + 1, 0, |
1045 | (fd - ttrace->paths.max) * sizeof(char *)); | 1050 | (fd - ttrace->files.max) * sizeof(struct file)); |
1046 | } else { | 1051 | } else { |
1047 | memset(npath, 0, (fd + 1) * sizeof(char *)); | 1052 | memset(nfiles, 0, (fd + 1) * sizeof(struct file)); |
1048 | } | 1053 | } |
1049 | 1054 | ||
1050 | ttrace->paths.table = npath; | 1055 | ttrace->files.table = nfiles; |
1051 | ttrace->paths.max = fd; | 1056 | ttrace->files.max = fd; |
1052 | } | 1057 | } |
1053 | 1058 | ||
1054 | ttrace->paths.table[fd] = strdup(pathname); | 1059 | return ttrace->files.table + fd; |
1060 | } | ||
1055 | 1061 | ||
1056 | return ttrace->paths.table[fd] != NULL ? 0 : -1; | 1062 | struct file *thread__files_entry(struct thread *thread, int fd) |
1063 | { | ||
1064 | return thread_trace__files_entry(thread__priv(thread), fd); | ||
1065 | } | ||
1066 | |||
1067 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) | ||
1068 | { | ||
1069 | struct thread_trace *ttrace = thread__priv(thread); | ||
1070 | struct file *file = thread_trace__files_entry(ttrace, fd); | ||
1071 | |||
1072 | if (file != NULL) { | ||
1073 | struct stat st; | ||
1074 | if (stat(pathname, &st) == 0) | ||
1075 | file->dev_maj = major(st.st_rdev); | ||
1076 | file->pathname = strdup(pathname); | ||
1077 | if (file->pathname) | ||
1078 | return 0; | ||
1079 | } | ||
1080 | |||
1081 | return -1; | ||
1057 | } | 1082 | } |
1058 | 1083 | ||
1059 | static int thread__read_fd_path(struct thread *thread, int fd) | 1084 | static int thread__read_fd_path(struct thread *thread, int fd) |
@@ -1093,7 +1118,7 @@ static const char *thread__fd_path(struct thread *thread, int fd, | |||
1093 | if (fd < 0) | 1118 | if (fd < 0) |
1094 | return NULL; | 1119 | return NULL; |
1095 | 1120 | ||
1096 | if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) { | 1121 | if ((fd > ttrace->files.max || ttrace->files.table[fd].pathname == NULL)) { |
1097 | if (!trace->live) | 1122 | if (!trace->live) |
1098 | return NULL; | 1123 | return NULL; |
1099 | ++trace->stats.proc_getname; | 1124 | ++trace->stats.proc_getname; |
@@ -1101,7 +1126,7 @@ static const char *thread__fd_path(struct thread *thread, int fd, | |||
1101 | return NULL; | 1126 | return NULL; |
1102 | } | 1127 | } |
1103 | 1128 | ||
1104 | return ttrace->paths.table[fd]; | 1129 | return ttrace->files.table[fd].pathname; |
1105 | } | 1130 | } |
1106 | 1131 | ||
1107 | size_t syscall_arg__scnprintf_fd(char *bf, size_t size, struct syscall_arg *arg) | 1132 | size_t syscall_arg__scnprintf_fd(char *bf, size_t size, struct syscall_arg *arg) |
@@ -1140,8 +1165,8 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, | |||
1140 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); | 1165 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); |
1141 | struct thread_trace *ttrace = thread__priv(arg->thread); | 1166 | struct thread_trace *ttrace = thread__priv(arg->thread); |
1142 | 1167 | ||
1143 | if (ttrace && fd >= 0 && fd <= ttrace->paths.max) | 1168 | if (ttrace && fd >= 0 && fd <= ttrace->files.max) |
1144 | zfree(&ttrace->paths.table[fd]); | 1169 | zfree(&ttrace->files.table[fd].pathname); |
1145 | 1170 | ||
1146 | return printed; | 1171 | return printed; |
1147 | } | 1172 | } |
@@ -1768,16 +1793,16 @@ static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel, | |||
1768 | return printed; | 1793 | return printed; |
1769 | } | 1794 | } |
1770 | 1795 | ||
1771 | static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size, bool raw_augmented) | 1796 | static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sample, int *augmented_args_size, int raw_augmented_args_size) |
1772 | { | 1797 | { |
1773 | void *augmented_args = NULL; | 1798 | void *augmented_args = NULL; |
1774 | /* | 1799 | /* |
1775 | * For now with BPF raw_augmented we hook into raw_syscalls:sys_enter | 1800 | * For now with BPF raw_augmented we hook into raw_syscalls:sys_enter |
1776 | * and there we get all 6 syscall args plus the tracepoint common | 1801 | * and there we get all 6 syscall args plus the tracepoint common fields |
1777 | * fields (sizeof(long)) and the syscall_nr (another long). So we check | 1802 | * that gets calculated at the start and the syscall_nr (another long). |
1778 | * if that is the case and if so don't look after the sc->args_size, | 1803 | * So we check if that is the case and if so don't look after the |
1779 | * but always after the full raw_syscalls:sys_enter payload, which is | 1804 | * sc->args_size but always after the full raw_syscalls:sys_enter payload, |
1780 | * fixed. | 1805 | * which is fixed. |
1781 | * | 1806 | * |
1782 | * We'll revisit this later to pass s->args_size to the BPF augmenter | 1807 | * We'll revisit this later to pass s->args_size to the BPF augmenter |
1783 | * (now tools/perf/examples/bpf/augmented_raw_syscalls.c, so that it | 1808 | * (now tools/perf/examples/bpf/augmented_raw_syscalls.c, so that it |
@@ -1785,7 +1810,7 @@ static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sam | |||
1785 | * use syscalls:sys_enter_NAME, so that we reduce the kernel/userspace | 1810 | * use syscalls:sys_enter_NAME, so that we reduce the kernel/userspace |
1786 | * traffic to just what is needed for each syscall. | 1811 | * traffic to just what is needed for each syscall. |
1787 | */ | 1812 | */ |
1788 | int args_size = raw_augmented ? (8 * (int)sizeof(long)) : sc->args_size; | 1813 | int args_size = raw_augmented_args_size ?: sc->args_size; |
1789 | 1814 | ||
1790 | *augmented_args_size = sample->raw_size - args_size; | 1815 | *augmented_args_size = sample->raw_size - args_size; |
1791 | if (*augmented_args_size > 0) | 1816 | if (*augmented_args_size > 0) |
@@ -1839,7 +1864,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1839 | * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. | 1864 | * here and avoid using augmented syscalls when the evsel is the raw_syscalls one. |
1840 | */ | 1865 | */ |
1841 | if (evsel != trace->syscalls.events.sys_enter) | 1866 | if (evsel != trace->syscalls.events.sys_enter) |
1842 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls); | 1867 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); |
1843 | ttrace->entry_time = sample->time; | 1868 | ttrace->entry_time = sample->time; |
1844 | msg = ttrace->entry_str; | 1869 | msg = ttrace->entry_str; |
1845 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); | 1870 | printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); |
@@ -1897,7 +1922,7 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evse | |||
1897 | goto out_put; | 1922 | goto out_put; |
1898 | 1923 | ||
1899 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); | 1924 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
1900 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls); | 1925 | augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); |
1901 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); | 1926 | syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); |
1902 | fprintf(trace->output, "%s", msg); | 1927 | fprintf(trace->output, "%s", msg); |
1903 | err = 0; | 1928 | err = 0; |
@@ -2686,7 +2711,9 @@ static int trace__set_ev_qualifier_filter(struct trace *trace) | |||
2686 | { | 2711 | { |
2687 | if (trace->syscalls.map) | 2712 | if (trace->syscalls.map) |
2688 | return trace__set_ev_qualifier_bpf_filter(trace); | 2713 | return trace__set_ev_qualifier_bpf_filter(trace); |
2689 | return trace__set_ev_qualifier_tp_filter(trace); | 2714 | if (trace->syscalls.events.sys_enter) |
2715 | return trace__set_ev_qualifier_tp_filter(trace); | ||
2716 | return 0; | ||
2690 | } | 2717 | } |
2691 | 2718 | ||
2692 | static int bpf_map__set_filter_pids(struct bpf_map *map __maybe_unused, | 2719 | static int bpf_map__set_filter_pids(struct bpf_map *map __maybe_unused, |
@@ -3812,13 +3839,6 @@ int cmd_trace(int argc, const char **argv) | |||
3812 | * syscall. | 3839 | * syscall. |
3813 | */ | 3840 | */ |
3814 | if (trace.syscalls.events.augmented) { | 3841 | if (trace.syscalls.events.augmented) { |
3815 | evsel = trace.syscalls.events.augmented; | ||
3816 | |||
3817 | if (perf_evsel__init_augmented_syscall_tp(evsel) || | ||
3818 | perf_evsel__init_augmented_syscall_tp_args(evsel)) | ||
3819 | goto out; | ||
3820 | evsel->handler = trace__sys_enter; | ||
3821 | |||
3822 | evlist__for_each_entry(trace.evlist, evsel) { | 3842 | evlist__for_each_entry(trace.evlist, evsel) { |
3823 | bool raw_syscalls_sys_exit = strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_exit") == 0; | 3843 | bool raw_syscalls_sys_exit = strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_exit") == 0; |
3824 | 3844 | ||
@@ -3827,9 +3847,41 @@ int cmd_trace(int argc, const char **argv) | |||
3827 | goto init_augmented_syscall_tp; | 3847 | goto init_augmented_syscall_tp; |
3828 | } | 3848 | } |
3829 | 3849 | ||
3850 | if (strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_enter") == 0) { | ||
3851 | struct perf_evsel *augmented = trace.syscalls.events.augmented; | ||
3852 | if (perf_evsel__init_augmented_syscall_tp(augmented, evsel) || | ||
3853 | perf_evsel__init_augmented_syscall_tp_args(augmented)) | ||
3854 | goto out; | ||
3855 | augmented->handler = trace__sys_enter; | ||
3856 | } | ||
3857 | |||
3830 | if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) { | 3858 | if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) { |
3859 | struct syscall_tp *sc; | ||
3831 | init_augmented_syscall_tp: | 3860 | init_augmented_syscall_tp: |
3832 | perf_evsel__init_augmented_syscall_tp(evsel); | 3861 | if (perf_evsel__init_augmented_syscall_tp(evsel, evsel)) |
3862 | goto out; | ||
3863 | sc = evsel->priv; | ||
3864 | /* | ||
3865 | * For now with BPF raw_augmented we hook into | ||
3866 | * raw_syscalls:sys_enter and there we get all | ||
3867 | * 6 syscall args plus the tracepoint common | ||
3868 | * fields and the syscall_nr (another long). | ||
3869 | * So we check if that is the case and if so | ||
3870 | * don't look after the sc->args_size but | ||
3871 | * always after the full raw_syscalls:sys_enter | ||
3872 | * payload, which is fixed. | ||
3873 | * | ||
3874 | * We'll revisit this later to pass | ||
3875 | * s->args_size to the BPF augmenter (now | ||
3876 | * tools/perf/examples/bpf/augmented_raw_syscalls.c, | ||
3877 | * so that it copies only what we need for each | ||
3878 | * syscall, like what happens when we use | ||
3879 | * syscalls:sys_enter_NAME, so that we reduce | ||
3880 | * the kernel/userspace traffic to just what is | ||
3881 | * needed for each syscall. | ||
3882 | */ | ||
3883 | if (trace.raw_augmented_syscalls) | ||
3884 | trace.raw_augmented_syscalls_args_size = (6 + 1) * sizeof(long) + sc->id.offset; | ||
3833 | perf_evsel__init_augmented_syscall_tp_ret(evsel); | 3885 | perf_evsel__init_augmented_syscall_tp_ret(evsel); |
3834 | evsel->handler = trace__sys_exit; | 3886 | evsel->handler = trace__sys_exit; |
3835 | } | 3887 | } |