probe-event.c (7ae383be81781c5e1347f71c3eb0d53ce5188200) probe-event.c (ae2cb1ac60758e99cec15e9edd68e0d22bfd310e)
1/*
2 * probe-event.c : perf-probe definition to probe_events format converter
3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or

--- 1063 unchanged lines hidden (view full) ---

1072}
1073
1074/* Parse probepoint definition. */
1075static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
1076{
1077 struct perf_probe_point *pp = &pev->point;
1078 char *ptr, *tmp;
1079 char c, nc = 0;
1/*
2 * probe-event.c : perf-probe definition to probe_events format converter
3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or

--- 1063 unchanged lines hidden (view full) ---

1072}
1073
1074/* Parse probepoint definition. */
1075static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
1076{
1077 struct perf_probe_point *pp = &pev->point;
1078 char *ptr, *tmp;
1079 char c, nc = 0;
1080 bool file_spec = false;
1080 /*
1081 * <Syntax>
1082 * perf probe [EVENT=]SRC[:LN|;PTN]
1083 * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
1084 *
1085 * TODO:Group name support
1086 */
1081 /*
1082 * <Syntax>
1083 * perf probe [EVENT=]SRC[:LN|;PTN]
1084 * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
1085 *
1086 * TODO:Group name support
1087 */
1087 if (!arg)
1088 return -EINVAL;
1089
1090 ptr = strpbrk(arg, ";=@+%");
1091 if (ptr && *ptr == '=') { /* Event name */
1092 *ptr = '\0';
1093 tmp = ptr + 1;
1094 if (strchr(arg, ':')) {
1095 semantic_error("Group name is not supported yet.\n");
1096 return -ENOTSUP;

--- 5 unchanged lines hidden (view full) ---

1102 }
1103 pev->event = strdup(arg);
1104 if (pev->event == NULL)
1105 return -ENOMEM;
1106 pev->group = NULL;
1107 arg = tmp;
1108 }
1109
1088
1089 ptr = strpbrk(arg, ";=@+%");
1090 if (ptr && *ptr == '=') { /* Event name */
1091 *ptr = '\0';
1092 tmp = ptr + 1;
1093 if (strchr(arg, ':')) {
1094 semantic_error("Group name is not supported yet.\n");
1095 return -ENOTSUP;

--- 5 unchanged lines hidden (view full) ---

1101 }
1102 pev->event = strdup(arg);
1103 if (pev->event == NULL)
1104 return -ENOMEM;
1105 pev->group = NULL;
1106 arg = tmp;
1107 }
1108
1109 /*
1110 * Check arg is function or file name and copy it.
1111 *
1112 * We consider arg to be a file spec if and only if it satisfies
1113 * all of the below criteria::
1114 * - it does not include any of "+@%",
1115 * - it includes one of ":;", and
1116 * - it has a period '.' in the name.
1117 *
1118 * Otherwise, we consider arg to be a function specification.
1119 */
1120 if (!strpbrk(arg, "+@%") && (ptr = strpbrk(arg, ";:")) != NULL) {
1121 /* This is a file spec if it includes a '.' before ; or : */
1122 if (memchr(arg, '.', ptr - arg))
1123 file_spec = true;
1124 }
1125
1110 ptr = strpbrk(arg, ";:+@%");
1111 if (ptr) {
1112 nc = *ptr;
1113 *ptr++ = '\0';
1114 }
1115
1116 tmp = strdup(arg);
1117 if (tmp == NULL)
1118 return -ENOMEM;
1119
1126 ptr = strpbrk(arg, ";:+@%");
1127 if (ptr) {
1128 nc = *ptr;
1129 *ptr++ = '\0';
1130 }
1131
1132 tmp = strdup(arg);
1133 if (tmp == NULL)
1134 return -ENOMEM;
1135
1120 /* Check arg is function or file and copy it */
1121 if (strchr(tmp, '.')) /* File */
1136 if (file_spec)
1122 pp->file = tmp;
1137 pp->file = tmp;
1123 else /* Function */
1138 else
1124 pp->function = tmp;
1125
1126 /* Parse other options */
1127 while (ptr) {
1128 arg = ptr;
1129 c = nc;
1130 if (c == ';') { /* Lazy pattern must be the last part */
1131 pp->lazy_line = strdup(arg);

--- 994 unchanged lines hidden (view full) ---

2126 pr_info(" %s", buf);
2127 }
2128 }
2129 pr_info(")\n");
2130 free(place);
2131 return ret;
2132}
2133
1139 pp->function = tmp;
1140
1141 /* Parse other options */
1142 while (ptr) {
1143 arg = ptr;
1144 c = nc;
1145 if (c == ';') { /* Lazy pattern must be the last part */
1146 pp->lazy_line = strdup(arg);

--- 994 unchanged lines hidden (view full) ---

2141 pr_info(" %s", buf);
2142 }
2143 }
2144 pr_info(")\n");
2145 free(place);
2146 return ret;
2147}
2148
2134static int __show_perf_probe_events(int fd, bool is_kprobe)
2149static bool filter_probe_trace_event(struct probe_trace_event *tev,
2150 struct strfilter *filter)
2135{
2151{
2152 char tmp[128];
2153
2154 /* At first, check the event name itself */
2155 if (strfilter__compare(filter, tev->event))
2156 return true;
2157
2158 /* Next, check the combination of name and group */
2159 if (e_snprintf(tmp, 128, "%s:%s", tev->group, tev->event) < 0)
2160 return false;
2161 return strfilter__compare(filter, tmp);
2162}
2163
2164static int __show_perf_probe_events(int fd, bool is_kprobe,
2165 struct strfilter *filter)
2166{
2136 int ret = 0;
2137 struct probe_trace_event tev;
2138 struct perf_probe_event pev;
2139 struct strlist *rawlist;
2140 struct str_node *ent;
2141
2142 memset(&tev, 0, sizeof(tev));
2143 memset(&pev, 0, sizeof(pev));
2144
2145 rawlist = get_probe_trace_command_rawlist(fd);
2146 if (!rawlist)
2147 return -ENOMEM;
2148
2149 strlist__for_each(ent, rawlist) {
2150 ret = parse_probe_trace_command(ent->s, &tev);
2151 if (ret >= 0) {
2167 int ret = 0;
2168 struct probe_trace_event tev;
2169 struct perf_probe_event pev;
2170 struct strlist *rawlist;
2171 struct str_node *ent;
2172
2173 memset(&tev, 0, sizeof(tev));
2174 memset(&pev, 0, sizeof(pev));
2175
2176 rawlist = get_probe_trace_command_rawlist(fd);
2177 if (!rawlist)
2178 return -ENOMEM;
2179
2180 strlist__for_each(ent, rawlist) {
2181 ret = parse_probe_trace_command(ent->s, &tev);
2182 if (ret >= 0) {
2183 if (!filter_probe_trace_event(&tev, filter))
2184 goto next;
2152 ret = convert_to_perf_probe_event(&tev, &pev,
2153 is_kprobe);
2154 if (ret >= 0)
2155 ret = show_perf_probe_event(&pev,
2156 tev.point.module);
2157 }
2185 ret = convert_to_perf_probe_event(&tev, &pev,
2186 is_kprobe);
2187 if (ret >= 0)
2188 ret = show_perf_probe_event(&pev,
2189 tev.point.module);
2190 }
2191next:
2158 clear_perf_probe_event(&pev);
2159 clear_probe_trace_event(&tev);
2160 if (ret < 0)
2161 break;
2162 }
2163 strlist__delete(rawlist);
2164
2165 return ret;
2166}
2167
2168/* List up current perf-probe events */
2192 clear_perf_probe_event(&pev);
2193 clear_probe_trace_event(&tev);
2194 if (ret < 0)
2195 break;
2196 }
2197 strlist__delete(rawlist);
2198
2199 return ret;
2200}
2201
2202/* List up current perf-probe events */
2169int show_perf_probe_events(void)
2203int show_perf_probe_events(struct strfilter *filter)
2170{
2171 int kp_fd, up_fd, ret;
2172
2173 setup_pager();
2174
2175 ret = init_symbol_maps(false);
2176 if (ret < 0)
2177 return ret;
2178
2179 kp_fd = open_kprobe_events(false);
2180 if (kp_fd >= 0) {
2204{
2205 int kp_fd, up_fd, ret;
2206
2207 setup_pager();
2208
2209 ret = init_symbol_maps(false);
2210 if (ret < 0)
2211 return ret;
2212
2213 kp_fd = open_kprobe_events(false);
2214 if (kp_fd >= 0) {
2181 ret = __show_perf_probe_events(kp_fd, true);
2215 ret = __show_perf_probe_events(kp_fd, true, filter);
2182 close(kp_fd);
2183 if (ret < 0)
2184 goto out;
2185 }
2186
2187 up_fd = open_uprobe_events(false);
2188 if (kp_fd < 0 && up_fd < 0) {
2189 print_both_open_warning(kp_fd, up_fd);
2190 ret = kp_fd;
2191 goto out;
2192 }
2193
2194 if (up_fd >= 0) {
2216 close(kp_fd);
2217 if (ret < 0)
2218 goto out;
2219 }
2220
2221 up_fd = open_uprobe_events(false);
2222 if (kp_fd < 0 && up_fd < 0) {
2223 print_both_open_warning(kp_fd, up_fd);
2224 ret = kp_fd;
2225 goto out;
2226 }
2227
2228 if (up_fd >= 0) {
2195 ret = __show_perf_probe_events(up_fd, false);
2229 ret = __show_perf_probe_events(up_fd, false, filter);
2196 close(up_fd);
2197 }
2198out:
2199 exit_symbol_maps();
2200 return ret;
2201}
2202
2203/* Get current perf-probe event names */

--- 58 unchanged lines hidden (view full) ---

2262 return ret;
2263}
2264
2265static int get_new_event_name(char *buf, size_t len, const char *base,
2266 struct strlist *namelist, bool allow_suffix)
2267{
2268 int i, ret;
2269
2230 close(up_fd);
2231 }
2232out:
2233 exit_symbol_maps();
2234 return ret;
2235}
2236
2237/* Get current perf-probe event names */

