diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
| -rw-r--r-- | tools/perf/builtin-record.c | 131 |
1 files changed, 89 insertions, 42 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d7ebbd757543..4ef78a5e6f32 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include "util/parse-events.h" | 14 | #include "util/parse-events.h" |
| 15 | #include "util/string.h" | 15 | #include "util/string.h" |
| 16 | 16 | ||
| 17 | #include "util/header.h" | ||
| 18 | |||
| 17 | #include <unistd.h> | 19 | #include <unistd.h> |
| 18 | #include <sched.h> | 20 | #include <sched.h> |
| 19 | 21 | ||
| @@ -39,6 +41,8 @@ static int force = 0; | |||
| 39 | static int append_file = 0; | 41 | static int append_file = 0; |
| 40 | static int call_graph = 0; | 42 | static int call_graph = 0; |
| 41 | static int verbose = 0; | 43 | static int verbose = 0; |
| 44 | static int inherit_stat = 0; | ||
| 45 | static int no_samples = 0; | ||
| 42 | 46 | ||
| 43 | static long samples; | 47 | static long samples; |
| 44 | static struct timeval last_read; | 48 | static struct timeval last_read; |
| @@ -52,7 +56,8 @@ static int nr_poll; | |||
| 52 | static int nr_cpu; | 56 | static int nr_cpu; |
| 53 | 57 | ||
| 54 | static int file_new = 1; | 58 | static int file_new = 1; |
| 55 | static struct perf_file_header file_header; | 59 | |
| 60 | struct perf_header *header; | ||
| 56 | 61 | ||
| 57 | struct mmap_event { | 62 | struct mmap_event { |
| 58 | struct perf_event_header header; | 63 | struct perf_event_header header; |
| @@ -289,7 +294,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 289 | while (1) { | 294 | while (1) { |
| 290 | char bf[BUFSIZ], *pbf = bf; | 295 | char bf[BUFSIZ], *pbf = bf; |
| 291 | struct mmap_event mmap_ev = { | 296 | struct mmap_event mmap_ev = { |
| 292 | .header.type = PERF_EVENT_MMAP, | 297 | .header = { .type = PERF_EVENT_MMAP }, |
| 293 | }; | 298 | }; |
| 294 | int n; | 299 | int n; |
| 295 | size_t size; | 300 | size_t size; |
| @@ -306,12 +311,11 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 306 | continue; | 311 | continue; |
| 307 | pbf += n + 3; | 312 | pbf += n + 3; |
| 308 | if (*pbf == 'x') { /* vm_exec */ | 313 | if (*pbf == 'x') { /* vm_exec */ |
| 309 | char *execname = strrchr(bf, ' '); | 314 | char *execname = strchr(bf, '/'); |
| 310 | 315 | ||
| 311 | if (execname == NULL || execname[1] != '/') | 316 | if (execname == NULL) |
| 312 | continue; | 317 | continue; |
| 313 | 318 | ||
| 314 | execname += 1; | ||
| 315 | size = strlen(execname); | 319 | size = strlen(execname); |
| 316 | execname[size - 1] = '\0'; /* Remove \n */ | 320 | execname[size - 1] = '\0'; /* Remove \n */ |
| 317 | memcpy(mmap_ev.filename, execname, size); | 321 | memcpy(mmap_ev.filename, execname, size); |
| @@ -329,7 +333,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 329 | fclose(fp); | 333 | fclose(fp); |
| 330 | } | 334 | } |
| 331 | 335 | ||
| 332 | static void synthesize_samples(void) | 336 | static void synthesize_all(void) |
| 333 | { | 337 | { |
| 334 | DIR *proc; | 338 | DIR *proc; |
| 335 | struct dirent dirent, *next; | 339 | struct dirent dirent, *next; |
| @@ -353,10 +357,35 @@ static void synthesize_samples(void) | |||
| 353 | 357 | ||
| 354 | static int group_fd; | 358 | static int group_fd; |
| 355 | 359 | ||
| 360 | static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr) | ||
| 361 | { | ||
| 362 | struct perf_header_attr *h_attr; | ||
| 363 | |||
| 364 | if (nr < header->attrs) { | ||
| 365 | h_attr = header->attr[nr]; | ||
| 366 | } else { | ||
| 367 | h_attr = perf_header_attr__new(a); | ||
| 368 | perf_header__add_attr(header, h_attr); | ||
| 369 | } | ||
| 370 | |||
| 371 | return h_attr; | ||
| 372 | } | ||
| 373 | |||
| 356 | static void create_counter(int counter, int cpu, pid_t pid) | 374 | static void create_counter(int counter, int cpu, pid_t pid) |
| 357 | { | 375 | { |
| 358 | struct perf_counter_attr *attr = attrs + counter; | 376 | struct perf_counter_attr *attr = attrs + counter; |
| 359 | int track = 1; | 377 | struct perf_header_attr *h_attr; |
| 378 | int track = !counter; /* only the first counter needs these */ | ||
| 379 | struct { | ||
| 380 | u64 count; | ||
| 381 | u64 time_enabled; | ||
| 382 | u64 time_running; | ||
| 383 | u64 id; | ||
| 384 | } read_data; | ||
| 385 | |||
| 386 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
| 387 | PERF_FORMAT_TOTAL_TIME_RUNNING | | ||
| 388 | PERF_FORMAT_ID; | ||
| 360 | 389 | ||
| 361 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 390 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 362 | 391 | ||
| @@ -366,25 +395,20 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 366 | attr->sample_freq = freq; | 395 | attr->sample_freq = freq; |
| 367 | } | 396 | } |
| 368 | 397 | ||
| 398 | if (no_samples) | ||
| 399 | attr->sample_freq = 0; | ||
| 400 | |||
| 401 | if (inherit_stat) | ||
| 402 | attr->inherit_stat = 1; | ||
| 403 | |||
| 369 | if (call_graph) | 404 | if (call_graph) |
| 370 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 405 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
| 371 | 406 | ||
| 372 | if (file_new) { | ||
| 373 | file_header.sample_type = attr->sample_type; | ||
| 374 | } else { | ||
| 375 | if (file_header.sample_type != attr->sample_type) { | ||
| 376 | fprintf(stderr, "incompatible append\n"); | ||
| 377 | exit(-1); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | attr->mmap = track; | 407 | attr->mmap = track; |
| 382 | attr->comm = track; | 408 | attr->comm = track; |
| 383 | attr->inherit = (cpu < 0) && inherit; | 409 | attr->inherit = (cpu < 0) && inherit; |
| 384 | attr->disabled = 1; | 410 | attr->disabled = 1; |
| 385 | 411 | ||
| 386 | track = 0; /* only the first counter needs these */ | ||
| 387 | |||
| 388 | try_again: | 412 | try_again: |
| 389 | fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); | 413 | fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); |
| 390 | 414 | ||
| @@ -415,6 +439,22 @@ try_again: | |||
| 415 | exit(-1); | 439 | exit(-1); |
| 416 | } | 440 | } |
| 417 | 441 | ||
| 442 | h_attr = get_header_attr(attr, counter); | ||
| 443 | |||
| 444 | if (!file_new) { | ||
| 445 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | ||
| 446 | fprintf(stderr, "incompatible append\n"); | ||
| 447 | exit(-1); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) { | ||
| 452 | perror("Unable to read perf file descriptor\n"); | ||
| 453 | exit(-1); | ||
| 454 | } | ||
| 455 | |||
| 456 | perf_header_attr__add_id(h_attr, read_data.id); | ||
| 457 | |||
| 418 | assert(fd[nr_cpu][counter] >= 0); | 458 | assert(fd[nr_cpu][counter] >= 0); |
| 419 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); | 459 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); |
| 420 | 460 | ||
| @@ -445,11 +485,6 @@ static void open_counters(int cpu, pid_t pid) | |||
| 445 | { | 485 | { |
| 446 | int counter; | 486 | int counter; |
| 447 | 487 | ||
| 448 | if (pid > 0) { | ||
| 449 | pid_synthesize_comm_event(pid, 0); | ||
| 450 | pid_synthesize_mmap_samples(pid); | ||
| 451 | } | ||
| 452 | |||
| 453 | group_fd = -1; | 488 | group_fd = -1; |
| 454 | for (counter = 0; counter < nr_counters; counter++) | 489 | for (counter = 0; counter < nr_counters; counter++) |
| 455 | create_counter(counter, cpu, pid); | 490 | create_counter(counter, cpu, pid); |
| @@ -459,17 +494,16 @@ static void open_counters(int cpu, pid_t pid) | |||
| 459 | 494 | ||
| 460 | static void atexit_header(void) | 495 | static void atexit_header(void) |
| 461 | { | 496 | { |
| 462 | file_header.data_size += bytes_written; | 497 | header->data_size += bytes_written; |
| 463 | 498 | ||
| 464 | if (pwrite(output, &file_header, sizeof(file_header), 0) == -1) | 499 | perf_header__write(header, output); |
| 465 | perror("failed to write on file headers"); | ||
| 466 | } | 500 | } |
| 467 | 501 | ||
| 468 | static int __cmd_record(int argc, const char **argv) | 502 | static int __cmd_record(int argc, const char **argv) |
| 469 | { | 503 | { |
| 470 | int i, counter; | 504 | int i, counter; |
| 471 | struct stat st; | 505 | struct stat st; |
| 472 | pid_t pid; | 506 | pid_t pid = 0; |
| 473 | int flags; | 507 | int flags; |
| 474 | int ret; | 508 | int ret; |
| 475 | 509 | ||
| @@ -500,22 +534,31 @@ static int __cmd_record(int argc, const char **argv) | |||
| 500 | exit(-1); | 534 | exit(-1); |
| 501 | } | 535 | } |
| 502 | 536 | ||
| 503 | if (!file_new) { | 537 | if (!file_new) |
| 504 | if (read(output, &file_header, sizeof(file_header)) == -1) { | 538 | header = perf_header__read(output); |
| 505 | perror("failed to read file headers"); | 539 | else |
| 506 | exit(-1); | 540 | header = perf_header__new(); |
| 507 | } | ||
| 508 | |||
| 509 | lseek(output, file_header.data_size, SEEK_CUR); | ||
| 510 | } | ||
| 511 | 541 | ||
| 512 | atexit(atexit_header); | 542 | atexit(atexit_header); |
| 513 | 543 | ||
| 514 | if (!system_wide) { | 544 | if (!system_wide) { |
| 515 | open_counters(-1, target_pid != -1 ? target_pid : getpid()); | 545 | pid = target_pid; |
| 546 | if (pid == -1) | ||
| 547 | pid = getpid(); | ||
| 548 | |||
| 549 | open_counters(-1, pid); | ||
| 516 | } else for (i = 0; i < nr_cpus; i++) | 550 | } else for (i = 0; i < nr_cpus; i++) |
| 517 | open_counters(i, target_pid); | 551 | open_counters(i, target_pid); |
| 518 | 552 | ||
| 553 | if (file_new) | ||
| 554 | perf_header__write(header, output); | ||
| 555 | |||
| 556 | if (!system_wide) { | ||
| 557 | pid_synthesize_comm_event(pid, 0); | ||
| 558 | pid_synthesize_mmap_samples(pid); | ||
| 559 | } else | ||
| 560 | synthesize_all(); | ||
| 561 | |||
| 519 | if (target_pid == -1 && argc) { | 562 | if (target_pid == -1 && argc) { |
| 520 | pid = fork(); | 563 | pid = fork(); |
| 521 | if (pid < 0) | 564 | if (pid < 0) |
| @@ -539,10 +582,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 539 | } | 582 | } |
| 540 | } | 583 | } |
| 541 | 584 | ||
| 542 | if (system_wide) | 585 | for (;;) { |
| 543 | synthesize_samples(); | ||
| 544 | |||
| 545 | while (!done) { | ||
| 546 | int hits = samples; | 586 | int hits = samples; |
| 547 | 587 | ||
| 548 | for (i = 0; i < nr_cpu; i++) { | 588 | for (i = 0; i < nr_cpu; i++) { |
| @@ -550,8 +590,11 @@ static int __cmd_record(int argc, const char **argv) | |||
| 550 | mmap_read(&mmap_array[i][counter]); | 590 | mmap_read(&mmap_array[i][counter]); |
| 551 | } | 591 | } |
| 552 | 592 | ||
| 553 | if (hits == samples) | 593 | if (hits == samples) { |
| 594 | if (done) | ||
| 595 | break; | ||
| 554 | ret = poll(event_array, nr_poll, 100); | 596 | ret = poll(event_array, nr_poll, 100); |
| 597 | } | ||
| 555 | } | 598 | } |
| 556 | 599 | ||
| 557 | /* | 600 | /* |
| @@ -600,10 +643,14 @@ static const struct option options[] = { | |||
| 600 | "do call-graph (stack chain/backtrace) recording"), | 643 | "do call-graph (stack chain/backtrace) recording"), |
| 601 | OPT_BOOLEAN('v', "verbose", &verbose, | 644 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 602 | "be more verbose (show counter open errors, etc)"), | 645 | "be more verbose (show counter open errors, etc)"), |
| 646 | OPT_BOOLEAN('s', "stat", &inherit_stat, | ||
| 647 | "per thread counts"), | ||
| 648 | OPT_BOOLEAN('n', "no-samples", &no_samples, | ||
| 649 | "don't sample"), | ||
| 603 | OPT_END() | 650 | OPT_END() |
| 604 | }; | 651 | }; |
| 605 | 652 | ||
| 606 | int cmd_record(int argc, const char **argv, const char *prefix) | 653 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
| 607 | { | 654 | { |
| 608 | int counter; | 655 | int counter; |
| 609 | 656 | ||
