diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 121 |
1 files changed, 79 insertions, 42 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4ef78a5e6f32..89a5ddcd1ded 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -34,7 +34,9 @@ static int output; | |||
34 | static const char *output_name = "perf.data"; | 34 | static const char *output_name = "perf.data"; |
35 | static int group = 0; | 35 | static int group = 0; |
36 | static unsigned int realtime_prio = 0; | 36 | static unsigned int realtime_prio = 0; |
37 | static int raw_samples = 0; | ||
37 | static int system_wide = 0; | 38 | static int system_wide = 0; |
39 | static int profile_cpu = -1; | ||
38 | static pid_t target_pid = -1; | 40 | static pid_t target_pid = -1; |
39 | static int inherit = 1; | 41 | static int inherit = 1; |
40 | static int force = 0; | 42 | static int force = 0; |
@@ -43,6 +45,7 @@ static int call_graph = 0; | |||
43 | static int verbose = 0; | 45 | static int verbose = 0; |
44 | static int inherit_stat = 0; | 46 | static int inherit_stat = 0; |
45 | static int no_samples = 0; | 47 | static int no_samples = 0; |
48 | static int sample_address = 0; | ||
46 | 49 | ||
47 | static long samples; | 50 | static long samples; |
48 | static struct timeval last_read; | 51 | static struct timeval last_read; |
@@ -202,46 +205,48 @@ static void sig_atexit(void) | |||
202 | kill(getpid(), signr); | 205 | kill(getpid(), signr); |
203 | } | 206 | } |
204 | 207 | ||
205 | static void pid_synthesize_comm_event(pid_t pid, int full) | 208 | static pid_t pid_synthesize_comm_event(pid_t pid, int full) |
206 | { | 209 | { |
207 | struct comm_event comm_ev; | 210 | struct comm_event comm_ev; |
208 | char filename[PATH_MAX]; | 211 | char filename[PATH_MAX]; |
209 | char bf[BUFSIZ]; | 212 | char bf[BUFSIZ]; |
210 | int fd; | 213 | FILE *fp; |
211 | size_t size; | 214 | size_t size = 0; |
212 | char *field, *sep; | ||
213 | DIR *tasks; | 215 | DIR *tasks; |
214 | struct dirent dirent, *next; | 216 | struct dirent dirent, *next; |
217 | pid_t tgid = 0; | ||
215 | 218 | ||
216 | snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); | 219 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); |
217 | 220 | ||
218 | fd = open(filename, O_RDONLY); | 221 | fp = fopen(filename, "r"); |
219 | if (fd < 0) { | 222 | if (fp == NULL) { |
220 | /* | 223 | /* |
221 | * We raced with a task exiting - just return: | 224 | * We raced with a task exiting - just return: |
222 | */ | 225 | */ |
223 | if (verbose) | 226 | if (verbose) |
224 | fprintf(stderr, "couldn't open %s\n", filename); | 227 | fprintf(stderr, "couldn't open %s\n", filename); |
225 | return; | 228 | return 0; |
226 | } | ||
227 | if (read(fd, bf, sizeof(bf)) < 0) { | ||
228 | fprintf(stderr, "couldn't read %s\n", filename); | ||
229 | exit(EXIT_FAILURE); | ||
230 | } | 229 | } |
231 | close(fd); | ||
232 | 230 | ||
233 | /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */ | ||
234 | memset(&comm_ev, 0, sizeof(comm_ev)); | 231 | memset(&comm_ev, 0, sizeof(comm_ev)); |
235 | field = strchr(bf, '('); | 232 | while (!comm_ev.comm[0] || !comm_ev.pid) { |
236 | if (field == NULL) | 233 | if (fgets(bf, sizeof(bf), fp) == NULL) |
237 | goto out_failure; | 234 | goto out_failure; |
238 | sep = strchr(++field, ')'); | 235 | |
239 | if (sep == NULL) | 236 | if (memcmp(bf, "Name:", 5) == 0) { |
240 | goto out_failure; | 237 | char *name = bf + 5; |
241 | size = sep - field; | 238 | while (*name && isspace(*name)) |
242 | memcpy(comm_ev.comm, field, size++); | 239 | ++name; |
243 | 240 | size = strlen(name) - 1; | |
244 | comm_ev.pid = pid; | 241 | memcpy(comm_ev.comm, name, size++); |
242 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
243 | char *tgids = bf + 5; | ||
244 | while (*tgids && isspace(*tgids)) | ||
245 | ++tgids; | ||
246 | tgid = comm_ev.pid = atoi(tgids); | ||
247 | } | ||
248 | } | ||
249 | |||
245 | comm_ev.header.type = PERF_EVENT_COMM; | 250 | comm_ev.header.type = PERF_EVENT_COMM; |
246 | size = ALIGN(size, sizeof(u64)); | 251 | size = ALIGN(size, sizeof(u64)); |
247 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); | 252 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); |
@@ -250,7 +255,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
250 | comm_ev.tid = pid; | 255 | comm_ev.tid = pid; |
251 | 256 | ||
252 | write_output(&comm_ev, comm_ev.header.size); | 257 | write_output(&comm_ev, comm_ev.header.size); |
253 | return; | 258 | goto out_fclose; |
254 | } | 259 | } |
255 | 260 | ||
256 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | 261 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); |
@@ -267,7 +272,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
267 | write_output(&comm_ev, comm_ev.header.size); | 272 | write_output(&comm_ev, comm_ev.header.size); |
268 | } | 273 | } |
269 | closedir(tasks); | 274 | closedir(tasks); |
270 | return; | 275 | |
276 | out_fclose: | ||
277 | fclose(fp); | ||
278 | return tgid; | ||
271 | 279 | ||
272 | out_failure: | 280 | out_failure: |
273 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", | 281 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", |
@@ -275,7 +283,7 @@ out_failure: | |||
275 | exit(EXIT_FAILURE); | 283 | exit(EXIT_FAILURE); |
276 | } | 284 | } |
277 | 285 | ||
278 | static void pid_synthesize_mmap_samples(pid_t pid) | 286 | static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) |
279 | { | 287 | { |
280 | char filename[PATH_MAX]; | 288 | char filename[PATH_MAX]; |
281 | FILE *fp; | 289 | FILE *fp; |
@@ -313,6 +321,10 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
313 | if (*pbf == 'x') { /* vm_exec */ | 321 | if (*pbf == 'x') { /* vm_exec */ |
314 | char *execname = strchr(bf, '/'); | 322 | char *execname = strchr(bf, '/'); |
315 | 323 | ||
324 | /* Catch VDSO */ | ||
325 | if (execname == NULL) | ||
326 | execname = strstr(bf, "[vdso]"); | ||
327 | |||
316 | if (execname == NULL) | 328 | if (execname == NULL) |
317 | continue; | 329 | continue; |
318 | 330 | ||
@@ -323,7 +335,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
323 | mmap_ev.len -= mmap_ev.start; | 335 | mmap_ev.len -= mmap_ev.start; |
324 | mmap_ev.header.size = (sizeof(mmap_ev) - | 336 | mmap_ev.header.size = (sizeof(mmap_ev) - |
325 | (sizeof(mmap_ev.filename) - size)); | 337 | (sizeof(mmap_ev.filename) - size)); |
326 | mmap_ev.pid = pid; | 338 | mmap_ev.pid = tgid; |
327 | mmap_ev.tid = pid; | 339 | mmap_ev.tid = pid; |
328 | 340 | ||
329 | write_output(&mmap_ev, mmap_ev.header.size); | 341 | write_output(&mmap_ev, mmap_ev.header.size); |
@@ -342,14 +354,14 @@ static void synthesize_all(void) | |||
342 | 354 | ||
343 | while (!readdir_r(proc, &dirent, &next) && next) { | 355 | while (!readdir_r(proc, &dirent, &next) && next) { |
344 | char *end; | 356 | char *end; |
345 | pid_t pid; | 357 | pid_t pid, tgid; |
346 | 358 | ||
347 | pid = strtol(dirent.d_name, &end, 10); | 359 | pid = strtol(dirent.d_name, &end, 10); |
348 | if (*end) /* only interested in proper numerical dirents */ | 360 | if (*end) /* only interested in proper numerical dirents */ |
349 | continue; | 361 | continue; |
350 | 362 | ||
351 | pid_synthesize_comm_event(pid, 1); | 363 | tgid = pid_synthesize_comm_event(pid, 1); |
352 | pid_synthesize_mmap_samples(pid); | 364 | pid_synthesize_mmap_samples(pid, tgid); |
353 | } | 365 | } |
354 | 366 | ||
355 | closedir(proc); | 367 | closedir(proc); |
@@ -387,7 +399,7 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
387 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 399 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
388 | PERF_FORMAT_ID; | 400 | PERF_FORMAT_ID; |
389 | 401 | ||
390 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 402 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
391 | 403 | ||
392 | if (freq) { | 404 | if (freq) { |
393 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 405 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
@@ -401,9 +413,15 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
401 | if (inherit_stat) | 413 | if (inherit_stat) |
402 | attr->inherit_stat = 1; | 414 | attr->inherit_stat = 1; |
403 | 415 | ||
416 | if (sample_address) | ||
417 | attr->sample_type |= PERF_SAMPLE_ADDR; | ||
418 | |||
404 | if (call_graph) | 419 | if (call_graph) |
405 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 420 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
406 | 421 | ||
422 | if (raw_samples) | ||
423 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
424 | |||
407 | attr->mmap = track; | 425 | attr->mmap = track; |
408 | attr->comm = track; | 426 | attr->comm = track; |
409 | attr->inherit = (cpu < 0) && inherit; | 427 | attr->inherit = (cpu < 0) && inherit; |
@@ -417,6 +435,8 @@ try_again: | |||
417 | 435 | ||
418 | if (err == EPERM) | 436 | if (err == EPERM) |
419 | die("Permission error - are you root?\n"); | 437 | die("Permission error - are you root?\n"); |
438 | else if (err == ENODEV && profile_cpu != -1) | ||
439 | die("No such device - did you specify an out-of-range profile CPU?\n"); | ||
420 | 440 | ||
421 | /* | 441 | /* |
422 | * If it's cycles then fall back to hrtimer | 442 | * If it's cycles then fall back to hrtimer |
@@ -516,10 +536,14 @@ static int __cmd_record(int argc, const char **argv) | |||
516 | signal(SIGCHLD, sig_handler); | 536 | signal(SIGCHLD, sig_handler); |
517 | signal(SIGINT, sig_handler); | 537 | signal(SIGINT, sig_handler); |
518 | 538 | ||
519 | if (!stat(output_name, &st) && !force && !append_file) { | 539 | if (!stat(output_name, &st) && st.st_size) { |
520 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 540 | if (!force && !append_file) { |
521 | output_name); | 541 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", |
522 | exit(-1); | 542 | output_name); |
543 | exit(-1); | ||
544 | } | ||
545 | } else { | ||
546 | append_file = 0; | ||
523 | } | 547 | } |
524 | 548 | ||
525 | flags = O_CREAT|O_RDWR; | 549 | flags = O_CREAT|O_RDWR; |
@@ -546,16 +570,22 @@ static int __cmd_record(int argc, const char **argv) | |||
546 | if (pid == -1) | 570 | if (pid == -1) |
547 | pid = getpid(); | 571 | pid = getpid(); |
548 | 572 | ||
549 | open_counters(-1, pid); | 573 | open_counters(profile_cpu, pid); |
550 | } else for (i = 0; i < nr_cpus; i++) | 574 | } else { |
551 | open_counters(i, target_pid); | 575 | if (profile_cpu != -1) { |
576 | open_counters(profile_cpu, target_pid); | ||
577 | } else { | ||
578 | for (i = 0; i < nr_cpus; i++) | ||
579 | open_counters(i, target_pid); | ||
580 | } | ||
581 | } | ||
552 | 582 | ||
553 | if (file_new) | 583 | if (file_new) |
554 | perf_header__write(header, output); | 584 | perf_header__write(header, output); |
555 | 585 | ||
556 | if (!system_wide) { | 586 | if (!system_wide) { |
557 | pid_synthesize_comm_event(pid, 0); | 587 | pid_t tgid = pid_synthesize_comm_event(pid, 0); |
558 | pid_synthesize_mmap_samples(pid); | 588 | pid_synthesize_mmap_samples(pid, tgid); |
559 | } else | 589 | } else |
560 | synthesize_all(); | 590 | synthesize_all(); |
561 | 591 | ||
@@ -623,10 +653,14 @@ static const struct option options[] = { | |||
623 | "record events on existing pid"), | 653 | "record events on existing pid"), |
624 | OPT_INTEGER('r', "realtime", &realtime_prio, | 654 | OPT_INTEGER('r', "realtime", &realtime_prio, |
625 | "collect data with this RT SCHED_FIFO priority"), | 655 | "collect data with this RT SCHED_FIFO priority"), |
656 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, | ||
657 | "collect raw sample records from all opened counters"), | ||
626 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 658 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
627 | "system-wide collection from all CPUs"), | 659 | "system-wide collection from all CPUs"), |
628 | OPT_BOOLEAN('A', "append", &append_file, | 660 | OPT_BOOLEAN('A', "append", &append_file, |
629 | "append to the output file to do incremental profiling"), | 661 | "append to the output file to do incremental profiling"), |
662 | OPT_INTEGER('C', "profile_cpu", &profile_cpu, | ||
663 | "CPU to profile on"), | ||
630 | OPT_BOOLEAN('f', "force", &force, | 664 | OPT_BOOLEAN('f', "force", &force, |
631 | "overwrite existing data file"), | 665 | "overwrite existing data file"), |
632 | OPT_LONG('c', "count", &default_interval, | 666 | OPT_LONG('c', "count", &default_interval, |
@@ -645,6 +679,8 @@ static const struct option options[] = { | |||
645 | "be more verbose (show counter open errors, etc)"), | 679 | "be more verbose (show counter open errors, etc)"), |
646 | OPT_BOOLEAN('s', "stat", &inherit_stat, | 680 | OPT_BOOLEAN('s', "stat", &inherit_stat, |
647 | "per thread counts"), | 681 | "per thread counts"), |
682 | OPT_BOOLEAN('d', "data", &sample_address, | ||
683 | "Sample addresses"), | ||
648 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 684 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
649 | "don't sample"), | 685 | "don't sample"), |
650 | OPT_END() | 686 | OPT_END() |
@@ -654,7 +690,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
654 | { | 690 | { |
655 | int counter; | 691 | int counter; |
656 | 692 | ||
657 | argc = parse_options(argc, argv, options, record_usage, 0); | 693 | argc = parse_options(argc, argv, options, record_usage, |
694 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
658 | if (!argc && target_pid == -1 && !system_wide) | 695 | if (!argc && target_pid == -1 && !system_wide) |
659 | usage_with_options(record_usage, options); | 696 | usage_with_options(record_usage, options); |
660 | 697 | ||