diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
| -rw-r--r-- | tools/perf/builtin-record.c | 305 |
1 files changed, 237 insertions, 68 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4db6e1ba54e3..f14cb5fdb91f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -31,6 +31,15 @@ | |||
| 31 | #include <sched.h> | 31 | #include <sched.h> |
| 32 | #include <sys/mman.h> | 32 | #include <sys/mman.h> |
| 33 | 33 | ||
| 34 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " | ||
| 35 | |||
| 36 | #ifdef NO_LIBUNWIND_SUPPORT | ||
| 37 | static char callchain_help[] = CALLCHAIN_HELP "[fp]"; | ||
| 38 | #else | ||
| 39 | static unsigned long default_stack_dump_size = 8192; | ||
| 40 | static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; | ||
| 41 | #endif | ||
| 42 | |||
| 34 | enum write_mode_t { | 43 | enum write_mode_t { |
| 35 | WRITE_FORCE, | 44 | WRITE_FORCE, |
| 36 | WRITE_APPEND | 45 | WRITE_APPEND |
| @@ -62,32 +71,38 @@ static void advance_output(struct perf_record *rec, size_t size) | |||
| 62 | rec->bytes_written += size; | 71 | rec->bytes_written += size; |
| 63 | } | 72 | } |
| 64 | 73 | ||
| 65 | static void write_output(struct perf_record *rec, void *buf, size_t size) | 74 | static int write_output(struct perf_record *rec, void *buf, size_t size) |
| 66 | { | 75 | { |
| 67 | while (size) { | 76 | while (size) { |
| 68 | int ret = write(rec->output, buf, size); | 77 | int ret = write(rec->output, buf, size); |
| 69 | 78 | ||
| 70 | if (ret < 0) | 79 | if (ret < 0) { |
| 71 | die("failed to write"); | 80 | pr_err("failed to write\n"); |
| 81 | return -1; | ||
| 82 | } | ||
| 72 | 83 | ||
| 73 | size -= ret; | 84 | size -= ret; |
| 74 | buf += ret; | 85 | buf += ret; |
| 75 | 86 | ||
| 76 | rec->bytes_written += ret; | 87 | rec->bytes_written += ret; |
| 77 | } | 88 | } |
| 89 | |||
| 90 | return 0; | ||
| 78 | } | 91 | } |
| 79 | 92 | ||
| 80 | static int process_synthesized_event(struct perf_tool *tool, | 93 | static int process_synthesized_event(struct perf_tool *tool, |
| 81 | union perf_event *event, | 94 | union perf_event *event, |
| 82 | struct perf_sample *sample __used, | 95 | struct perf_sample *sample __maybe_unused, |
| 83 | struct machine *machine __used) | 96 | struct machine *machine __maybe_unused) |
| 84 | { | 97 | { |
| 85 | struct perf_record *rec = container_of(tool, struct perf_record, tool); | 98 | struct perf_record *rec = container_of(tool, struct perf_record, tool); |
| 86 | write_output(rec, event, event->header.size); | 99 | if (write_output(rec, event, event->header.size) < 0) |
| 100 | return -1; | ||
| 101 | |||
| 87 | return 0; | 102 | return 0; |
| 88 | } | 103 | } |
| 89 | 104 | ||
| 90 | static void perf_record__mmap_read(struct perf_record *rec, | 105 | static int perf_record__mmap_read(struct perf_record *rec, |
| 91 | struct perf_mmap *md) | 106 | struct perf_mmap *md) |
| 92 | { | 107 | { |
| 93 | unsigned int head = perf_mmap__read_head(md); | 108 | unsigned int head = perf_mmap__read_head(md); |
| @@ -95,9 +110,10 @@ static void perf_record__mmap_read(struct perf_record *rec, | |||
| 95 | unsigned char *data = md->base + rec->page_size; | 110 | unsigned char *data = md->base + rec->page_size; |
| 96 | unsigned long size; | 111 | unsigned long size; |
| 97 | void *buf; | 112 | void *buf; |
| 113 | int rc = 0; | ||
| 98 | 114 | ||
| 99 | if (old == head) | 115 | if (old == head) |
| 100 | return; | 116 | return 0; |
| 101 | 117 | ||
| 102 | rec->samples++; | 118 | rec->samples++; |
| 103 | 119 | ||
| @@ -108,17 +124,26 @@ static void perf_record__mmap_read(struct perf_record *rec, | |||
| 108 | size = md->mask + 1 - (old & md->mask); | 124 | size = md->mask + 1 - (old & md->mask); |
| 109 | old += size; | 125 | old += size; |
| 110 | 126 | ||
| 111 | write_output(rec, buf, size); | 127 | if (write_output(rec, buf, size) < 0) { |
| 128 | rc = -1; | ||
| 129 | goto out; | ||
| 130 | } | ||
| 112 | } | 131 | } |
| 113 | 132 | ||
| 114 | buf = &data[old & md->mask]; | 133 | buf = &data[old & md->mask]; |
| 115 | size = head - old; | 134 | size = head - old; |
| 116 | old += size; | 135 | old += size; |
| 117 | 136 | ||
| 118 | write_output(rec, buf, size); | 137 | if (write_output(rec, buf, size) < 0) { |
| 138 | rc = -1; | ||
| 139 | goto out; | ||
| 140 | } | ||
| 119 | 141 | ||
| 120 | md->prev = old; | 142 | md->prev = old; |
| 121 | perf_mmap__write_tail(md, old); | 143 | perf_mmap__write_tail(md, old); |
| 144 | |||
| 145 | out: | ||
| 146 | return rc; | ||
| 122 | } | 147 | } |
| 123 | 148 | ||
| 124 | static volatile int done = 0; | 149 | static volatile int done = 0; |
| @@ -134,7 +159,7 @@ static void sig_handler(int sig) | |||
| 134 | signr = sig; | 159 | signr = sig; |
| 135 | } | 160 | } |
| 136 | 161 | ||
| 137 | static void perf_record__sig_exit(int exit_status __used, void *arg) | 162 | static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg) |
| 138 | { | 163 | { |
| 139 | struct perf_record *rec = arg; | 164 | struct perf_record *rec = arg; |
| 140 | int status; | 165 | int status; |
| @@ -163,31 +188,32 @@ static bool perf_evlist__equal(struct perf_evlist *evlist, | |||
| 163 | if (evlist->nr_entries != other->nr_entries) | 188 | if (evlist->nr_entries != other->nr_entries) |
| 164 | return false; | 189 | return false; |
| 165 | 190 | ||
| 166 | pair = list_entry(other->entries.next, struct perf_evsel, node); | 191 | pair = perf_evlist__first(other); |
| 167 | 192 | ||
| 168 | list_for_each_entry(pos, &evlist->entries, node) { | 193 | list_for_each_entry(pos, &evlist->entries, node) { |
| 169 | if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0)) | 194 | if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0)) |
| 170 | return false; | 195 | return false; |
| 171 | pair = list_entry(pair->node.next, struct perf_evsel, node); | 196 | pair = perf_evsel__next(pair); |
| 172 | } | 197 | } |
| 173 | 198 | ||
| 174 | return true; | 199 | return true; |
| 175 | } | 200 | } |
| 176 | 201 | ||
| 177 | static void perf_record__open(struct perf_record *rec) | 202 | static int perf_record__open(struct perf_record *rec) |
| 178 | { | 203 | { |
| 179 | struct perf_evsel *pos, *first; | 204 | struct perf_evsel *pos; |
| 180 | struct perf_evlist *evlist = rec->evlist; | 205 | struct perf_evlist *evlist = rec->evlist; |
| 181 | struct perf_session *session = rec->session; | 206 | struct perf_session *session = rec->session; |
| 182 | struct perf_record_opts *opts = &rec->opts; | 207 | struct perf_record_opts *opts = &rec->opts; |
| 183 | 208 | int rc = 0; | |
| 184 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 185 | 209 | ||
| 186 | perf_evlist__config_attrs(evlist, opts); | 210 | perf_evlist__config_attrs(evlist, opts); |
| 187 | 211 | ||
| 212 | if (opts->group) | ||
| 213 | perf_evlist__set_leader(evlist); | ||
| 214 | |||
| 188 | list_for_each_entry(pos, &evlist->entries, node) { | 215 | list_for_each_entry(pos, &evlist->entries, node) { |
| 189 | struct perf_event_attr *attr = &pos->attr; | 216 | struct perf_event_attr *attr = &pos->attr; |
| 190 | struct xyarray *group_fd = NULL; | ||
| 191 | /* | 217 | /* |
| 192 | * Check if parse_single_tracepoint_event has already asked for | 218 | * Check if parse_single_tracepoint_event has already asked for |
| 193 | * PERF_SAMPLE_TIME. | 219 | * PERF_SAMPLE_TIME. |
| @@ -202,24 +228,24 @@ static void perf_record__open(struct perf_record *rec) | |||
| 202 | */ | 228 | */ |
| 203 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | 229 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; |
| 204 | 230 | ||
| 205 | if (opts->group && pos != first) | ||
| 206 | group_fd = first->fd; | ||
| 207 | fallback_missing_features: | 231 | fallback_missing_features: |
| 208 | if (opts->exclude_guest_missing) | 232 | if (opts->exclude_guest_missing) |
| 209 | attr->exclude_guest = attr->exclude_host = 0; | 233 | attr->exclude_guest = attr->exclude_host = 0; |
| 210 | retry_sample_id: | 234 | retry_sample_id: |
| 211 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; | 235 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; |
| 212 | try_again: | 236 | try_again: |
| 213 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, | 237 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { |
| 214 | opts->group, group_fd) < 0) { | ||
| 215 | int err = errno; | 238 | int err = errno; |
| 216 | 239 | ||
| 217 | if (err == EPERM || err == EACCES) { | 240 | if (err == EPERM || err == EACCES) { |
| 218 | ui__error_paranoid(); | 241 | ui__error_paranoid(); |
| 219 | exit(EXIT_FAILURE); | 242 | rc = -err; |
| 243 | goto out; | ||
| 220 | } else if (err == ENODEV && opts->target.cpu_list) { | 244 | } else if (err == ENODEV && opts->target.cpu_list) { |
| 221 | die("No such device - did you specify" | 245 | pr_err("No such device - did you specify" |
| 222 | " an out-of-range profile CPU?\n"); | 246 | " an out-of-range profile CPU?\n"); |
| 247 | rc = -err; | ||
| 248 | goto out; | ||
| 223 | } else if (err == EINVAL) { | 249 | } else if (err == EINVAL) { |
| 224 | if (!opts->exclude_guest_missing && | 250 | if (!opts->exclude_guest_missing && |
| 225 | (attr->exclude_guest || attr->exclude_host)) { | 251 | (attr->exclude_guest || attr->exclude_host)) { |
| @@ -266,42 +292,57 @@ try_again: | |||
| 266 | if (err == ENOENT) { | 292 | if (err == ENOENT) { |
| 267 | ui__error("The %s event is not supported.\n", | 293 | ui__error("The %s event is not supported.\n", |
| 268 | perf_evsel__name(pos)); | 294 | perf_evsel__name(pos)); |
| 269 | exit(EXIT_FAILURE); | 295 | rc = -err; |
| 296 | goto out; | ||
| 270 | } | 297 | } |
| 271 | 298 | ||
| 272 | printf("\n"); | 299 | printf("\n"); |
| 273 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 300 | error("sys_perf_event_open() syscall returned with %d " |
| 274 | err, strerror(err)); | 301 | "(%s) for event %s. /bin/dmesg may provide " |
| 302 | "additional information.\n", | ||
| 303 | err, strerror(err), perf_evsel__name(pos)); | ||
| 275 | 304 | ||
| 276 | #if defined(__i386__) || defined(__x86_64__) | 305 | #if defined(__i386__) || defined(__x86_64__) |
| 277 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 306 | if (attr->type == PERF_TYPE_HARDWARE && |
| 278 | die("No hardware sampling interrupt available." | 307 | err == EOPNOTSUPP) { |
| 279 | " No APIC? If so then you can boot the kernel" | 308 | pr_err("No hardware sampling interrupt available." |
| 280 | " with the \"lapic\" boot parameter to" | 309 | " No APIC? If so then you can boot the kernel" |
| 281 | " force-enable it.\n"); | 310 | " with the \"lapic\" boot parameter to" |
| 311 | " force-enable it.\n"); | ||
| 312 | rc = -err; | ||
| 313 | goto out; | ||
| 314 | } | ||
| 282 | #endif | 315 | #endif |
| 283 | 316 | ||
| 284 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 317 | pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
| 318 | rc = -err; | ||
| 319 | goto out; | ||
| 285 | } | 320 | } |
| 286 | } | 321 | } |
| 287 | 322 | ||
| 288 | if (perf_evlist__set_filters(evlist)) { | 323 | if (perf_evlist__apply_filters(evlist)) { |
| 289 | error("failed to set filter with %d (%s)\n", errno, | 324 | error("failed to set filter with %d (%s)\n", errno, |
| 290 | strerror(errno)); | 325 | strerror(errno)); |
| 291 | exit(-1); | 326 | rc = -1; |
| 327 | goto out; | ||
| 292 | } | 328 | } |
| 293 | 329 | ||
| 294 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { | 330 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
| 295 | if (errno == EPERM) | 331 | if (errno == EPERM) { |
| 296 | die("Permission error mapping pages.\n" | 332 | pr_err("Permission error mapping pages.\n" |
| 297 | "Consider increasing " | 333 | "Consider increasing " |
| 298 | "/proc/sys/kernel/perf_event_mlock_kb,\n" | 334 | "/proc/sys/kernel/perf_event_mlock_kb,\n" |
| 299 | "or try again with a smaller value of -m/--mmap_pages.\n" | 335 | "or try again with a smaller value of -m/--mmap_pages.\n" |
| 300 | "(current value: %d)\n", opts->mmap_pages); | 336 | "(current value: %d)\n", opts->mmap_pages); |
| 301 | else if (!is_power_of_2(opts->mmap_pages)) | 337 | rc = -errno; |
| 302 | die("--mmap_pages/-m value must be a power of two."); | 338 | } else if (!is_power_of_2(opts->mmap_pages)) { |
| 303 | 339 | pr_err("--mmap_pages/-m value must be a power of two."); | |
| 304 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 340 | rc = -EINVAL; |
| 341 | } else { | ||
| 342 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
| 343 | rc = -errno; | ||
| 344 | } | ||
| 345 | goto out; | ||
| 305 | } | 346 | } |
| 306 | 347 | ||
| 307 | if (rec->file_new) | 348 | if (rec->file_new) |
| @@ -309,11 +350,14 @@ try_again: | |||
| 309 | else { | 350 | else { |
| 310 | if (!perf_evlist__equal(session->evlist, evlist)) { | 351 | if (!perf_evlist__equal(session->evlist, evlist)) { |
| 311 | fprintf(stderr, "incompatible append\n"); | 352 | fprintf(stderr, "incompatible append\n"); |
| 312 | exit(-1); | 353 | rc = -1; |
| 354 | goto out; | ||
| 313 | } | 355 | } |
| 314 | } | 356 | } |
| 315 | 357 | ||
| 316 | perf_session__set_id_hdr_size(session); | 358 | perf_session__set_id_hdr_size(session); |
| 359 | out: | ||
| 360 | return rc; | ||
| 317 | } | 361 | } |
| 318 | 362 | ||
| 319 | static int process_buildids(struct perf_record *rec) | 363 | static int process_buildids(struct perf_record *rec) |
| @@ -329,10 +373,13 @@ static int process_buildids(struct perf_record *rec) | |||
| 329 | size, &build_id__mark_dso_hit_ops); | 373 | size, &build_id__mark_dso_hit_ops); |
| 330 | } | 374 | } |
| 331 | 375 | ||
| 332 | static void perf_record__exit(int status __used, void *arg) | 376 | static void perf_record__exit(int status, void *arg) |
| 333 | { | 377 | { |
| 334 | struct perf_record *rec = arg; | 378 | struct perf_record *rec = arg; |
| 335 | 379 | ||
| 380 | if (status != 0) | ||
| 381 | return; | ||
| 382 | |||
| 336 | if (!rec->opts.pipe_output) { | 383 | if (!rec->opts.pipe_output) { |
| 337 | rec->session->header.data_size += rec->bytes_written; | 384 | rec->session->header.data_size += rec->bytes_written; |
| 338 | 385 | ||
| @@ -387,17 +434,26 @@ static struct perf_event_header finished_round_event = { | |||
| 387 | .type = PERF_RECORD_FINISHED_ROUND, | 434 | .type = PERF_RECORD_FINISHED_ROUND, |
| 388 | }; | 435 | }; |
| 389 | 436 | ||
| 390 | static void perf_record__mmap_read_all(struct perf_record *rec) | 437 | static int perf_record__mmap_read_all(struct perf_record *rec) |
| 391 | { | 438 | { |
| 392 | int i; | 439 | int i; |
| 440 | int rc = 0; | ||
| 393 | 441 | ||
| 394 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 442 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
| 395 | if (rec->evlist->mmap[i].base) | 443 | if (rec->evlist->mmap[i].base) { |
| 396 | perf_record__mmap_read(rec, &rec->evlist->mmap[i]); | 444 | if (perf_record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { |
| 445 | rc = -1; | ||
| 446 | goto out; | ||
| 447 | } | ||
| 448 | } | ||
| 397 | } | 449 | } |
| 398 | 450 | ||
| 399 | if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA)) | 451 | if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA)) |
| 400 | write_output(rec, &finished_round_event, sizeof(finished_round_event)); | 452 | rc = write_output(rec, &finished_round_event, |
| 453 | sizeof(finished_round_event)); | ||
| 454 | |||
| 455 | out: | ||
| 456 | return rc; | ||
| 401 | } | 457 | } |
| 402 | 458 | ||
| 403 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | 459 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) |
| @@ -457,7 +513,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 457 | output = open(output_name, flags, S_IRUSR | S_IWUSR); | 513 | output = open(output_name, flags, S_IRUSR | S_IWUSR); |
| 458 | if (output < 0) { | 514 | if (output < 0) { |
| 459 | perror("failed to create output file"); | 515 | perror("failed to create output file"); |
| 460 | exit(-1); | 516 | return -1; |
| 461 | } | 517 | } |
| 462 | 518 | ||
| 463 | rec->output = output; | 519 | rec->output = output; |
| @@ -497,7 +553,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 497 | } | 553 | } |
| 498 | } | 554 | } |
| 499 | 555 | ||
| 500 | perf_record__open(rec); | 556 | if (perf_record__open(rec) != 0) { |
| 557 | err = -1; | ||
| 558 | goto out_delete_session; | ||
| 559 | } | ||
| 501 | 560 | ||
| 502 | /* | 561 | /* |
| 503 | * perf_session__delete(session) will be called at perf_record__exit() | 562 | * perf_session__delete(session) will be called at perf_record__exit() |
| @@ -507,19 +566,20 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 507 | if (opts->pipe_output) { | 566 | if (opts->pipe_output) { |
| 508 | err = perf_header__write_pipe(output); | 567 | err = perf_header__write_pipe(output); |
| 509 | if (err < 0) | 568 | if (err < 0) |
| 510 | return err; | 569 | goto out_delete_session; |
| 511 | } else if (rec->file_new) { | 570 | } else if (rec->file_new) { |
| 512 | err = perf_session__write_header(session, evsel_list, | 571 | err = perf_session__write_header(session, evsel_list, |
| 513 | output, false); | 572 | output, false); |
| 514 | if (err < 0) | 573 | if (err < 0) |
| 515 | return err; | 574 | goto out_delete_session; |
| 516 | } | 575 | } |
| 517 | 576 | ||
| 518 | if (!rec->no_buildid | 577 | if (!rec->no_buildid |
| 519 | && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) { | 578 | && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) { |
| 520 | pr_err("Couldn't generate buildids. " | 579 | pr_err("Couldn't generate buildids. " |
| 521 | "Use --no-buildid to profile anyway.\n"); | 580 | "Use --no-buildid to profile anyway.\n"); |
| 522 | return -1; | 581 | err = -1; |
| 582 | goto out_delete_session; | ||
| 523 | } | 583 | } |
| 524 | 584 | ||
| 525 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); | 585 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); |
| @@ -527,7 +587,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 527 | machine = perf_session__find_host_machine(session); | 587 | machine = perf_session__find_host_machine(session); |
| 528 | if (!machine) { | 588 | if (!machine) { |
| 529 | pr_err("Couldn't find native kernel information.\n"); | 589 | pr_err("Couldn't find native kernel information.\n"); |
| 530 | return -1; | 590 | err = -1; |
| 591 | goto out_delete_session; | ||
| 531 | } | 592 | } |
| 532 | 593 | ||
| 533 | if (opts->pipe_output) { | 594 | if (opts->pipe_output) { |
| @@ -535,14 +596,14 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 535 | process_synthesized_event); | 596 | process_synthesized_event); |
| 536 | if (err < 0) { | 597 | if (err < 0) { |
| 537 | pr_err("Couldn't synthesize attrs.\n"); | 598 | pr_err("Couldn't synthesize attrs.\n"); |
| 538 | return err; | 599 | goto out_delete_session; |
| 539 | } | 600 | } |
| 540 | 601 | ||
| 541 | err = perf_event__synthesize_event_types(tool, process_synthesized_event, | 602 | err = perf_event__synthesize_event_types(tool, process_synthesized_event, |
| 542 | machine); | 603 | machine); |
| 543 | if (err < 0) { | 604 | if (err < 0) { |
| 544 | pr_err("Couldn't synthesize event_types.\n"); | 605 | pr_err("Couldn't synthesize event_types.\n"); |
| 545 | return err; | 606 | goto out_delete_session; |
| 546 | } | 607 | } |
| 547 | 608 | ||
| 548 | if (have_tracepoints(&evsel_list->entries)) { | 609 | if (have_tracepoints(&evsel_list->entries)) { |
| @@ -558,7 +619,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 558 | process_synthesized_event); | 619 | process_synthesized_event); |
| 559 | if (err <= 0) { | 620 | if (err <= 0) { |
| 560 | pr_err("Couldn't record tracing data.\n"); | 621 | pr_err("Couldn't record tracing data.\n"); |
| 561 | return err; | 622 | goto out_delete_session; |
| 562 | } | 623 | } |
| 563 | advance_output(rec, err); | 624 | advance_output(rec, err); |
| 564 | } | 625 | } |
| @@ -586,20 +647,24 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 586 | perf_event__synthesize_guest_os); | 647 | perf_event__synthesize_guest_os); |
| 587 | 648 | ||
| 588 | if (!opts->target.system_wide) | 649 | if (!opts->target.system_wide) |
| 589 | perf_event__synthesize_thread_map(tool, evsel_list->threads, | 650 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, |
| 590 | process_synthesized_event, | 651 | process_synthesized_event, |
| 591 | machine); | 652 | machine); |
| 592 | else | 653 | else |
| 593 | perf_event__synthesize_threads(tool, process_synthesized_event, | 654 | err = perf_event__synthesize_threads(tool, process_synthesized_event, |
| 594 | machine); | 655 | machine); |
| 595 | 656 | ||
| 657 | if (err != 0) | ||
| 658 | goto out_delete_session; | ||
| 659 | |||
| 596 | if (rec->realtime_prio) { | 660 | if (rec->realtime_prio) { |
| 597 | struct sched_param param; | 661 | struct sched_param param; |
| 598 | 662 | ||
| 599 | param.sched_priority = rec->realtime_prio; | 663 | param.sched_priority = rec->realtime_prio; |
| 600 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 664 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
| 601 | pr_err("Could not set realtime priority.\n"); | 665 | pr_err("Could not set realtime priority.\n"); |
| 602 | exit(-1); | 666 | err = -1; |
| 667 | goto out_delete_session; | ||
| 603 | } | 668 | } |
| 604 | } | 669 | } |
| 605 | 670 | ||
| @@ -614,7 +679,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 614 | for (;;) { | 679 | for (;;) { |
| 615 | int hits = rec->samples; | 680 | int hits = rec->samples; |
| 616 | 681 | ||
| 617 | perf_record__mmap_read_all(rec); | 682 | if (perf_record__mmap_read_all(rec) < 0) { |
| 683 | err = -1; | ||
| 684 | goto out_delete_session; | ||
| 685 | } | ||
| 618 | 686 | ||
| 619 | if (hits == rec->samples) { | 687 | if (hits == rec->samples) { |
| 620 | if (done) | 688 | if (done) |
| @@ -732,6 +800,106 @@ error: | |||
| 732 | return ret; | 800 | return ret; |
| 733 | } | 801 | } |
| 734 | 802 | ||
| 803 | #ifndef NO_LIBUNWIND_SUPPORT | ||
| 804 | static int get_stack_size(char *str, unsigned long *_size) | ||
| 805 | { | ||
| 806 | char *endptr; | ||
| 807 | unsigned long size; | ||
| 808 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
| 809 | |||
| 810 | size = strtoul(str, &endptr, 0); | ||
| 811 | |||
| 812 | do { | ||
| 813 | if (*endptr) | ||
| 814 | break; | ||
| 815 | |||
| 816 | size = round_up(size, sizeof(u64)); | ||
| 817 | if (!size || size > max_size) | ||
| 818 | break; | ||
| 819 | |||
| 820 | *_size = size; | ||
| 821 | return 0; | ||
| 822 | |||
| 823 | } while (0); | ||
| 824 | |||
| 825 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", | ||
| 826 | max_size, str); | ||
| 827 | return -1; | ||
| 828 | } | ||
| 829 | #endif /* !NO_LIBUNWIND_SUPPORT */ | ||
| 830 | |||
| 831 | static int | ||
| 832 | parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | ||
| 833 | int unset) | ||
| 834 | { | ||
| 835 | struct perf_record *rec = (struct perf_record *)opt->value; | ||
| 836 | char *tok, *name, *saveptr = NULL; | ||
| 837 | char *buf; | ||
| 838 | int ret = -1; | ||
| 839 | |||
| 840 | /* --no-call-graph */ | ||
| 841 | if (unset) | ||
| 842 | return 0; | ||
| 843 | |||
| 844 | /* We specified default option if none is provided. */ | ||
| 845 | BUG_ON(!arg); | ||
| 846 | |||
| 847 | /* We need buffer that we know we can write to. */ | ||
| 848 | buf = malloc(strlen(arg) + 1); | ||
| 849 | if (!buf) | ||
| 850 | return -ENOMEM; | ||
| 851 | |||
| 852 | strcpy(buf, arg); | ||
| 853 | |||
| 854 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
| 855 | name = tok ? : (char *)buf; | ||
| 856 | |||
| 857 | do { | ||
| 858 | /* Framepointer style */ | ||
| 859 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
| 860 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
| 861 | rec->opts.call_graph = CALLCHAIN_FP; | ||
| 862 | ret = 0; | ||
| 863 | } else | ||
| 864 | pr_err("callchain: No more arguments " | ||
| 865 | "needed for -g fp\n"); | ||
| 866 | break; | ||
| 867 | |||
| 868 | #ifndef NO_LIBUNWIND_SUPPORT | ||
| 869 | /* Dwarf style */ | ||
| 870 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { | ||
| 871 | ret = 0; | ||
| 872 | rec->opts.call_graph = CALLCHAIN_DWARF; | ||
| 873 | rec->opts.stack_dump_size = default_stack_dump_size; | ||
| 874 | |||
| 875 | tok = strtok_r(NULL, ",", &saveptr); | ||
| 876 | if (tok) { | ||
| 877 | unsigned long size = 0; | ||
| 878 | |||
| 879 | ret = get_stack_size(tok, &size); | ||
| 880 | rec->opts.stack_dump_size = size; | ||
| 881 | } | ||
| 882 | |||
| 883 | if (!ret) | ||
| 884 | pr_debug("callchain: stack dump size %d\n", | ||
| 885 | rec->opts.stack_dump_size); | ||
| 886 | #endif /* !NO_LIBUNWIND_SUPPORT */ | ||
| 887 | } else { | ||
| 888 | pr_err("callchain: Unknown -g option " | ||
| 889 | "value: %s\n", arg); | ||
| 890 | break; | ||
| 891 | } | ||
| 892 | |||
| 893 | } while (0); | ||
| 894 | |||
| 895 | free(buf); | ||
| 896 | |||
| 897 | if (!ret) | ||
| 898 | pr_debug("callchain: type %d\n", rec->opts.call_graph); | ||
| 899 | |||
| 900 | return ret; | ||
| 901 | } | ||
| 902 | |||
| 735 | static const char * const record_usage[] = { | 903 | static const char * const record_usage[] = { |
| 736 | "perf record [<options>] [<command>]", | 904 | "perf record [<options>] [<command>]", |
| 737 | "perf record [<options>] -- <command> [<options>]", | 905 | "perf record [<options>] -- <command> [<options>]", |
| @@ -803,8 +971,9 @@ const struct option record_options[] = { | |||
| 803 | "number of mmap data pages"), | 971 | "number of mmap data pages"), |
| 804 | OPT_BOOLEAN(0, "group", &record.opts.group, | 972 | OPT_BOOLEAN(0, "group", &record.opts.group, |
| 805 | "put the counters into a counter group"), | 973 | "put the counters into a counter group"), |
| 806 | OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph, | 974 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]", |
| 807 | "do call-graph (stack chain/backtrace) recording"), | 975 | callchain_help, &parse_callchain_opt, |
| 976 | "fp"), | ||
| 808 | OPT_INCR('v', "verbose", &verbose, | 977 | OPT_INCR('v', "verbose", &verbose, |
| 809 | "be more verbose (show counter open errors, etc)"), | 978 | "be more verbose (show counter open errors, etc)"), |
| 810 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), | 979 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), |
| @@ -836,7 +1005,7 @@ const struct option record_options[] = { | |||
| 836 | OPT_END() | 1005 | OPT_END() |
| 837 | }; | 1006 | }; |
| 838 | 1007 | ||
| 839 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 1008 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) |
| 840 | { | 1009 | { |
| 841 | int err = -ENOMEM; | 1010 | int err = -ENOMEM; |
| 842 | struct perf_evsel *pos; | 1011 | struct perf_evsel *pos; |