--- 58 unchanged lines hidden (view full) ---

2296 return ret;
2297}
2298
2299static int get_new_event_name(char *buf, size_t len, const char *base,
2300 struct strlist *namelist, bool allow_suffix)
2301{
2302 int i, ret;
2303
2304 if (*base == '.')
2305 base++;
2306
2270 /* Try no suffix */
2271 ret = e_snprintf(buf, len, "%s", base);
2272 if (ret < 0) {
2273 pr_debug("snprintf() failed: %d\n", ret);
2274 return ret;
2275 }
2276 if (!strlist__has_entry(namelist, buf))
2277 return 0;

--- 64 unchanged lines hidden (view full) ---

2342 print_open_warning(fd, !pev->uprobes);
2343 return fd;
2344 }
2345
2346 /* Get current event names */
2347 namelist = get_probe_trace_event_names(fd, false);
2348 if (!namelist) {
2349 pr_debug("Failed to get current event list.\n");
2307 /* Try no suffix */
2308 ret = e_snprintf(buf, len, "%s", base);
2309 if (ret < 0) {
2310 pr_debug("snprintf() failed: %d\n", ret);
2311 return ret;
2312 }
2313 if (!strlist__has_entry(namelist, buf))
2314 return 0;

--- 64 unchanged lines hidden (view full) ---

2379 print_open_warning(fd, !pev->uprobes);
2380 return fd;
2381 }
2382
2383 /* Get current event names */
2384 namelist = get_probe_trace_event_names(fd, false);
2385 if (!namelist) {
2386 pr_debug("Failed to get current event list.\n");
2350 return -EIO;
2387 ret = -ENOMEM;
2388 goto close_out;
2351 }
2352 /* Get kprobe blacklist if exists */
2353 if (!pev->uprobes) {
2354 ret = kprobe_blacklist__load(&blacklist);
2355 if (ret < 0)
2356 pr_debug("No kprobe blacklist support, ignored\n");
2357 }
2358

