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 const char *name = audit_syscall_to_name(id, trace->audit_machine); 60 61 if (name == NULL) 62 return -1; 63 64 if (id > trace->syscalls.max) { 65 struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); 66 67 if (nsyscalls == NULL) 68 return -1; 69 70 if (trace->syscalls.max != -1) { 71 memset(nsyscalls + trace->syscalls.max + 1, 0, 72 (id - trace->syscalls.max) * sizeof(*sc)); 73 } else { 74 memset(nsyscalls, 0, (id + 1) * sizeof(*sc)); 75 } 76 77 trace->syscalls.table = nsyscalls; 78 trace->syscalls.max = id; 79 } 80 81 sc = trace->syscalls.table + id; 82 sc->name = name; 83 sc->fmt = syscall_fmt__find(sc->name); 84 85 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); 86 sc->tp_format = event_format__new("syscalls", tp_name); 87 88 if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) { 89 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias); 90 sc->tp_format = event_format__new("syscalls", tp_name); 91 } 92 93 return sc->tp_format != NULL ? 0 : -1; 94 } 95 96 static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp) 97 { 98 int i = 0; 99 size_t printed = 0; 100 101 if (sc->tp_format != NULL) { 102 struct format_field *field; 103 104 for (field = sc->tp_format->format.fields->next; field; field = field->next) { 105 printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "", 106 field->name, args[i++]); 107 } 108 } else { 109 while (i < 6) { 110 printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]); 111 ++i; 112 } 113 } 114 115 return printed; 116 } 117 118 typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, 119 struct perf_sample *sample); 120 121 static struct syscall *trace__syscall_info(struct trace *trace, 122 struct perf_evsel *evsel, 123 struct perf_sample *sample) 124 { 125 int id = perf_evsel__intval(evsel, sample, "id"); 126 127 if (id < 0) { 128 printf("Invalid syscall %d id, skipping...\n", id); 129 return NULL; 130 } 131 132 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && 133 trace__read_syscall_info(trace, id)) 134 goto out_cant_read; 135 136 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) 137 goto out_cant_read; 138 139 return &trace->syscalls.table[id]; 140 141 out_cant_read: 142 printf("Problems reading syscall %d information\n", id); 143 return NULL; 144 } 145 146 static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, 147 struct perf_sample *sample) 148 { 149 void *args; 150 struct syscall *sc = trace__syscall_info(trace, evsel, sample); 151 152 if (sc == NULL) 153 return -1; 154 155 args = perf_evsel__rawptr(evsel, sample, "args"); 156 if (args == NULL) { 157 printf("Problems reading syscall arguments\n"); 158 return -1; 159 } 160 161 printf("%s(", sc->name); 162 syscall__fprintf_args(sc, args, stdout); 163 164 return 0; 165 } 166 167 static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, 168 struct perf_sample *sample) 169 { 170 int ret; 171 struct syscall *sc = trace__syscall_info(trace, evsel, sample); 172 173 if (sc == NULL) 174 return -1; 175 176 ret = perf_evsel__intval(evsel, sample, "ret"); 177 178 if (ret < 0 && sc->fmt && sc->fmt->errmsg) { 179 char bf[256]; 180 const char *emsg = strerror_r(-ret, bf, sizeof(bf)), 181 *e = audit_errno_to_name(-ret); 182 183 printf(") = -1 %s %s", e, emsg); 184 } else if (ret == 0 && sc->fmt && sc->fmt->timeout) 185 printf(") = 0 Timeout"); 186 else 187 printf(") = %d", ret); 188 189 putchar('\n'); 190 return 0; 191 } 192 193 static int trace__run(struct trace *trace) 194 { 195 struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); 196 struct perf_evsel *evsel; 197 int err = -1, i, nr_events = 0, before; 198 199 if (evlist == NULL) { 200 printf("Not enough memory to run!\n"); 201 goto out; 202 } 203 204 if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || 205 perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { 206 printf("Couldn't read the raw_syscalls tracepoints information!\n"); 207 goto out_delete_evlist; 208 } 209 210 err = perf_evlist__create_maps(evlist, &trace->opts.target); 211 if (err < 0) { 212 printf("Problems parsing the target to trace, check your options!\n"); 213 goto out_delete_evlist; 214 } 215 216 perf_evlist__config_attrs(evlist, &trace->opts); 217 218 err = perf_evlist__open(evlist); 219 if (err < 0) { 220 printf("Couldn't create the events: %s\n", strerror(errno)); 221 goto out_delete_evlist; 222 } 223 224 err = perf_evlist__mmap(evlist, UINT_MAX, false); 225 if (err < 0) { 226 printf("Couldn't mmap the events: %s\n", strerror(errno)); 227 goto out_delete_evlist; 228 } 229 230 perf_evlist__enable(evlist); 231 again: 232 before = nr_events; 233 234 for (i = 0; i < evlist->nr_mmaps; i++) { 235 union perf_event *event; 236 237 while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { 238 const u32 type = event->header.type; 239 tracepoint_handler handler; 240 struct perf_sample sample; 241 242 ++nr_events; 243 244 switch (type) { 245 case PERF_RECORD_SAMPLE: 246 break; 247 case PERF_RECORD_LOST: 248 printf("LOST %" PRIu64 " events!\n", event->lost.lost); 249 continue; 250 default: 251 printf("Unexpected %s event, skipping...\n", 252 perf_event__name(type)); 253 continue; 254 } 255 256 err = perf_evlist__parse_sample(evlist, event, &sample); 257 if (err) { 258 printf("Can't parse sample, err = %d, skipping...\n", err); 259 continue; 260 } 261 262 evsel = perf_evlist__id2evsel(evlist, sample.id); 263 if (evsel == NULL) { 264 printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); 265 continue; 266 } 267 268 if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) 269 printf("%d ", sample.tid); 270 271 if (sample.raw_data == NULL) { 272 printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", 273 perf_evsel__name(evsel), sample.tid, 274 sample.cpu, sample.raw_size); 275 continue; 276 } 277 278 handler = evsel->handler.func; 279 handler(trace, evsel, &sample); 280 } 281 } 282 283 if (nr_events == before) 284 poll(evlist->pollfd, evlist->nr_fds, -1); 285 286 goto again; 287 288 out_delete_evlist: 289 perf_evlist__delete(evlist); 290 out: 291 return err; 292 } 293 294 int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) 295 { 296 const char * const trace_usage[] = { 297 "perf trace [<options>]", 298 NULL 299 }; 300 struct trace trace = { 301 .audit_machine = audit_detect_machine(), 302 .syscalls = { 303 . max = -1, 304 }, 305 .opts = { 306 .target = { 307 .uid = UINT_MAX, 308 .uses_mmap = true, 309 }, 310 .user_freq = UINT_MAX, 311 .user_interval = ULLONG_MAX, 312 .no_delay = true, 313 .mmap_pages = 1024, 314 }, 315 }; 316 const struct option trace_options[] = { 317 OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", 318 "trace events on existing process id"), 319 OPT_STRING(0, "tid", &trace.opts.target.tid, "tid", 320 "trace events on existing thread id"), 321 OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide, 322 "system-wide collection from all CPUs"), 323 OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu", 324 "list of cpus to monitor"), 325 OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, 326 "child tasks do not inherit counters"), 327 OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages, 328 "number of mmap data pages"), 329 OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", 330 "user to profile"), 331 OPT_END() 332 }; 333 int err; 334 335 argc = parse_options(argc, argv, trace_options, trace_usage, 0); 336 if (argc) 337 usage_with_options(trace_usage, trace_options); 338 339 err = perf_target__parse_uid(&trace.opts.target); 340 if (err) { 341 char bf[BUFSIZ]; 342 perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); 343 printf("%s", bf); 344 return err; 345 } 346 347 return trace__run(&trace); 348 } 349