aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-record.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r--tools/perf/builtin-record.c305
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
37static char callchain_help[] = CALLCHAIN_HELP "[fp]";
38#else
39static unsigned long default_stack_dump_size = 8192;
40static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
41#endif
42
34enum write_mode_t { 43enum 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
65static void write_output(struct perf_record *rec, void *buf, size_t size) 74static 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
80static int process_synthesized_event(struct perf_tool *tool, 93static 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
90static void perf_record__mmap_read(struct perf_record *rec, 105static 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
145out:
146 return rc;
122} 147}
123 148
124static volatile int done = 0; 149static volatile int done = 0;
@@ -134,7 +159,7 @@ static void sig_handler(int sig)
134 signr = sig; 159 signr = sig;
135} 160}
136 161
137static void perf_record__sig_exit(int exit_status __used, void *arg) 162static 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
177static void perf_record__open(struct perf_record *rec) 202static 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;
207fallback_missing_features: 231fallback_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;
210retry_sample_id: 234retry_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;
212try_again: 236try_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);
359out:
360 return rc;
317} 361}
318 362
319static int process_buildids(struct perf_record *rec) 363static 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
332static void perf_record__exit(int status __used, void *arg) 376static 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
390static void perf_record__mmap_read_all(struct perf_record *rec) 437static 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
455out:
456 return rc;
401} 457}
402 458
403static int __cmd_record(struct perf_record *rec, int argc, const char **argv) 459static 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, &param)) { 664 if (sched_setscheduler(0, SCHED_FIFO, &param)) {
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
804static 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
831static int
832parse_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
735static const char * const record_usage[] = { 903static 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
839int cmd_record(int argc, const char **argv, const char *prefix __used) 1008int 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;