--- 66 unchanged lines hidden (view full) ---

2425 /* Show how to use the event. */
2426 pr_info("\nYou can now use it in all perf tools, such as:\n\n");
2427 pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
2428 tev->event);
2429 }
2430
2431 kprobe_blacklist__delete(&blacklist);
2432 strlist__delete(namelist);
2389 }
2390 /* Get kprobe blacklist if exists */
2391 if (!pev->uprobes) {
2392 ret = kprobe_blacklist__load(&blacklist);
2393 if (ret < 0)
2394 pr_debug("No kprobe blacklist support, ignored\n");
2395 }
2396

--- 66 unchanged lines hidden (view full) ---

2463 /* Show how to use the event. */
2464 pr_info("\nYou can now use it in all perf tools, such as:\n\n");
2465 pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
2466 tev->event);
2467 }
2468
2469 kprobe_blacklist__delete(&blacklist);
2470 strlist__delete(namelist);
2471close_out:
2433 close(fd);
2434 return ret;
2435}
2436
2437static int find_probe_functions(struct map *map, char *name)
2438{
2439 int found = 0;
2440 struct symbol *sym;
2441
2442 map__for_each_symbol_by_name(map, name, sym) {
2443 found++;
2444 }
2445
2446 return found;
2447}
2448
2449#define strdup_or_goto(str, label) \
2450 ({ char *__p = strdup(str); if (!__p) goto label; __p; })
2451
2472 close(fd);
2473 return ret;
2474}
2475
2476static int find_probe_functions(struct map *map, char *name)
2477{
2478 int found = 0;
2479 struct symbol *sym;
2480
2481 map__for_each_symbol_by_name(map, name, sym) {
2482 found++;
2483 }
2484
2485 return found;
2486}
2487
2488#define strdup_or_goto(str, label) \
2489 ({ char *__p = strdup(str); if (!__p) goto label; __p; })
2490
2491void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
2492 struct probe_trace_event *tev __maybe_unused,
2493 struct map *map __maybe_unused) { }
2494
2452/*
2453 * Find probe function addresses from map.
2454 * Return an error or the number of found probe_trace_event
2455 */
2456static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
2457 struct probe_trace_event **tevs,
2458 int max_tevs, const char *target)
2459{

--- 90 unchanged lines hidden (view full) ---

2550
2551 tev->args[i].value = strdup_or_goto(pev->args[i].var,
2552 nomem_out);
2553 if (pev->args[i].type)
2554 tev->args[i].type =
2555 strdup_or_goto(pev->args[i].type,
2556 nomem_out);
2557 }
2495/*
2496 * Find probe function addresses from map.
2497 * Return an error or the number of found probe_trace_event
2498 */
2499static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
2500 struct probe_trace_event **tevs,
2501 int max_tevs, const char *target)
2502{

--- 90 unchanged lines hidden (view full) ---

2593
2594 tev->args[i].value = strdup_or_goto(pev->args[i].var,
2595 nomem_out);
2596 if (pev->args[i].type)
2597 tev->args[i].type =
2598 strdup_or_goto(pev->args[i].type,
2599 nomem_out);
2600 }
2601 arch__fix_tev_from_maps(pev, tev, map);
2558 }
2559
2560out:
2561 put_target_map(map, pev->uprobes);
2562 return ret;
2563
2564nomem_out:
2565 ret = -ENOMEM;
2566err_out:
2567 clear_probe_trace_events(*tevs, num_matched_functions);
2568 zfree(tevs);
2569 goto out;
2570}
2571
2602 }
2603
2604out:
2605 put_target_map(map, pev->uprobes);
2606 return ret;
2607
2608nomem_out:
2609 ret = -ENOMEM;
2610err_out:
2611 clear_probe_trace_events(*tevs, num_matched_functions);
2612 zfree(tevs);
2613 goto out;
2614}
2615
2616bool __weak arch__prefers_symtab(void) { return false; }
2617
2572static int convert_to_probe_trace_events(struct perf_probe_event *pev,
2573 struct probe_trace_event **tevs,
2574 int max_tevs, const char *target)
2575{
2576 int ret;
2577
2578 if (pev->uprobes && !pev->group) {
2579 /* Replace group name if not given */
2580 ret = convert_exec_to_group(target, &pev->group);
2581 if (ret != 0) {
2582 pr_warning("Failed to make a group name.\n");
2583 return ret;
2584 }
2585 }
2586
2618static int convert_to_probe_trace_events(struct perf_probe_event *pev,
2619 struct probe_trace_event **tevs,
2620 int max_tevs, const char *target)
2621{
2622 int ret;
2623
2624 if (pev->uprobes && !pev->group) {
2625 /* Replace group name if not given */
2626 ret = convert_exec_to_group(target, &pev->group);
2627 if (ret != 0) {
2628 pr_warning("Failed to make a group name.\n");
2629 return ret;
2630 }
2631 }
2632
2633 if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
2634 ret = find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
2635 if (ret > 0)
2636 return ret; /* Found in symbol table */
2637 }
2638
2587 /* Convert perf_probe_event with debuginfo */
2588 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
2589 if (ret != 0)
2590 return ret; /* Found in debuginfo or got an error */
2591
2592 return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
2593}
2594

