diff options
Diffstat (limited to 'tools/perf/util/parse-events.c')
-rw-r--r-- | tools/perf/util/parse-events.c | 317 |
1 files changed, 274 insertions, 43 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4f7b0efdde2f..4c19d5e79d8c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -279,7 +279,24 @@ const char *event_type(int type) | |||
279 | return "unknown"; | 279 | return "unknown"; |
280 | } | 280 | } |
281 | 281 | ||
282 | static int parse_events__is_name_term(struct parse_events_term *term) | ||
283 | { | ||
284 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; | ||
285 | } | ||
286 | |||
287 | static char *get_config_name(struct list_head *head_terms) | ||
288 | { | ||
289 | struct parse_events_term *term; | ||
290 | |||
291 | if (!head_terms) | ||
292 | return NULL; | ||
282 | 293 | ||
294 | list_for_each_entry(term, head_terms, list) | ||
295 | if (parse_events__is_name_term(term)) | ||
296 | return term->val.str; | ||
297 | |||
298 | return NULL; | ||
299 | } | ||
283 | 300 | ||
284 | static struct perf_evsel * | 301 | static struct perf_evsel * |
285 | __add_event(struct list_head *list, int *idx, | 302 | __add_event(struct list_head *list, int *idx, |
@@ -333,11 +350,25 @@ static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES] | |||
333 | return -1; | 350 | return -1; |
334 | } | 351 | } |
335 | 352 | ||
353 | typedef int config_term_func_t(struct perf_event_attr *attr, | ||
354 | struct parse_events_term *term, | ||
355 | struct parse_events_error *err); | ||
356 | static int config_term_common(struct perf_event_attr *attr, | ||
357 | struct parse_events_term *term, | ||
358 | struct parse_events_error *err); | ||
359 | static int config_attr(struct perf_event_attr *attr, | ||
360 | struct list_head *head, | ||
361 | struct parse_events_error *err, | ||
362 | config_term_func_t config_term); | ||
363 | |||
336 | int parse_events_add_cache(struct list_head *list, int *idx, | 364 | int parse_events_add_cache(struct list_head *list, int *idx, |
337 | char *type, char *op_result1, char *op_result2) | 365 | char *type, char *op_result1, char *op_result2, |
366 | struct parse_events_error *err, | ||
367 | struct list_head *head_config) | ||
338 | { | 368 | { |
339 | struct perf_event_attr attr; | 369 | struct perf_event_attr attr; |
340 | char name[MAX_NAME_LEN]; | 370 | LIST_HEAD(config_terms); |
371 | char name[MAX_NAME_LEN], *config_name; | ||
341 | int cache_type = -1, cache_op = -1, cache_result = -1; | 372 | int cache_type = -1, cache_op = -1, cache_result = -1; |
342 | char *op_result[2] = { op_result1, op_result2 }; | 373 | char *op_result[2] = { op_result1, op_result2 }; |
343 | int i, n; | 374 | int i, n; |
@@ -351,6 +382,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, | |||
351 | if (cache_type == -1) | 382 | if (cache_type == -1) |
352 | return -EINVAL; | 383 | return -EINVAL; |
353 | 384 | ||
385 | config_name = get_config_name(head_config); | ||
354 | n = snprintf(name, MAX_NAME_LEN, "%s", type); | 386 | n = snprintf(name, MAX_NAME_LEN, "%s", type); |
355 | 387 | ||
356 | for (i = 0; (i < 2) && (op_result[i]); i++) { | 388 | for (i = 0; (i < 2) && (op_result[i]); i++) { |
@@ -391,7 +423,16 @@ int parse_events_add_cache(struct list_head *list, int *idx, | |||
391 | memset(&attr, 0, sizeof(attr)); | 423 | memset(&attr, 0, sizeof(attr)); |
392 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); | 424 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); |
393 | attr.type = PERF_TYPE_HW_CACHE; | 425 | attr.type = PERF_TYPE_HW_CACHE; |
394 | return add_event(list, idx, &attr, name, NULL); | 426 | |
427 | if (head_config) { | ||
428 | if (config_attr(&attr, head_config, err, | ||
429 | config_term_common)) | ||
430 | return -EINVAL; | ||
431 | |||
432 | if (get_config_terms(head_config, &config_terms)) | ||
433 | return -ENOMEM; | ||
434 | } | ||
435 | return add_event(list, idx, &attr, config_name ? : name, &config_terms); | ||
395 | } | 436 | } |
396 | 437 | ||
397 | static void tracepoint_error(struct parse_events_error *e, int err, | 438 | static void tracepoint_error(struct parse_events_error *e, int err, |
@@ -399,6 +440,9 @@ static void tracepoint_error(struct parse_events_error *e, int err, | |||
399 | { | 440 | { |
400 | char help[BUFSIZ]; | 441 | char help[BUFSIZ]; |
401 | 442 | ||
443 | if (!e) | ||
444 | return; | ||
445 | |||
402 | /* | 446 | /* |
403 | * We get error directly from syscall errno ( > 0), | 447 | * We get error directly from syscall errno ( > 0), |
404 | * or from encoded pointer's error ( < 0). | 448 | * or from encoded pointer's error ( < 0). |
@@ -537,6 +581,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, | |||
537 | struct __add_bpf_event_param { | 581 | struct __add_bpf_event_param { |
538 | struct parse_events_evlist *data; | 582 | struct parse_events_evlist *data; |
539 | struct list_head *list; | 583 | struct list_head *list; |
584 | struct list_head *head_config; | ||
540 | }; | 585 | }; |
541 | 586 | ||
542 | static int add_bpf_event(struct probe_trace_event *tev, int fd, | 587 | static int add_bpf_event(struct probe_trace_event *tev, int fd, |
@@ -553,7 +598,8 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd, | |||
553 | tev->group, tev->event, fd); | 598 | tev->group, tev->event, fd); |
554 | 599 | ||
555 | err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group, | 600 | err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group, |
556 | tev->event, evlist->error, NULL); | 601 | tev->event, evlist->error, |
602 | param->head_config); | ||
557 | if (err) { | 603 | if (err) { |
558 | struct perf_evsel *evsel, *tmp; | 604 | struct perf_evsel *evsel, *tmp; |
559 | 605 | ||
@@ -578,11 +624,12 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd, | |||
578 | 624 | ||
579 | int parse_events_load_bpf_obj(struct parse_events_evlist *data, | 625 | int parse_events_load_bpf_obj(struct parse_events_evlist *data, |
580 | struct list_head *list, | 626 | struct list_head *list, |
581 | struct bpf_object *obj) | 627 | struct bpf_object *obj, |
628 | struct list_head *head_config) | ||
582 | { | 629 | { |
583 | int err; | 630 | int err; |
584 | char errbuf[BUFSIZ]; | 631 | char errbuf[BUFSIZ]; |
585 | struct __add_bpf_event_param param = {data, list}; | 632 | struct __add_bpf_event_param param = {data, list, head_config}; |
586 | static bool registered_unprobe_atexit = false; | 633 | static bool registered_unprobe_atexit = false; |
587 | 634 | ||
588 | if (IS_ERR(obj) || !obj) { | 635 | if (IS_ERR(obj) || !obj) { |
@@ -628,17 +675,99 @@ errout: | |||
628 | return err; | 675 | return err; |
629 | } | 676 | } |
630 | 677 | ||
678 | static int | ||
679 | parse_events_config_bpf(struct parse_events_evlist *data, | ||
680 | struct bpf_object *obj, | ||
681 | struct list_head *head_config) | ||
682 | { | ||
683 | struct parse_events_term *term; | ||
684 | int error_pos; | ||
685 | |||
686 | if (!head_config || list_empty(head_config)) | ||
687 | return 0; | ||
688 | |||
689 | list_for_each_entry(term, head_config, list) { | ||
690 | char errbuf[BUFSIZ]; | ||
691 | int err; | ||
692 | |||
693 | if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) { | ||
694 | snprintf(errbuf, sizeof(errbuf), | ||
695 | "Invalid config term for BPF object"); | ||
696 | errbuf[BUFSIZ - 1] = '\0'; | ||
697 | |||
698 | data->error->idx = term->err_term; | ||
699 | data->error->str = strdup(errbuf); | ||
700 | return -EINVAL; | ||
701 | } | ||
702 | |||
703 | err = bpf__config_obj(obj, term, data->evlist, &error_pos); | ||
704 | if (err) { | ||
705 | bpf__strerror_config_obj(obj, term, data->evlist, | ||
706 | &error_pos, err, errbuf, | ||
707 | sizeof(errbuf)); | ||
708 | data->error->help = strdup( | ||
709 | "Hint:\tValid config terms:\n" | ||
710 | " \tmap:[<arraymap>].value<indices>=[value]\n" | ||
711 | " \tmap:[<eventmap>].event<indices>=[event]\n" | ||
712 | "\n" | ||
713 | " \twhere <indices> is something like [0,3...5] or [all]\n" | ||
714 | " \t(add -v to see detail)"); | ||
715 | data->error->str = strdup(errbuf); | ||
716 | if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE) | ||
717 | data->error->idx = term->err_val; | ||
718 | else | ||
719 | data->error->idx = term->err_term + error_pos; | ||
720 | return err; | ||
721 | } | ||
722 | } | ||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Split config terms: | ||
728 | * perf record -e bpf.c/call-graph=fp,map:array.value[0]=1/ ... | ||
729 | * 'call-graph=fp' is 'evt config', should be applied to each | ||
730 | * events in bpf.c. | ||
731 | * 'map:array.value[0]=1' is 'obj config', should be processed | ||
732 | * with parse_events_config_bpf. | ||
733 | * | ||
734 | * Move object config terms from the first list to obj_head_config. | ||
735 | */ | ||
736 | static void | ||
737 | split_bpf_config_terms(struct list_head *evt_head_config, | ||
738 | struct list_head *obj_head_config) | ||
739 | { | ||
740 | struct parse_events_term *term, *temp; | ||
741 | |||
742 | /* | ||
743 | * Currectly, all possible user config term | ||
744 | * belong to bpf object. parse_events__is_hardcoded_term() | ||
745 | * happends to be a good flag. | ||
746 | * | ||
747 | * See parse_events_config_bpf() and | ||
748 | * config_term_tracepoint(). | ||
749 | */ | ||
750 | list_for_each_entry_safe(term, temp, evt_head_config, list) | ||
751 | if (!parse_events__is_hardcoded_term(term)) | ||
752 | list_move_tail(&term->list, obj_head_config); | ||
753 | } | ||
754 | |||
631 | int parse_events_load_bpf(struct parse_events_evlist *data, | 755 | int parse_events_load_bpf(struct parse_events_evlist *data, |
632 | struct list_head *list, | 756 | struct list_head *list, |
633 | char *bpf_file_name, | 757 | char *bpf_file_name, |
634 | bool source) | 758 | bool source, |
759 | struct list_head *head_config) | ||
635 | { | 760 | { |
761 | int err; | ||
636 | struct bpf_object *obj; | 762 | struct bpf_object *obj; |
763 | LIST_HEAD(obj_head_config); | ||
764 | |||
765 | if (head_config) | ||
766 | split_bpf_config_terms(head_config, &obj_head_config); | ||
637 | 767 | ||
638 | obj = bpf__prepare_load(bpf_file_name, source); | 768 | obj = bpf__prepare_load(bpf_file_name, source); |
639 | if (IS_ERR(obj)) { | 769 | if (IS_ERR(obj)) { |
640 | char errbuf[BUFSIZ]; | 770 | char errbuf[BUFSIZ]; |
641 | int err; | ||
642 | 771 | ||
643 | err = PTR_ERR(obj); | 772 | err = PTR_ERR(obj); |
644 | 773 | ||
@@ -656,7 +785,18 @@ int parse_events_load_bpf(struct parse_events_evlist *data, | |||
656 | return err; | 785 | return err; |
657 | } | 786 | } |
658 | 787 | ||
659 | return parse_events_load_bpf_obj(data, list, obj); | 788 | err = parse_events_load_bpf_obj(data, list, obj, head_config); |
789 | if (err) | ||
790 | return err; | ||
791 | err = parse_events_config_bpf(data, obj, &obj_head_config); | ||
792 | |||
793 | /* | ||
794 | * Caller doesn't know anything about obj_head_config, | ||
795 | * so combine them together again before returnning. | ||
796 | */ | ||
797 | if (head_config) | ||
798 | list_splice_tail(&obj_head_config, head_config); | ||
799 | return err; | ||
660 | } | 800 | } |
661 | 801 | ||
662 | static int | 802 | static int |
@@ -743,9 +883,59 @@ static int check_type_val(struct parse_events_term *term, | |||
743 | return -EINVAL; | 883 | return -EINVAL; |
744 | } | 884 | } |
745 | 885 | ||
746 | typedef int config_term_func_t(struct perf_event_attr *attr, | 886 | /* |
747 | struct parse_events_term *term, | 887 | * Update according to parse-events.l |
748 | struct parse_events_error *err); | 888 | */ |
889 | static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { | ||
890 | [PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>", | ||
891 | [PARSE_EVENTS__TERM_TYPE_CONFIG] = "config", | ||
892 | [PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1", | ||
893 | [PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2", | ||
894 | [PARSE_EVENTS__TERM_TYPE_NAME] = "name", | ||
895 | [PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period", | ||
896 | [PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq", | ||
897 | [PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type", | ||
898 | [PARSE_EVENTS__TERM_TYPE_TIME] = "time", | ||
899 | [PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph", | ||
900 | [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size", | ||
901 | [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", | ||
902 | [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", | ||
903 | }; | ||
904 | |||
905 | static bool config_term_shrinked; | ||
906 | |||
907 | static bool | ||
908 | config_term_avail(int term_type, struct parse_events_error *err) | ||
909 | { | ||
910 | if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) { | ||
911 | err->str = strdup("Invalid term_type"); | ||
912 | return false; | ||
913 | } | ||
914 | if (!config_term_shrinked) | ||
915 | return true; | ||
916 | |||
917 | switch (term_type) { | ||
918 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
919 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
920 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
921 | case PARSE_EVENTS__TERM_TYPE_NAME: | ||
922 | return true; | ||
923 | default: | ||
924 | if (!err) | ||
925 | return false; | ||
926 | |||
927 | /* term_type is validated so indexing is safe */ | ||
928 | if (asprintf(&err->str, "'%s' is not usable in 'perf stat'", | ||
929 | config_term_names[term_type]) < 0) | ||
930 | err->str = NULL; | ||
931 | return false; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | void parse_events__shrink_config_terms(void) | ||
936 | { | ||
937 | config_term_shrinked = true; | ||
938 | } | ||
749 | 939 | ||
750 | static int config_term_common(struct perf_event_attr *attr, | 940 | static int config_term_common(struct perf_event_attr *attr, |
751 | struct parse_events_term *term, | 941 | struct parse_events_term *term, |
@@ -812,6 +1002,17 @@ do { \ | |||
812 | return -EINVAL; | 1002 | return -EINVAL; |
813 | } | 1003 | } |
814 | 1004 | ||
1005 | /* | ||
1006 | * Check term availbility after basic checking so | ||
1007 | * PARSE_EVENTS__TERM_TYPE_USER can be found and filtered. | ||
1008 | * | ||
1009 | * If check availbility at the entry of this function, | ||
1010 | * user will see "'<sysfs term>' is not usable in 'perf stat'" | ||
1011 | * if an invalid config term is provided for legacy events | ||
1012 | * (for example, instructions/badterm/...), which is confusing. | ||
1013 | */ | ||
1014 | if (!config_term_avail(term->type_term, err)) | ||
1015 | return -EINVAL; | ||
815 | return 0; | 1016 | return 0; |
816 | #undef CHECK_TYPE_VAL | 1017 | #undef CHECK_TYPE_VAL |
817 | } | 1018 | } |
@@ -958,23 +1159,8 @@ int parse_events_add_numeric(struct parse_events_evlist *data, | |||
958 | return -ENOMEM; | 1159 | return -ENOMEM; |
959 | } | 1160 | } |
960 | 1161 | ||
961 | return add_event(list, &data->idx, &attr, NULL, &config_terms); | 1162 | return add_event(list, &data->idx, &attr, |
962 | } | 1163 | get_config_name(head_config), &config_terms); |
963 | |||
964 | static int parse_events__is_name_term(struct parse_events_term *term) | ||
965 | { | ||
966 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; | ||
967 | } | ||
968 | |||
969 | static char *pmu_event_name(struct list_head *head_terms) | ||
970 | { | ||
971 | struct parse_events_term *term; | ||
972 | |||
973 | list_for_each_entry(term, head_terms, list) | ||
974 | if (parse_events__is_name_term(term)) | ||
975 | return term->val.str; | ||
976 | |||
977 | return NULL; | ||
978 | } | 1164 | } |
979 | 1165 | ||
980 | int parse_events_add_pmu(struct parse_events_evlist *data, | 1166 | int parse_events_add_pmu(struct parse_events_evlist *data, |
@@ -1021,7 +1207,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, | |||
1021 | return -EINVAL; | 1207 | return -EINVAL; |
1022 | 1208 | ||
1023 | evsel = __add_event(list, &data->idx, &attr, | 1209 | evsel = __add_event(list, &data->idx, &attr, |
1024 | pmu_event_name(head_config), pmu->cpus, | 1210 | get_config_name(head_config), pmu->cpus, |
1025 | &config_terms); | 1211 | &config_terms); |
1026 | if (evsel) { | 1212 | if (evsel) { |
1027 | evsel->unit = info.unit; | 1213 | evsel->unit = info.unit; |
@@ -1383,8 +1569,7 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
1383 | return 0; | 1569 | return 0; |
1384 | } | 1570 | } |
1385 | 1571 | ||
1386 | if (data.terms) | 1572 | parse_events_terms__delete(data.terms); |
1387 | parse_events__free_terms(data.terms); | ||
1388 | return ret; | 1573 | return ret; |
1389 | } | 1574 | } |
1390 | 1575 | ||
@@ -1392,9 +1577,10 @@ int parse_events(struct perf_evlist *evlist, const char *str, | |||
1392 | struct parse_events_error *err) | 1577 | struct parse_events_error *err) |
1393 | { | 1578 | { |
1394 | struct parse_events_evlist data = { | 1579 | struct parse_events_evlist data = { |
1395 | .list = LIST_HEAD_INIT(data.list), | 1580 | .list = LIST_HEAD_INIT(data.list), |
1396 | .idx = evlist->nr_entries, | 1581 | .idx = evlist->nr_entries, |
1397 | .error = err, | 1582 | .error = err, |
1583 | .evlist = evlist, | ||
1398 | }; | 1584 | }; |
1399 | int ret; | 1585 | int ret; |
1400 | 1586 | ||
@@ -2065,12 +2251,29 @@ int parse_events_term__clone(struct parse_events_term **new, | |||
2065 | term->err_term, term->err_val); | 2251 | term->err_term, term->err_val); |
2066 | } | 2252 | } |
2067 | 2253 | ||
2068 | void parse_events__free_terms(struct list_head *terms) | 2254 | void parse_events_terms__purge(struct list_head *terms) |
2069 | { | 2255 | { |
2070 | struct parse_events_term *term, *h; | 2256 | struct parse_events_term *term, *h; |
2071 | 2257 | ||
2072 | list_for_each_entry_safe(term, h, terms, list) | 2258 | list_for_each_entry_safe(term, h, terms, list) { |
2259 | if (term->array.nr_ranges) | ||
2260 | free(term->array.ranges); | ||
2261 | list_del_init(&term->list); | ||
2073 | free(term); | 2262 | free(term); |
2263 | } | ||
2264 | } | ||
2265 | |||
2266 | void parse_events_terms__delete(struct list_head *terms) | ||
2267 | { | ||
2268 | if (!terms) | ||
2269 | return; | ||
2270 | parse_events_terms__purge(terms); | ||
2271 | free(terms); | ||
2272 | } | ||
2273 | |||
2274 | void parse_events__clear_array(struct parse_events_array *a) | ||
2275 | { | ||
2276 | free(a->ranges); | ||
2074 | } | 2277 | } |
2075 | 2278 | ||
2076 | void parse_events_evlist_error(struct parse_events_evlist *data, | 2279 | void parse_events_evlist_error(struct parse_events_evlist *data, |
@@ -2085,6 +2288,33 @@ void parse_events_evlist_error(struct parse_events_evlist *data, | |||
2085 | WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); | 2288 | WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); |
2086 | } | 2289 | } |
2087 | 2290 | ||
2291 | static void config_terms_list(char *buf, size_t buf_sz) | ||
2292 | { | ||
2293 | int i; | ||
2294 | bool first = true; | ||
2295 | |||
2296 | buf[0] = '\0'; | ||
2297 | for (i = 0; i < __PARSE_EVENTS__TERM_TYPE_NR; i++) { | ||
2298 | const char *name = config_term_names[i]; | ||
2299 | |||
2300 | if (!config_term_avail(i, NULL)) | ||
2301 | continue; | ||
2302 | if (!name) | ||
2303 | continue; | ||
2304 | if (name[0] == '<') | ||
2305 | continue; | ||
2306 | |||
2307 | if (strlen(buf) + strlen(name) + 2 >= buf_sz) | ||
2308 | return; | ||
2309 | |||
2310 | if (!first) | ||
2311 | strcat(buf, ","); | ||
2312 | else | ||
2313 | first = false; | ||
2314 | strcat(buf, name); | ||
2315 | } | ||
2316 | } | ||
2317 | |||
2088 | /* | 2318 | /* |
2089 | * Return string contains valid config terms of an event. | 2319 | * Return string contains valid config terms of an event. |
2090 | * @additional_terms: For terms such as PMU sysfs terms. | 2320 | * @additional_terms: For terms such as PMU sysfs terms. |
@@ -2092,17 +2322,18 @@ void parse_events_evlist_error(struct parse_events_evlist *data, | |||
2092 | char *parse_events_formats_error_string(char *additional_terms) | 2322 | char *parse_events_formats_error_string(char *additional_terms) |
2093 | { | 2323 | { |
2094 | char *str; | 2324 | char *str; |
2095 | static const char *static_terms = "config,config1,config2,name," | 2325 | /* "branch_type" is the longest name */ |
2096 | "period,freq,branch_type,time," | 2326 | char static_terms[__PARSE_EVENTS__TERM_TYPE_NR * |
2097 | "call-graph,stack-size\n"; | 2327 | (sizeof("branch_type") - 1)]; |
2098 | 2328 | ||
2329 | config_terms_list(static_terms, sizeof(static_terms)); | ||
2099 | /* valid terms */ | 2330 | /* valid terms */ |
2100 | if (additional_terms) { | 2331 | if (additional_terms) { |
2101 | if (!asprintf(&str, "valid terms: %s,%s", | 2332 | if (asprintf(&str, "valid terms: %s,%s", |
2102 | additional_terms, static_terms)) | 2333 | additional_terms, static_terms) < 0) |
2103 | goto fail; | 2334 | goto fail; |
2104 | } else { | 2335 | } else { |
2105 | if (!asprintf(&str, "valid terms: %s", static_terms)) | 2336 | if (asprintf(&str, "valid terms: %s", static_terms) < 0) |
2106 | goto fail; | 2337 | goto fail; |
2107 | } | 2338 | } |
2108 | return str; | 2339 | return str; |