diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
| -rw-r--r-- | tools/perf/builtin-record.c | 108 |
1 files changed, 67 insertions, 41 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 6da09928130f..3d051b9cf25f 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; |
| @@ -203,46 +205,48 @@ static void sig_atexit(void) | |||
| 203 | kill(getpid(), signr); | 205 | kill(getpid(), signr); |
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | static void pid_synthesize_comm_event(pid_t pid, int full) | 208 | static pid_t pid_synthesize_comm_event(pid_t pid, int full) |
| 207 | { | 209 | { |
| 208 | struct comm_event comm_ev; | 210 | struct comm_event comm_ev; |
| 209 | char filename[PATH_MAX]; | 211 | char filename[PATH_MAX]; |
| 210 | char bf[BUFSIZ]; | 212 | char bf[BUFSIZ]; |
| 211 | int fd; | 213 | FILE *fp; |
| 212 | size_t size; | 214 | size_t size = 0; |
| 213 | char *field, *sep; | ||
| 214 | DIR *tasks; | 215 | DIR *tasks; |
| 215 | struct dirent dirent, *next; | 216 | struct dirent dirent, *next; |
| 217 | pid_t tgid = 0; | ||
| 216 | 218 | ||
| 217 | snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); | 219 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); |
| 218 | 220 | ||
| 219 | fd = open(filename, O_RDONLY); | 221 | fp = fopen(filename, "r"); |
| 220 | if (fd < 0) { | 222 | if (fd == NULL) { |
| 221 | /* | 223 | /* |
| 222 | * We raced with a task exiting - just return: | 224 | * We raced with a task exiting - just return: |
| 223 | */ | 225 | */ |
| 224 | if (verbose) | 226 | if (verbose) |
| 225 | fprintf(stderr, "couldn't open %s\n", filename); | 227 | fprintf(stderr, "couldn't open %s\n", filename); |
| 226 | return; | 228 | return 0; |
| 227 | } | 229 | } |
| 228 | if (read(fd, bf, sizeof(bf)) < 0) { | ||
| 229 | fprintf(stderr, "couldn't read %s\n", filename); | ||
| 230 | exit(EXIT_FAILURE); | ||
| 231 | } | ||
| 232 | close(fd); | ||
| 233 | 230 | ||
| 234 | /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */ | ||
| 235 | memset(&comm_ev, 0, sizeof(comm_ev)); | 231 | memset(&comm_ev, 0, sizeof(comm_ev)); |
| 236 | field = strchr(bf, '('); | 232 | while (!comm_ev.comm[0] || !comm_ev.pid) { |
| 237 | if (field == NULL) | 233 | if (fgets(bf, sizeof(bf), fp) == NULL) |
| 238 | goto out_failure; | 234 | goto out_failure; |
| 239 | sep = strchr(++field, ')'); | 235 | |
| 240 | if (sep == NULL) | 236 | if (memcmp(bf, "Name:", 5) == 0) { |
| 241 | goto out_failure; | 237 | char *name = bf + 5; |
| 242 | size = sep - field; | 238 | while (*name && isspace(*name)) |
| 243 | memcpy(comm_ev.comm, field, size++); | 239 | ++name; |
| 244 | 240 | size = strlen(name) - 1; | |
| 245 | 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 | |||
| 246 | comm_ev.header.type = PERF_EVENT_COMM; | 250 | comm_ev.header.type = PERF_EVENT_COMM; |
| 247 | size = ALIGN(size, sizeof(u64)); | 251 | size = ALIGN(size, sizeof(u64)); |
| 248 | 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); |
| @@ -251,7 +255,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 251 | comm_ev.tid = pid; | 255 | comm_ev.tid = pid; |
| 252 | 256 | ||
| 253 | write_output(&comm_ev, comm_ev.header.size); | 257 | write_output(&comm_ev, comm_ev.header.size); |
| 254 | return; | 258 | goto out_fclose; |
| 255 | } | 259 | } |
| 256 | 260 | ||
| 257 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | 261 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); |
| @@ -268,7 +272,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 268 | write_output(&comm_ev, comm_ev.header.size); | 272 | write_output(&comm_ev, comm_ev.header.size); |
| 269 | } | 273 | } |
| 270 | closedir(tasks); | 274 | closedir(tasks); |
| 271 | return; | 275 | |
| 276 | out_fclose: | ||
| 277 | fclose(fp); | ||
| 278 | return tgid; | ||
| 272 | 279 | ||
| 273 | out_failure: | 280 | out_failure: |
| 274 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", | 281 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", |
| @@ -276,7 +283,7 @@ out_failure: | |||
| 276 | exit(EXIT_FAILURE); | 283 | exit(EXIT_FAILURE); |
| 277 | } | 284 | } |
| 278 | 285 | ||
| 279 | static void pid_synthesize_mmap_samples(pid_t pid) | 286 | static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) |
| 280 | { | 287 | { |
| 281 | char filename[PATH_MAX]; | 288 | char filename[PATH_MAX]; |
| 282 | FILE *fp; | 289 | FILE *fp; |
| @@ -328,7 +335,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 328 | mmap_ev.len -= mmap_ev.start; | 335 | mmap_ev.len -= mmap_ev.start; |
| 329 | mmap_ev.header.size = (sizeof(mmap_ev) - | 336 | mmap_ev.header.size = (sizeof(mmap_ev) - |
| 330 | (sizeof(mmap_ev.filename) - size)); | 337 | (sizeof(mmap_ev.filename) - size)); |
| 331 | mmap_ev.pid = pid; | 338 | mmap_ev.pid = tgid; |
| 332 | mmap_ev.tid = pid; | 339 | mmap_ev.tid = pid; |
| 333 | 340 | ||
| 334 | write_output(&mmap_ev, mmap_ev.header.size); | 341 | write_output(&mmap_ev, mmap_ev.header.size); |
| @@ -347,14 +354,14 @@ static void synthesize_all(void) | |||
| 347 | 354 | ||
| 348 | while (!readdir_r(proc, &dirent, &next) && next) { | 355 | while (!readdir_r(proc, &dirent, &next) && next) { |
| 349 | char *end; | 356 | char *end; |
| 350 | pid_t pid; | 357 | pid_t pid, tgid; |
| 351 | 358 | ||
| 352 | pid = strtol(dirent.d_name, &end, 10); | 359 | pid = strtol(dirent.d_name, &end, 10); |
| 353 | if (*end) /* only interested in proper numerical dirents */ | 360 | if (*end) /* only interested in proper numerical dirents */ |
| 354 | continue; | 361 | continue; |
| 355 | 362 | ||
| 356 | pid_synthesize_comm_event(pid, 1); | 363 | tgid = pid_synthesize_comm_event(pid, 1); |
| 357 | pid_synthesize_mmap_samples(pid); | 364 | pid_synthesize_mmap_samples(pid, tgid); |
| 358 | } | 365 | } |
| 359 | 366 | ||
| 360 | closedir(proc); | 367 | closedir(proc); |
| @@ -392,7 +399,7 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 392 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 399 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
| 393 | PERF_FORMAT_ID; | 400 | PERF_FORMAT_ID; |
| 394 | 401 | ||
| 395 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 402 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 396 | 403 | ||
| 397 | if (freq) { | 404 | if (freq) { |
| 398 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 405 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
| @@ -412,6 +419,9 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 412 | if (call_graph) | 419 | if (call_graph) |
| 413 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 420 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
| 414 | 421 | ||
| 422 | if (raw_samples) | ||
| 423 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
| 424 | |||
| 415 | attr->mmap = track; | 425 | attr->mmap = track; |
| 416 | attr->comm = track; | 426 | attr->comm = track; |
| 417 | attr->inherit = (cpu < 0) && inherit; | 427 | attr->inherit = (cpu < 0) && inherit; |
| @@ -425,6 +435,8 @@ try_again: | |||
| 425 | 435 | ||
| 426 | if (err == EPERM) | 436 | if (err == EPERM) |
| 427 | 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"); | ||
| 428 | 440 | ||
| 429 | /* | 441 | /* |
| 430 | * If it's cycles then fall back to hrtimer | 442 | * If it's cycles then fall back to hrtimer |
| @@ -524,10 +536,14 @@ static int __cmd_record(int argc, const char **argv) | |||
| 524 | signal(SIGCHLD, sig_handler); | 536 | signal(SIGCHLD, sig_handler); |
| 525 | signal(SIGINT, sig_handler); | 537 | signal(SIGINT, sig_handler); |
| 526 | 538 | ||
| 527 | if (!stat(output_name, &st) && !force && !append_file) { | 539 | if (!stat(output_name, &st) && st.st_size) { |
| 528 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 540 | if (!force && !append_file) { |
| 529 | output_name); | 541 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", |
| 530 | exit(-1); | 542 | output_name); |
| 543 | exit(-1); | ||
| 544 | } | ||
| 545 | } else { | ||
| 546 | append_file = 0; | ||
| 531 | } | 547 | } |
| 532 | 548 | ||
| 533 | flags = O_CREAT|O_RDWR; | 549 | flags = O_CREAT|O_RDWR; |
| @@ -554,16 +570,22 @@ static int __cmd_record(int argc, const char **argv) | |||
| 554 | if (pid == -1) | 570 | if (pid == -1) |
| 555 | pid = getpid(); | 571 | pid = getpid(); |
| 556 | 572 | ||
| 557 | open_counters(-1, pid); | 573 | open_counters(profile_cpu, pid); |
| 558 | } else for (i = 0; i < nr_cpus; i++) | 574 | } else { |
| 559 | 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 | } | ||
| 560 | 582 | ||
| 561 | if (file_new) | 583 | if (file_new) |
| 562 | perf_header__write(header, output); | 584 | perf_header__write(header, output); |
| 563 | 585 | ||
| 564 | if (!system_wide) { | 586 | if (!system_wide) { |
| 565 | pid_synthesize_comm_event(pid, 0); | 587 | pid_t tgid = pid_synthesize_comm_event(pid, 0); |
| 566 | pid_synthesize_mmap_samples(pid); | 588 | pid_synthesize_mmap_samples(pid, tgid); |
| 567 | } else | 589 | } else |
| 568 | synthesize_all(); | 590 | synthesize_all(); |
| 569 | 591 | ||
| @@ -631,10 +653,14 @@ static const struct option options[] = { | |||
| 631 | "record events on existing pid"), | 653 | "record events on existing pid"), |
| 632 | OPT_INTEGER('r', "realtime", &realtime_prio, | 654 | OPT_INTEGER('r', "realtime", &realtime_prio, |
| 633 | "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"), | ||
| 634 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 658 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
| 635 | "system-wide collection from all CPUs"), | 659 | "system-wide collection from all CPUs"), |
| 636 | OPT_BOOLEAN('A', "append", &append_file, | 660 | OPT_BOOLEAN('A', "append", &append_file, |
| 637 | "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"), | ||
| 638 | OPT_BOOLEAN('f', "force", &force, | 664 | OPT_BOOLEAN('f', "force", &force, |
| 639 | "overwrite existing data file"), | 665 | "overwrite existing data file"), |
| 640 | OPT_LONG('c', "count", &default_interval, | 666 | OPT_LONG('c', "count", &default_interval, |