--- 84 unchanged lines hidden (view full) ---

2679 pr_info("Removed event: %s\n", ent->s);
2680 return 0;
2681error:
2682 pr_warning("Failed to delete event: %s\n",
2683 strerror_r(-ret, buf, sizeof(buf)));
2684 return ret;
2685}
2686
2639 /* Convert perf_probe_event with debuginfo */
2640 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
2641 if (ret != 0)
2642 return ret; /* Found in debuginfo or got an error */
2643
2644 return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
2645}
2646

--- 84 unchanged lines hidden (view full) ---

2731 pr_info("Removed event: %s\n", ent->s);
2732 return 0;
2733error:
2734 pr_warning("Failed to delete event: %s\n",
2735 strerror_r(-ret, buf, sizeof(buf)));
2736 return ret;
2737}
2738
2687static int del_trace_probe_event(int fd, const char *buf,
2688 struct strlist *namelist)
2739static int del_trace_probe_events(int fd, struct strfilter *filter,
2740 struct strlist *namelist)
2689{
2741{
2690 struct str_node *ent, *n;
2691 int ret = -1;
2742 struct str_node *ent;
2743 const char *p;
2744 int ret = -ENOENT;
2692
2745
2693 if (strpbrk(buf, "*?")) { /* Glob-exp */
2694 strlist__for_each_safe(ent, n, namelist)
2695 if (strglobmatch(ent->s, buf)) {
2696 ret = __del_trace_probe_event(fd, ent);
2697 if (ret < 0)
2698 break;
2699 strlist__remove(namelist, ent);
2700 }
2701 } else {
2702 ent = strlist__find(namelist, buf);
2703 if (ent) {
2746 if (!namelist)
2747 return -ENOENT;
2748
2749 strlist__for_each(ent, namelist) {
2750 p = strchr(ent->s, ':');
2751 if ((p && strfilter__compare(filter, p + 1)) ||
2752 strfilter__compare(filter, ent->s)) {
2704 ret = __del_trace_probe_event(fd, ent);
2753 ret = __del_trace_probe_event(fd, ent);
2705 if (ret >= 0)
2706 strlist__remove(namelist, ent);
2754 if (ret < 0)
2755 break;
2707 }
2708 }
2709
2710 return ret;
2711}
2712
2756 }
2757 }
2758
2759 return ret;
2760}
2761
2713int del_perf_probe_events(struct strlist *dellist)
2762int del_perf_probe_events(struct strfilter *filter)
2714{
2763{
2715 int ret = -1, ufd = -1, kfd = -1;
2716 char buf[128];
2717 const char *group, *event;
2718 char *p, *str;
2719 struct str_node *ent;
2764 int ret, ret2, ufd = -1, kfd = -1;
2720 struct strlist *namelist = NULL, *unamelist = NULL;
2765 struct strlist *namelist = NULL, *unamelist = NULL;
2766 char *str = strfilter__string(filter);
2721
2767
2768 if (!str)
2769 return -EINVAL;
2770
2771 pr_debug("Delete filter: \'%s\'\n", str);
2772
2722 /* Get current event names */
2723 kfd = open_kprobe_events(true);
2724 if (kfd >= 0)
2725 namelist = get_probe_trace_event_names(kfd, true);
2726
2727 ufd = open_uprobe_events(true);
2728 if (ufd >= 0)
2729 unamelist = get_probe_trace_event_names(ufd, true);
2730
2731 if (kfd < 0 && ufd < 0) {
2732 print_both_open_warning(kfd, ufd);
2773 /* Get current event names */
2774 kfd = open_kprobe_events(true);
2775 if (kfd >= 0)
2776 namelist = get_probe_trace_event_names(kfd, true);
2777
2778 ufd = open_uprobe_events(true);
2779 if (ufd >= 0)
2780 unamelist = get_probe_trace_event_names(ufd, true);
2781
2782 if (kfd < 0 && ufd < 0) {
2783 print_both_open_warning(kfd, ufd);
2784 ret = kfd;
2733 goto error;
2734 }
2735
2785 goto error;
2786 }
2787
2736 if (namelist == NULL && unamelist == NULL)
2788 ret = del_trace_probe_events(kfd, filter, namelist);
2789 if (ret < 0 && ret != -ENOENT)
2737 goto error;
2738
2790 goto error;
2791
2739 strlist__for_each(ent, dellist) {
2740 str = strdup(ent->s);
2741 if (str == NULL) {
2742 ret = -ENOMEM;
2743 goto error;
2744 }
2745 pr_debug("Parsing: %s\n", str);
2746 p = strchr(str, ':');
2747 if (p) {
2748 group = str;
2749 *p = '\0';
2750 event = p + 1;
2751 } else {
2752 group = "*";
2753 event = str;
2754 }
2755
2756 ret = e_snprintf(buf, 128, "%s:%s", group, event);
2757 if (ret < 0) {
2758 pr_err("Failed to copy event.");
2759 free(str);
2760 goto error;
2761 }
2762
2763 pr_debug("Group: %s, Event: %s\n", group, event);
2764
2765 if (namelist)
2766 ret = del_trace_probe_event(kfd, buf, namelist);
2767
2768 if (unamelist && ret != 0)
2769 ret = del_trace_probe_event(ufd, buf, unamelist);
2770
2771 if (ret != 0)
2772 pr_info("Info: Event \"%s\" does not exist.\n", buf);
2773
2774 free(str);
2792 ret2 = del_trace_probe_events(ufd, filter, unamelist);
2793 if (ret2 < 0 && ret2 != -ENOENT)
2794 ret = ret2;
2795 else if (ret == -ENOENT && ret2 == -ENOENT) {
2796 pr_debug("\"%s\" does not hit any event.\n", str);
2797 /* Note that this is silently ignored */
2798 ret = 0;
2775 }
2776
2777error:
2778 if (kfd >= 0) {
2779 strlist__delete(namelist);
2780 close(kfd);
2781 }
2782
2783 if (ufd >= 0) {
2784 strlist__delete(unamelist);
2785 close(ufd);
2786 }
2799 }
2800
2801error:
2802 if (kfd >= 0) {
2803 strlist__delete(namelist);
2804 close(kfd);
2805 }
2806
2807 if (ufd >= 0) {
2808 strlist__delete(unamelist);
2809 close(ufd);
2810 }
2811 free(str);
2787
2788 return ret;
2789}
2790
2791/* TODO: don't use a global variable for filter ... */
2792static struct strfilter *available_func_filter;
2793
2794/*

--- 53 unchanged lines hidden ---
2812
2813 return ret;
2814}
2815
2816/* TODO: don't use a global variable for filter ... */
2817static struct strfilter *available_func_filter;
2818
2819/*

--- 53 unchanged lines hidden ---