1 #include "builtin.h" 2 #include "util/evlist.h" 3 #include "util/parse-options.h" 4 #include "util/thread_map.h" 5 #include "event-parse.h" 6 7 #include <libaudit.h> 8 #include <stdlib.h> 9 10 static struct syscall_fmt { 11 const char *name; 12 const char *alias; 13 bool errmsg; 14 bool timeout; 15 } syscall_fmts[] = { 16 { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, 17 { .name = "fstat", .errmsg = true, .alias = "newfstat", }, 18 { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, 19 { .name = "futex", .errmsg = true, }, 20 { .name = "poll", .errmsg = true, .timeout = true, }, 21 { .name = "ppoll", .errmsg = true, .timeout = true, }, 22 { .name = "read", .errmsg = true, }, 23 { .name = "recvfrom", .errmsg = true, }, 24 { .name = "select", .errmsg = true, .timeout = true, }, 25 { .name = "stat", .errmsg = true, .alias = "newstat", }, 26 }; 27 28 static int syscall_fmt__cmp(const void *name, const void *fmtp) 29 { 30 const struct syscall_fmt *fmt = fmtp; 31 return strcmp(name, fmt->name); 32 } 33 34 static struct syscall_fmt *syscall_fmt__find(const char *name) 35 { 36 const int nmemb = ARRAY_SIZE(syscall_fmts); 37 return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp); 38 } 39 40 struct syscall { 41 struct event_format *tp_format; 42 const char *name; 43 struct syscall_fmt *fmt; 44 }; 45 46 struct trace { 47 int audit_machine; 48 struct { 49 int max; 50 struct syscall *table; 51 } syscalls; 52 struct perf_record_opts opts; 53 }; 54 55 static int trace__read_syscall_info(struct trace *trace, int id) 56 { 57 char tp_name[128]; 58 struct syscall *sc; 59 60 if (id > trace->syscalls.max) { 61 struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); 62 63 if (nsyscalls == NULL) 64 return -1; 65 66 if (trace->syscalls.max != -1) { 67 memset(nsyscalls + trace->syscalls.max + 1, 0, 68 (id - trace->syscalls.max) * sizeof(*sc)); 69 } else { 70 memset(nsyscalls, 0, (id + 1) * sizeof(*sc)); 71 } 72 73 trace->syscalls.table = nsyscalls; 74 trace->syscalls.max = id; 75 } 76 77 sc = trace->syscalls.table + id; 78 sc->name = audit_syscall_to_name(id, trace->audit_machine); 79 if (sc->name == NULL) 80 return -1; 81 82 sc->fmt = syscall_fmt__find(sc->name); 83 84 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); 85 sc->tp_format = event_format__new("syscalls", tp_name); 86 87 if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) { 88 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias); 89 sc->tp_format = event_format__new("syscalls", tp_name); 90 } 91 92 return sc->tp_format != NULL ? 0 : -1; 93 } 94 95 static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp) 96 { 97 int i = 0; 98 size_t printed = 0; 99 100 if (sc->tp_format != NULL) { 101 struct format_field *field; 102 103 for (field = sc->tp_format->format.fields->next; field; field = field->next) { 104 printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "", 105 field->name, args[i++]); 106 } 107 } else { 108 while (i < 6) { 109 printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]); 110 ++i; 111 } 112 } 113 114 return printed; 115 } 116 117 static int trace__run(struct trace *trace) 118 { 119 struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); 120 struct perf_evsel *evsel, *evsel_enter, *evsel_exit; 121 int err = -1, i, nr_events = 0, before; 122 123 if (evlist == NULL) { 124 printf("Not enough memory to run!\n"); 125 goto out; 126 } 127 128 evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0); 129 if (evsel_enter == NULL) { 130 printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n"); 131 goto out_delete_evlist; 132 } 133 134 perf_evlist__add(evlist, evsel_enter); 135 136 evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1); 137 if (evsel_exit == NULL) { 138 printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n"); 139 goto out_delete_evlist; 140 } 141 142 perf_evlist__add(evlist, evsel_exit); 143 144 err = perf_evlist__create_maps(evlist, &trace->opts.target); 145 if (err < 0) { 146 printf("Problems parsing the target to trace, check your options!\n"); 147 goto out_delete_evlist; 148 } 149 150 perf_evlist__config_attrs(evlist, &trace->opts); 151 152 err = perf_evlist__open(evlist); 153 if (err < 0) { 154 printf("Couldn't create the events: %s\n", strerror(errno)); 155 goto out_delete_evlist; 156 } 157 158 err = perf_evlist__mmap(evlist, UINT_MAX, false); 159 if (err < 0) { 160 printf("Couldn't mmap the events: %s\n", strerror(errno)); 161 goto out_delete_evlist; 162 } 163 164 perf_evlist__enable(evlist); 165 again: 166 before = nr_events; 167 168 for (i = 0; i < evlist->nr_mmaps; i++) { 169 union perf_event *event; 170 171 while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { 172 const u32 type = event->header.type; 173 struct syscall *sc; 174 struct perf_sample sample; 175 int id; 176 177 ++nr_events; 178 179 switch (type) { 180 case PERF_RECORD_SAMPLE: 181 break; 182 case PERF_RECORD_LOST: 183 printf("LOST %" PRIu64 " events!\n", event->lost.lost); 184 continue; 185 default: 186 printf("Unexpected %s event, skipping...\n", 187 perf_event__name(type)); 188 continue; 189 } 190 191 err = perf_evlist__parse_sample(evlist, event, &sample); 192 if (err) { 193 printf("Can't parse sample, err = %d, skipping...\n", err); 194 continue; 195 } 196 197 evsel = perf_evlist__id2evsel(evlist, sample.id); 198 if (evsel == NULL) { 199 printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); 200 continue; 201 } 202 203 id = perf_evsel__intval(evsel, &sample, "id"); 204 if (id < 0) { 205 printf("Invalid syscall %d id, skipping...\n", id); 206 continue; 207 } 208 209 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && 210 trace__read_syscall_info(trace, id)) 211 continue; 212 213 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) 214 continue; 215 216 sc = &trace->syscalls.table[id]; 217 218 if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) 219 printf("%d ", sample.tid); 220 221 if (evsel == evsel_enter) { 222 void *args = perf_evsel__rawptr(evsel, &sample, "args"); 223 224 printf("%s(", sc->name); 225 syscall__fprintf_args(sc, args, stdout); 226 } else if (evsel == evsel_exit) { 227 int ret = perf_evsel__intval(evsel, &sample, "ret"); 228 229 if (ret < 0 && sc->fmt && sc->fmt->errmsg) { 230 char bf[256]; 231 const char *emsg = strerror_r(-ret, bf, sizeof(bf)), 232 *e = audit_errno_to_name(-ret); 233 234 printf(") = -1 %s %s", e, emsg); 235 } else if (ret == 0 && sc->fmt && sc->fmt->timeout) 236 printf(") = 0 Timeout"); 237 else 238 printf(") = %d", ret); 239 240 putchar('\n'); 241 } 242 } 243 } 244 245 if (nr_events == before) 246 poll(evlist->pollfd, evlist->nr_fds, -1); 247 248 goto again; 249 250 out_delete_evlist: 251 perf_evlist__delete(evlist); 252 out: 253 return err; 254 } 255 256 int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) 257 { 258 const char * const trace_usage[] = { 259 "perf trace [<options>]", 260 NULL 261 }; 262 struct trace trace = { 263 .audit_machine = audit_detect_machine(), 264 .syscalls = { 265 . max = -1, 266 }, 267 .opts = { 268 .target = { 269 .uid = UINT_MAX, 270 .uses_mmap = true, 271 }, 272 .user_freq = UINT_MAX, 273 .user_interval = ULLONG_MAX, 274 .no_delay = true, 275 .mmap_pages = 1024, 276 }, 277 }; 278 const struct option trace_options[] = { 279 OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", 280 "trace events on existing process id"), 281 OPT_STRING(0, "tid", &trace.opts.target.tid, "tid", 282 "trace events on existing thread id"), 283 OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide, 284 "system-wide collection from all CPUs"), 285 OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu", 286 "list of cpus to monitor"), 287 OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, 288 "child tasks do not inherit counters"), 289 OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages, 290 "number of mmap data pages"), 291 OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", 292 "user to profile"), 293 OPT_END() 294 }; 295 int err; 296 297 argc = parse_options(argc, argv, trace_options, trace_usage, 0); 298 if (argc) 299 usage_with_options(trace_usage, trace_options); 300 301 err = perf_target__parse_uid(&trace.opts.target); 302 if (err) { 303 char bf[BUFSIZ]; 304 perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); 305 printf("%s", bf); 306 return err; 307 } 308 309 return trace__run(&trace); 310 } 311