aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/probe-event.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/probe-event.c')
-rw-r--r--tools/perf/util/probe-event.c863
1 files changed, 475 insertions, 388 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index d8b048c20cde..0d1542f33d87 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -70,34 +70,32 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
70} 70}
71 71
72static char *synthesize_perf_probe_point(struct perf_probe_point *pp); 72static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
73static int convert_name_to_addr(struct perf_probe_event *pev,
74 const char *exec);
75static void clear_probe_trace_event(struct probe_trace_event *tev); 73static void clear_probe_trace_event(struct probe_trace_event *tev);
76static struct machine machine; 74static struct machine *host_machine;
77 75
78/* Initialize symbol maps and path of vmlinux/modules */ 76/* Initialize symbol maps and path of vmlinux/modules */
79static int init_vmlinux(void) 77static int init_symbol_maps(bool user_only)
80{ 78{
81 int ret; 79 int ret;
82 80
83 symbol_conf.sort_by_name = true; 81 symbol_conf.sort_by_name = true;
84 if (symbol_conf.vmlinux_name == NULL)
85 symbol_conf.try_vmlinux_path = true;
86 else
87 pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
88 ret = symbol__init(); 82 ret = symbol__init();
89 if (ret < 0) { 83 if (ret < 0) {
90 pr_debug("Failed to init symbol map.\n"); 84 pr_debug("Failed to init symbol map.\n");
91 goto out; 85 goto out;
92 } 86 }
93 87
94 ret = machine__init(&machine, "", HOST_KERNEL_ID); 88 if (host_machine || user_only) /* already initialized */
95 if (ret < 0) 89 return 0;
96 goto out;
97 90
98 if (machine__create_kernel_maps(&machine) < 0) { 91 if (symbol_conf.vmlinux_name)
99 pr_debug("machine__create_kernel_maps() failed.\n"); 92 pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
100 goto out; 93
94 host_machine = machine__new_host();
95 if (!host_machine) {
96 pr_debug("machine__new_host() failed.\n");
97 symbol__exit();
98 ret = -1;
101 } 99 }
102out: 100out:
103 if (ret < 0) 101 if (ret < 0)
@@ -105,21 +103,66 @@ out:
105 return ret; 103 return ret;
106} 104}
107 105
106static void exit_symbol_maps(void)
107{
108 if (host_machine) {
109 machine__delete(host_machine);
110 host_machine = NULL;
111 }
112 symbol__exit();
113}
114
108static struct symbol *__find_kernel_function_by_name(const char *name, 115static struct symbol *__find_kernel_function_by_name(const char *name,
109 struct map **mapp) 116 struct map **mapp)
110{ 117{
111 return machine__find_kernel_function_by_name(&machine, name, mapp, 118 return machine__find_kernel_function_by_name(host_machine, name, mapp,
112 NULL); 119 NULL);
113} 120}
114 121
122static struct symbol *__find_kernel_function(u64 addr, struct map **mapp)
123{
124 return machine__find_kernel_function(host_machine, addr, mapp, NULL);
125}
126
127static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void)
128{
129 /* kmap->ref_reloc_sym should be set if host_machine is initialized */
130 struct kmap *kmap;
131
132 if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0)
133 return NULL;
134
135 kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]);
136 return kmap->ref_reloc_sym;
137}
138
139static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc)
140{
141 struct ref_reloc_sym *reloc_sym;
142 struct symbol *sym;
143 struct map *map;
144
145 /* ref_reloc_sym is just a label. Need a special fix*/
146 reloc_sym = kernel_get_ref_reloc_sym();
147 if (reloc_sym && strcmp(name, reloc_sym->name) == 0)
148 return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr;
149 else {
150 sym = __find_kernel_function_by_name(name, &map);
151 if (sym)
152 return map->unmap_ip(map, sym->start) -
153 (reloc) ? 0 : map->reloc;
154 }
155 return 0;
156}
157
115static struct map *kernel_get_module_map(const char *module) 158static struct map *kernel_get_module_map(const char *module)
116{ 159{
117 struct rb_node *nd; 160 struct rb_node *nd;
118 struct map_groups *grp = &machine.kmaps; 161 struct map_groups *grp = &host_machine->kmaps;
119 162
120 /* A file path -- this is an offline module */ 163 /* A file path -- this is an offline module */
121 if (module && strchr(module, '/')) 164 if (module && strchr(module, '/'))
122 return machine__new_module(&machine, 0, module); 165 return machine__new_module(host_machine, 0, module);
123 166
124 if (!module) 167 if (!module)
125 module = "kernel"; 168 module = "kernel";
@@ -141,7 +184,7 @@ static struct dso *kernel_get_module_dso(const char *module)
141 const char *vmlinux_name; 184 const char *vmlinux_name;
142 185
143 if (module) { 186 if (module) {
144 list_for_each_entry(dso, &machine.kernel_dsos, node) { 187 list_for_each_entry(dso, &host_machine->kernel_dsos, node) {
145 if (strncmp(dso->short_name + 1, module, 188 if (strncmp(dso->short_name + 1, module,
146 dso->short_name_len - 2) == 0) 189 dso->short_name_len - 2) == 0)
147 goto found; 190 goto found;
@@ -150,7 +193,7 @@ static struct dso *kernel_get_module_dso(const char *module)
150 return NULL; 193 return NULL;
151 } 194 }
152 195
153 map = machine.vmlinux_maps[MAP__FUNCTION]; 196 map = host_machine->vmlinux_maps[MAP__FUNCTION];
154 dso = map->dso; 197 dso = map->dso;
155 198
156 vmlinux_name = symbol_conf.vmlinux_name; 199 vmlinux_name = symbol_conf.vmlinux_name;
@@ -173,20 +216,6 @@ const char *kernel_get_module_path(const char *module)
173 return (dso) ? dso->long_name : NULL; 216 return (dso) ? dso->long_name : NULL;
174} 217}
175 218
176static int init_user_exec(void)
177{
178 int ret = 0;
179
180 symbol_conf.try_vmlinux_path = false;
181 symbol_conf.sort_by_name = true;
182 ret = symbol__init();
183
184 if (ret < 0)
185 pr_debug("Failed to init symbol map.\n");
186
187 return ret;
188}
189
190static int convert_exec_to_group(const char *exec, char **result) 219static int convert_exec_to_group(const char *exec, char **result)
191{ 220{
192 char *ptr1, *ptr2, *exec_copy; 221 char *ptr1, *ptr2, *exec_copy;
@@ -218,32 +247,23 @@ out:
218 return ret; 247 return ret;
219} 248}
220 249
221static int convert_to_perf_probe_point(struct probe_trace_point *tp, 250static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
222 struct perf_probe_point *pp)
223{ 251{
224 pp->function = strdup(tp->symbol); 252 int i;
225
226 if (pp->function == NULL)
227 return -ENOMEM;
228
229 pp->offset = tp->offset;
230 pp->retprobe = tp->retprobe;
231 253
232 return 0; 254 for (i = 0; i < ntevs; i++)
255 clear_probe_trace_event(tevs + i);
233} 256}
234 257
235#ifdef HAVE_DWARF_SUPPORT 258#ifdef HAVE_DWARF_SUPPORT
259
236/* Open new debuginfo of given module */ 260/* Open new debuginfo of given module */
237static struct debuginfo *open_debuginfo(const char *module) 261static struct debuginfo *open_debuginfo(const char *module)
238{ 262{
239 const char *path; 263 const char *path = module;
240 264
241 /* A file path -- this is an offline module */ 265 if (!module || !strchr(module, '/')) {
242 if (module && strchr(module, '/'))
243 path = module;
244 else {
245 path = kernel_get_module_path(module); 266 path = kernel_get_module_path(module);
246
247 if (!path) { 267 if (!path) {
248 pr_err("Failed to find path of %s module.\n", 268 pr_err("Failed to find path of %s module.\n",
249 module ?: "kernel"); 269 module ?: "kernel");
@@ -253,46 +273,6 @@ static struct debuginfo *open_debuginfo(const char *module)
253 return debuginfo__new(path); 273 return debuginfo__new(path);
254} 274}
255 275
256/*
257 * Convert trace point to probe point with debuginfo
258 * Currently only handles kprobes.
259 */
260static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
261 struct perf_probe_point *pp)
262{
263 struct symbol *sym;
264 struct map *map;
265 u64 addr;
266 int ret = -ENOENT;
267 struct debuginfo *dinfo;
268
269 sym = __find_kernel_function_by_name(tp->symbol, &map);
270 if (sym) {
271 addr = map->unmap_ip(map, sym->start + tp->offset);
272 pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol,
273 tp->offset, addr);
274
275 dinfo = debuginfo__new_online_kernel(addr);
276 if (dinfo) {
277 ret = debuginfo__find_probe_point(dinfo,
278 (unsigned long)addr, pp);
279 debuginfo__delete(dinfo);
280 } else {
281 pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n",
282 addr);
283 ret = -ENOENT;
284 }
285 }
286 if (ret <= 0) {
287 pr_debug("Failed to find corresponding probes from "
288 "debuginfo. Use kprobe event information.\n");
289 return convert_to_perf_probe_point(tp, pp);
290 }
291 pp->retprobe = tp->retprobe;
292
293 return 0;
294}
295
296static int get_text_start_address(const char *exec, unsigned long *address) 276static int get_text_start_address(const char *exec, unsigned long *address)
297{ 277{
298 Elf *elf; 278 Elf *elf;
@@ -321,12 +301,62 @@ out:
321 return ret; 301 return ret;
322} 302}
323 303
304/*
305 * Convert trace point to probe point with debuginfo
306 */
307static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
308 struct perf_probe_point *pp,
309 bool is_kprobe)
310{
311 struct debuginfo *dinfo = NULL;
312 unsigned long stext = 0;
313 u64 addr = tp->address;
314 int ret = -ENOENT;
315
316 /* convert the address to dwarf address */
317 if (!is_kprobe) {
318 if (!addr) {
319 ret = -EINVAL;
320 goto error;
321 }
322 ret = get_text_start_address(tp->module, &stext);
323 if (ret < 0)
324 goto error;
325 addr += stext;
326 } else {
327 addr = kernel_get_symbol_address_by_name(tp->symbol, false);
328 if (addr == 0)
329 goto error;
330 addr += tp->offset;
331 }
332
333 pr_debug("try to find information at %" PRIx64 " in %s\n", addr,
334 tp->module ? : "kernel");
335
336 dinfo = open_debuginfo(tp->module);
337 if (dinfo) {
338 ret = debuginfo__find_probe_point(dinfo,
339 (unsigned long)addr, pp);
340 debuginfo__delete(dinfo);
341 } else {
342 pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr);
343 ret = -ENOENT;
344 }
345
346 if (ret > 0) {
347 pp->retprobe = tp->retprobe;
348 return 0;
349 }
350error:
351 pr_debug("Failed to find corresponding probes from debuginfo.\n");
352 return ret ? : -ENOENT;
353}
354
324static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, 355static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
325 int ntevs, const char *exec) 356 int ntevs, const char *exec)
326{ 357{
327 int i, ret = 0; 358 int i, ret = 0;
328 unsigned long offset, stext = 0; 359 unsigned long stext = 0;
329 char buf[32];
330 360
331 if (!exec) 361 if (!exec)
332 return 0; 362 return 0;
@@ -337,15 +367,9 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
337 367
338 for (i = 0; i < ntevs && ret >= 0; i++) { 368 for (i = 0; i < ntevs && ret >= 0; i++) {
339 /* point.address is the addres of point.symbol + point.offset */ 369 /* point.address is the addres of point.symbol + point.offset */
340 offset = tevs[i].point.address - stext; 370 tevs[i].point.address -= stext;
341 tevs[i].point.offset = 0;
342 zfree(&tevs[i].point.symbol);
343 ret = e_snprintf(buf, 32, "0x%lx", offset);
344 if (ret < 0)
345 break;
346 tevs[i].point.module = strdup(exec); 371 tevs[i].point.module = strdup(exec);
347 tevs[i].point.symbol = strdup(buf); 372 if (!tevs[i].point.module) {
348 if (!tevs[i].point.symbol || !tevs[i].point.module) {
349 ret = -ENOMEM; 373 ret = -ENOMEM;
350 break; 374 break;
351 } 375 }
@@ -388,12 +412,40 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
388 return ret; 412 return ret;
389} 413}
390 414
391static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) 415/* Post processing the probe events */
416static int post_process_probe_trace_events(struct probe_trace_event *tevs,
417 int ntevs, const char *module,
418 bool uprobe)
392{ 419{
420 struct ref_reloc_sym *reloc_sym;
421 char *tmp;
393 int i; 422 int i;
394 423
395 for (i = 0; i < ntevs; i++) 424 if (uprobe)
396 clear_probe_trace_event(tevs + i); 425 return add_exec_to_probe_trace_events(tevs, ntevs, module);
426
427 /* Note that currently ref_reloc_sym based probe is not for drivers */
428 if (module)
429 return add_module_to_probe_trace_events(tevs, ntevs, module);
430
431 reloc_sym = kernel_get_ref_reloc_sym();
432 if (!reloc_sym) {
433 pr_warning("Relocated base symbol is not found!\n");
434 return -EINVAL;
435 }
436
437 for (i = 0; i < ntevs; i++) {
438 if (tevs[i].point.address) {
439 tmp = strdup(reloc_sym->name);
440 if (!tmp)
441 return -ENOMEM;
442 free(tevs[i].point.symbol);
443 tevs[i].point.symbol = tmp;
444 tevs[i].point.offset = tevs[i].point.address -
445 reloc_sym->unrelocated_addr;
446 }
447 }
448 return 0;
397} 449}
398 450
399/* Try to find perf_probe_event with debuginfo */ 451/* Try to find perf_probe_event with debuginfo */
@@ -416,21 +468,16 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
416 return 0; 468 return 0;
417 } 469 }
418 470
471 pr_debug("Try to find probe point from debuginfo.\n");
419 /* Searching trace events corresponding to a probe event */ 472 /* Searching trace events corresponding to a probe event */
420 ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); 473 ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
421 474
422 debuginfo__delete(dinfo); 475 debuginfo__delete(dinfo);
423 476
424 if (ntevs > 0) { /* Succeeded to find trace events */ 477 if (ntevs > 0) { /* Succeeded to find trace events */
425 pr_debug("find %d probe_trace_events.\n", ntevs); 478 pr_debug("Found %d probe_trace_events.\n", ntevs);
426 if (target) { 479 ret = post_process_probe_trace_events(*tevs, ntevs,
427 if (pev->uprobes) 480 target, pev->uprobes);
428 ret = add_exec_to_probe_trace_events(*tevs,
429 ntevs, target);
430 else
431 ret = add_module_to_probe_trace_events(*tevs,
432 ntevs, target);
433 }
434 if (ret < 0) { 481 if (ret < 0) {
435 clear_probe_trace_events(*tevs, ntevs); 482 clear_probe_trace_events(*tevs, ntevs);
436 zfree(tevs); 483 zfree(tevs);
@@ -563,20 +610,16 @@ static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)
563 * Show line-range always requires debuginfo to find source file and 610 * Show line-range always requires debuginfo to find source file and
564 * line number. 611 * line number.
565 */ 612 */
566int show_line_range(struct line_range *lr, const char *module) 613static int __show_line_range(struct line_range *lr, const char *module)
567{ 614{
568 int l = 1; 615 int l = 1;
569 struct line_node *ln; 616 struct int_node *ln;
570 struct debuginfo *dinfo; 617 struct debuginfo *dinfo;
571 FILE *fp; 618 FILE *fp;
572 int ret; 619 int ret;
573 char *tmp; 620 char *tmp;
574 621
575 /* Search a line range */ 622 /* Search a line range */
576 ret = init_vmlinux();
577 if (ret < 0)
578 return ret;
579
580 dinfo = open_debuginfo(module); 623 dinfo = open_debuginfo(module);
581 if (!dinfo) { 624 if (!dinfo) {
582 pr_warning("Failed to open debuginfo file.\n"); 625 pr_warning("Failed to open debuginfo file.\n");
@@ -623,8 +666,8 @@ int show_line_range(struct line_range *lr, const char *module)
623 goto end; 666 goto end;
624 } 667 }
625 668
626 list_for_each_entry(ln, &lr->line_list, list) { 669 intlist__for_each(ln, lr->line_list) {
627 for (; ln->line > l; l++) { 670 for (; ln->i > l; l++) {
628 ret = show_one_line(fp, l - lr->offset); 671 ret = show_one_line(fp, l - lr->offset);
629 if (ret < 0) 672 if (ret < 0)
630 goto end; 673 goto end;
@@ -646,6 +689,19 @@ end:
646 return ret; 689 return ret;
647} 690}
648 691
692int show_line_range(struct line_range *lr, const char *module)
693{
694 int ret;
695
696 ret = init_symbol_maps(false);
697 if (ret < 0)
698 return ret;
699 ret = __show_line_range(lr, module);
700 exit_symbol_maps();
701
702 return ret;
703}
704
649static int show_available_vars_at(struct debuginfo *dinfo, 705static int show_available_vars_at(struct debuginfo *dinfo,
650 struct perf_probe_event *pev, 706 struct perf_probe_event *pev,
651 int max_vls, struct strfilter *_filter, 707 int max_vls, struct strfilter *_filter,
@@ -707,14 +763,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
707 int i, ret = 0; 763 int i, ret = 0;
708 struct debuginfo *dinfo; 764 struct debuginfo *dinfo;
709 765
710 ret = init_vmlinux(); 766 ret = init_symbol_maps(false);
711 if (ret < 0) 767 if (ret < 0)
712 return ret; 768 return ret;
713 769
714 dinfo = open_debuginfo(module); 770 dinfo = open_debuginfo(module);
715 if (!dinfo) { 771 if (!dinfo) {
716 pr_warning("Failed to open debuginfo file.\n"); 772 pr_warning("Failed to open debuginfo file.\n");
717 return -ENOENT; 773 ret = -ENOENT;
774 goto out;
718 } 775 }
719 776
720 setup_pager(); 777 setup_pager();
@@ -724,23 +781,19 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
724 externs); 781 externs);
725 782
726 debuginfo__delete(dinfo); 783 debuginfo__delete(dinfo);
784out:
785 exit_symbol_maps();
727 return ret; 786 return ret;
728} 787}
729 788
730#else /* !HAVE_DWARF_SUPPORT */ 789#else /* !HAVE_DWARF_SUPPORT */
731 790
732static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 791static int
733 struct perf_probe_point *pp) 792find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
793 struct perf_probe_point *pp __maybe_unused,
794 bool is_kprobe __maybe_unused)
734{ 795{
735 struct symbol *sym; 796 return -ENOSYS;
736
737 sym = __find_kernel_function_by_name(tp->symbol, NULL);
738 if (!sym) {
739 pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
740 return -ENOENT;
741 }
742
743 return convert_to_perf_probe_point(tp, pp);
744} 797}
745 798
746static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 799static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
@@ -776,24 +829,22 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
776 829
777void line_range__clear(struct line_range *lr) 830void line_range__clear(struct line_range *lr)
778{ 831{
779 struct line_node *ln;
780
781 free(lr->function); 832 free(lr->function);
782 free(lr->file); 833 free(lr->file);
783 free(lr->path); 834 free(lr->path);
784 free(lr->comp_dir); 835 free(lr->comp_dir);
785 while (!list_empty(&lr->line_list)) { 836 intlist__delete(lr->line_list);
786 ln = list_first_entry(&lr->line_list, struct line_node, list);
787 list_del(&ln->list);
788 free(ln);
789 }
790 memset(lr, 0, sizeof(*lr)); 837 memset(lr, 0, sizeof(*lr));
791} 838}
792 839
793void line_range__init(struct line_range *lr) 840int line_range__init(struct line_range *lr)
794{ 841{
795 memset(lr, 0, sizeof(*lr)); 842 memset(lr, 0, sizeof(*lr));
796 INIT_LIST_HEAD(&lr->line_list); 843 lr->line_list = intlist__new(NULL);
844 if (!lr->line_list)
845 return -ENOMEM;
846 else
847 return 0;
797} 848}
798 849
799static int parse_line_num(char **ptr, int *val, const char *what) 850static int parse_line_num(char **ptr, int *val, const char *what)
@@ -1267,16 +1318,21 @@ static int parse_probe_trace_command(const char *cmd,
1267 } else 1318 } else
1268 p = argv[1]; 1319 p = argv[1];
1269 fmt1_str = strtok_r(p, "+", &fmt); 1320 fmt1_str = strtok_r(p, "+", &fmt);
1270 tp->symbol = strdup(fmt1_str); 1321 if (fmt1_str[0] == '0') /* only the address started with 0x */
1271 if (tp->symbol == NULL) { 1322 tp->address = strtoul(fmt1_str, NULL, 0);
1272 ret = -ENOMEM; 1323 else {
1273 goto out; 1324 /* Only the symbol-based probe has offset */
1325 tp->symbol = strdup(fmt1_str);
1326 if (tp->symbol == NULL) {
1327 ret = -ENOMEM;
1328 goto out;
1329 }
1330 fmt2_str = strtok_r(NULL, "", &fmt);
1331 if (fmt2_str == NULL)
1332 tp->offset = 0;
1333 else
1334 tp->offset = strtoul(fmt2_str, NULL, 10);
1274 } 1335 }
1275 fmt2_str = strtok_r(NULL, "", &fmt);
1276 if (fmt2_str == NULL)
1277 tp->offset = 0;
1278 else
1279 tp->offset = strtoul(fmt2_str, NULL, 10);
1280 1336
1281 tev->nargs = argc - 2; 1337 tev->nargs = argc - 2;
1282 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); 1338 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
@@ -1518,20 +1574,27 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
1518 if (buf == NULL) 1574 if (buf == NULL)
1519 return NULL; 1575 return NULL;
1520 1576
1577 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p',
1578 tev->group, tev->event);
1579 if (len <= 0)
1580 goto error;
1581
1582 /* Uprobes must have tp->address and tp->module */
1583 if (tev->uprobes && (!tp->address || !tp->module))
1584 goto error;
1585
1586 /* Use the tp->address for uprobes */
1521 if (tev->uprobes) 1587 if (tev->uprobes)
1522 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s", 1588 ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
1523 tp->retprobe ? 'r' : 'p', 1589 tp->module, tp->address);
1524 tev->group, tev->event,
1525 tp->module, tp->symbol);
1526 else 1590 else
1527 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", 1591 ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",
1528 tp->retprobe ? 'r' : 'p',
1529 tev->group, tev->event,
1530 tp->module ?: "", tp->module ? ":" : "", 1592 tp->module ?: "", tp->module ? ":" : "",
1531 tp->symbol, tp->offset); 1593 tp->symbol, tp->offset);
1532 1594
1533 if (len <= 0) 1595 if (ret <= 0)
1534 goto error; 1596 goto error;
1597 len += ret;
1535 1598
1536 for (i = 0; i < tev->nargs; i++) { 1599 for (i = 0; i < tev->nargs; i++) {
1537 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, 1600 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
@@ -1547,6 +1610,79 @@ error:
1547 return NULL; 1610 return NULL;
1548} 1611}
1549 1612
1613static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
1614 struct perf_probe_point *pp,
1615 bool is_kprobe)
1616{
1617 struct symbol *sym = NULL;
1618 struct map *map;
1619 u64 addr;
1620 int ret = -ENOENT;
1621
1622 if (!is_kprobe) {
1623 map = dso__new_map(tp->module);
1624 if (!map)
1625 goto out;
1626 addr = tp->address;
1627 sym = map__find_symbol(map, addr, NULL);
1628 } else {
1629 addr = kernel_get_symbol_address_by_name(tp->symbol, true);
1630 if (addr) {
1631 addr += tp->offset;
1632 sym = __find_kernel_function(addr, &map);
1633 }
1634 }
1635 if (!sym)
1636 goto out;
1637
1638 pp->retprobe = tp->retprobe;
1639 pp->offset = addr - map->unmap_ip(map, sym->start);
1640 pp->function = strdup(sym->name);
1641 ret = pp->function ? 0 : -ENOMEM;
1642
1643out:
1644 if (map && !is_kprobe) {
1645 dso__delete(map->dso);
1646 map__delete(map);
1647 }
1648
1649 return ret;
1650}
1651
1652static int convert_to_perf_probe_point(struct probe_trace_point *tp,
1653 struct perf_probe_point *pp,
1654 bool is_kprobe)
1655{
1656 char buf[128];
1657 int ret;
1658
1659 ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe);
1660 if (!ret)
1661 return 0;
1662 ret = find_perf_probe_point_from_map(tp, pp, is_kprobe);
1663 if (!ret)
1664 return 0;
1665
1666 pr_debug("Failed to find probe point from both of dwarf and map.\n");
1667
1668 if (tp->symbol) {
1669 pp->function = strdup(tp->symbol);
1670 pp->offset = tp->offset;
1671 } else if (!tp->module && !is_kprobe) {
1672 ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address);
1673 if (ret < 0)
1674 return ret;
1675 pp->function = strdup(buf);
1676 pp->offset = 0;
1677 }
1678 if (pp->function == NULL)
1679 return -ENOMEM;
1680
1681 pp->retprobe = tp->retprobe;
1682
1683 return 0;
1684}
1685
1550static int convert_to_perf_probe_event(struct probe_trace_event *tev, 1686static int convert_to_perf_probe_event(struct probe_trace_event *tev,
1551 struct perf_probe_event *pev, bool is_kprobe) 1687 struct perf_probe_event *pev, bool is_kprobe)
1552{ 1688{
@@ -1560,11 +1696,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,
1560 return -ENOMEM; 1696 return -ENOMEM;
1561 1697
1562 /* Convert trace_point to probe_point */ 1698 /* Convert trace_point to probe_point */
1563 if (is_kprobe) 1699 ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe);
1564 ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
1565 else
1566 ret = convert_to_perf_probe_point(&tev->point, &pev->point);
1567
1568 if (ret < 0) 1700 if (ret < 0)
1569 return ret; 1701 return ret;
1570 1702
@@ -1731,7 +1863,8 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)
1731} 1863}
1732 1864
1733/* Show an event */ 1865/* Show an event */
1734static int show_perf_probe_event(struct perf_probe_event *pev) 1866static int show_perf_probe_event(struct perf_probe_event *pev,
1867 const char *module)
1735{ 1868{
1736 int i, ret; 1869 int i, ret;
1737 char buf[128]; 1870 char buf[128];
@@ -1747,6 +1880,8 @@ static int show_perf_probe_event(struct perf_probe_event *pev)
1747 return ret; 1880 return ret;
1748 1881
1749 printf(" %-20s (on %s", buf, place); 1882 printf(" %-20s (on %s", buf, place);
1883 if (module)
1884 printf(" in %s", module);
1750 1885
1751 if (pev->nargs > 0) { 1886 if (pev->nargs > 0) {
1752 printf(" with"); 1887 printf(" with");
@@ -1784,7 +1919,8 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)
1784 ret = convert_to_perf_probe_event(&tev, &pev, 1919 ret = convert_to_perf_probe_event(&tev, &pev,
1785 is_kprobe); 1920 is_kprobe);
1786 if (ret >= 0) 1921 if (ret >= 0)
1787 ret = show_perf_probe_event(&pev); 1922 ret = show_perf_probe_event(&pev,
1923 tev.point.module);
1788 } 1924 }
1789 clear_perf_probe_event(&pev); 1925 clear_perf_probe_event(&pev);
1790 clear_probe_trace_event(&tev); 1926 clear_probe_trace_event(&tev);
@@ -1807,7 +1943,7 @@ int show_perf_probe_events(void)
1807 if (fd < 0) 1943 if (fd < 0)
1808 return fd; 1944 return fd;
1809 1945
1810 ret = init_vmlinux(); 1946 ret = init_symbol_maps(false);
1811 if (ret < 0) 1947 if (ret < 0)
1812 return ret; 1948 return ret;
1813 1949
@@ -1820,6 +1956,7 @@ int show_perf_probe_events(void)
1820 close(fd); 1956 close(fd);
1821 } 1957 }
1822 1958
1959 exit_symbol_maps();
1823 return ret; 1960 return ret;
1824} 1961}
1825 1962
@@ -1982,7 +2119,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
1982 group = pev->group; 2119 group = pev->group;
1983 pev->event = tev->event; 2120 pev->event = tev->event;
1984 pev->group = tev->group; 2121 pev->group = tev->group;
1985 show_perf_probe_event(pev); 2122 show_perf_probe_event(pev, tev->point.module);
1986 /* Trick here - restore current event/group */ 2123 /* Trick here - restore current event/group */
1987 pev->event = (char *)event; 2124 pev->event = (char *)event;
1988 pev->group = (char *)group; 2125 pev->group = (char *)group;
@@ -2008,113 +2145,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
2008 return ret; 2145 return ret;
2009} 2146}
2010 2147
2011static int convert_to_probe_trace_events(struct perf_probe_event *pev, 2148static char *looking_function_name;
2012 struct probe_trace_event **tevs, 2149static int num_matched_functions;
2013 int max_tevs, const char *target) 2150
2151static int probe_function_filter(struct map *map __maybe_unused,
2152 struct symbol *sym)
2014{ 2153{
2154 if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
2155 strcmp(looking_function_name, sym->name) == 0) {
2156 num_matched_functions++;
2157 return 0;
2158 }
2159 return 1;
2160}
2161
2162#define strdup_or_goto(str, label) \
2163 ({ char *__p = strdup(str); if (!__p) goto label; __p; })
2164
2165/*
2166 * Find probe function addresses from map.
2167 * Return an error or the number of found probe_trace_event
2168 */
2169static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
2170 struct probe_trace_event **tevs,
2171 int max_tevs, const char *target)
2172{
2173 struct map *map = NULL;
2174 struct kmap *kmap = NULL;
2175 struct ref_reloc_sym *reloc_sym = NULL;
2015 struct symbol *sym; 2176 struct symbol *sym;
2016 int ret, i; 2177 struct rb_node *nd;
2017 struct probe_trace_event *tev; 2178 struct probe_trace_event *tev;
2179 struct perf_probe_point *pp = &pev->point;
2180 struct probe_trace_point *tp;
2181 int ret, i;
2018 2182
2019 if (pev->uprobes && !pev->group) { 2183 /* Init maps of given executable or kernel */
2020 /* Replace group name if not given */ 2184 if (pev->uprobes)
2021 ret = convert_exec_to_group(target, &pev->group); 2185 map = dso__new_map(target);
2022 if (ret != 0) { 2186 else
2023 pr_warning("Failed to make a group name.\n"); 2187 map = kernel_get_module_map(target);
2024 return ret; 2188 if (!map) {
2025 } 2189 ret = -EINVAL;
2190 goto out;
2026 } 2191 }
2027 2192
2028 /* Convert perf_probe_event with debuginfo */ 2193 /*
2029 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); 2194 * Load matched symbols: Since the different local symbols may have
2030 if (ret != 0) 2195 * same name but different addresses, this lists all the symbols.
2031 return ret; /* Found in debuginfo or got an error */ 2196 */
2032 2197 num_matched_functions = 0;
2033 if (pev->uprobes) { 2198 looking_function_name = pp->function;
2034 ret = convert_name_to_addr(pev, target); 2199 ret = map__load(map, probe_function_filter);
2035 if (ret < 0) 2200 if (ret || num_matched_functions == 0) {
2036 return ret; 2201 pr_err("Failed to find symbol %s in %s\n", pp->function,
2202 target ? : "kernel");
2203 ret = -ENOENT;
2204 goto out;
2205 } else if (num_matched_functions > max_tevs) {
2206 pr_err("Too many functions matched in %s\n",
2207 target ? : "kernel");
2208 ret = -E2BIG;
2209 goto out;
2037 } 2210 }
2038 2211
2039 /* Allocate trace event buffer */ 2212 if (!pev->uprobes) {
2040 tev = *tevs = zalloc(sizeof(struct probe_trace_event)); 2213 kmap = map__kmap(map);
2041 if (tev == NULL) 2214 reloc_sym = kmap->ref_reloc_sym;
2042 return -ENOMEM; 2215 if (!reloc_sym) {
2216 pr_warning("Relocated base symbol is not found!\n");
2217 ret = -EINVAL;
2218 goto out;
2219 }
2220 }
2043 2221
2044 /* Copy parameters */ 2222 /* Setup result trace-probe-events */
2045 tev->point.symbol = strdup(pev->point.function); 2223 *tevs = zalloc(sizeof(*tev) * num_matched_functions);
2046 if (tev->point.symbol == NULL) { 2224 if (!*tevs) {
2047 ret = -ENOMEM; 2225 ret = -ENOMEM;
2048 goto error; 2226 goto out;
2049 } 2227 }
2050 2228
2051 if (target) { 2229 ret = 0;
2052 tev->point.module = strdup(target); 2230 map__for_each_symbol(map, sym, nd) {
2053 if (tev->point.module == NULL) { 2231 tev = (*tevs) + ret;
2054 ret = -ENOMEM; 2232 tp = &tev->point;
2055 goto error; 2233 if (ret == num_matched_functions) {
2234 pr_warning("Too many symbols are listed. Skip it.\n");
2235 break;
2056 } 2236 }
2057 } 2237 ret++;
2058
2059 tev->point.offset = pev->point.offset;
2060 tev->point.retprobe = pev->point.retprobe;
2061 tev->nargs = pev->nargs;
2062 tev->uprobes = pev->uprobes;
2063 2238
2064 if (tev->nargs) { 2239 if (pp->offset > sym->end - sym->start) {
2065 tev->args = zalloc(sizeof(struct probe_trace_arg) 2240 pr_warning("Offset %ld is bigger than the size of %s\n",
2066 * tev->nargs); 2241 pp->offset, sym->name);
2067 if (tev->args == NULL) { 2242 ret = -ENOENT;
2068 ret = -ENOMEM; 2243 goto err_out;
2069 goto error; 2244 }
2245 /* Add one probe point */
2246 tp->address = map->unmap_ip(map, sym->start) + pp->offset;
2247 if (reloc_sym) {
2248 tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
2249 tp->offset = tp->address - reloc_sym->addr;
2250 } else {
2251 tp->symbol = strdup_or_goto(sym->name, nomem_out);
2252 tp->offset = pp->offset;
2253 }
2254 tp->retprobe = pp->retprobe;
2255 if (target)
2256 tev->point.module = strdup_or_goto(target, nomem_out);
2257 tev->uprobes = pev->uprobes;
2258 tev->nargs = pev->nargs;
2259 if (tev->nargs) {
2260 tev->args = zalloc(sizeof(struct probe_trace_arg) *
2261 tev->nargs);
2262 if (tev->args == NULL)
2263 goto nomem_out;
2070 } 2264 }
2071 for (i = 0; i < tev->nargs; i++) { 2265 for (i = 0; i < tev->nargs; i++) {
2072 if (pev->args[i].name) { 2266 if (pev->args[i].name)
2073 tev->args[i].name = strdup(pev->args[i].name); 2267 tev->args[i].name =
2074 if (tev->args[i].name == NULL) { 2268 strdup_or_goto(pev->args[i].name,
2075 ret = -ENOMEM; 2269 nomem_out);
2076 goto error; 2270
2077 } 2271 tev->args[i].value = strdup_or_goto(pev->args[i].var,
2078 } 2272 nomem_out);
2079 tev->args[i].value = strdup(pev->args[i].var); 2273 if (pev->args[i].type)
2080 if (tev->args[i].value == NULL) { 2274 tev->args[i].type =
2081 ret = -ENOMEM; 2275 strdup_or_goto(pev->args[i].type,
2082 goto error; 2276 nomem_out);
2083 }
2084 if (pev->args[i].type) {
2085 tev->args[i].type = strdup(pev->args[i].type);
2086 if (tev->args[i].type == NULL) {
2087 ret = -ENOMEM;
2088 goto error;
2089 }
2090 }
2091 } 2277 }
2092 } 2278 }
2093 2279
2094 if (pev->uprobes) 2280out:
2095 return 1; 2281 if (map && pev->uprobes) {
2282 /* Only when using uprobe(exec) map needs to be released */
2283 dso__delete(map->dso);
2284 map__delete(map);
2285 }
2286 return ret;
2096 2287
2097 /* Currently just checking function name from symbol map */ 2288nomem_out:
2098 sym = __find_kernel_function_by_name(tev->point.symbol, NULL); 2289 ret = -ENOMEM;
2099 if (!sym) { 2290err_out:
2100 pr_warning("Kernel symbol \'%s\' not found.\n", 2291 clear_probe_trace_events(*tevs, num_matched_functions);
2101 tev->point.symbol); 2292 zfree(tevs);
2102 ret = -ENOENT; 2293 goto out;
2103 goto error; 2294}
2104 } else if (tev->point.offset > sym->end - sym->start) { 2295
2105 pr_warning("Offset specified is greater than size of %s\n", 2296static int convert_to_probe_trace_events(struct perf_probe_event *pev,
2106 tev->point.symbol); 2297 struct probe_trace_event **tevs,
2107 ret = -ENOENT; 2298 int max_tevs, const char *target)
2108 goto error; 2299{
2300 int ret;
2109 2301
2302 if (pev->uprobes && !pev->group) {
2303 /* Replace group name if not given */
2304 ret = convert_exec_to_group(target, &pev->group);
2305 if (ret != 0) {
2306 pr_warning("Failed to make a group name.\n");
2307 return ret;
2308 }
2110 } 2309 }
2111 2310
2112 return 1; 2311 /* Convert perf_probe_event with debuginfo */
2113error: 2312 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
2114 clear_probe_trace_event(tev); 2313 if (ret != 0)
2115 free(tev); 2314 return ret; /* Found in debuginfo or got an error */
2116 *tevs = NULL; 2315
2117 return ret; 2316 return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
2118} 2317}
2119 2318
2120struct __event_package { 2319struct __event_package {
@@ -2135,12 +2334,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
2135 if (pkgs == NULL) 2334 if (pkgs == NULL)
2136 return -ENOMEM; 2335 return -ENOMEM;
2137 2336
2138 if (!pevs->uprobes) 2337 ret = init_symbol_maps(pevs->uprobes);
2139 /* Init vmlinux path */
2140 ret = init_vmlinux();
2141 else
2142 ret = init_user_exec();
2143
2144 if (ret < 0) { 2338 if (ret < 0) {
2145 free(pkgs); 2339 free(pkgs);
2146 return ret; 2340 return ret;
@@ -2174,6 +2368,7 @@ end:
2174 zfree(&pkgs[i].tevs); 2368 zfree(&pkgs[i].tevs);
2175 } 2369 }
2176 free(pkgs); 2370 free(pkgs);
2371 exit_symbol_maps();
2177 2372
2178 return ret; 2373 return ret;
2179} 2374}
@@ -2323,159 +2518,51 @@ static struct strfilter *available_func_filter;
2323static int filter_available_functions(struct map *map __maybe_unused, 2518static int filter_available_functions(struct map *map __maybe_unused,
2324 struct symbol *sym) 2519 struct symbol *sym)
2325{ 2520{
2326 if (sym->binding == STB_GLOBAL && 2521 if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
2327 strfilter__compare(available_func_filter, sym->name)) 2522 strfilter__compare(available_func_filter, sym->name))
2328 return 0; 2523 return 0;
2329 return 1; 2524 return 1;
2330} 2525}
2331 2526
2332static int __show_available_funcs(struct map *map) 2527int show_available_funcs(const char *target, struct strfilter *_filter,
2333{ 2528 bool user)
2334 if (map__load(map, filter_available_functions)) {
2335 pr_err("Failed to load map.\n");
2336 return -EINVAL;
2337 }
2338 if (!dso__sorted_by_name(map->dso, map->type))
2339 dso__sort_by_name(map->dso, map->type);
2340
2341 dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
2342 return 0;
2343}
2344
2345static int available_kernel_funcs(const char *module)
2346{ 2529{
2347 struct map *map; 2530 struct map *map;
2348 int ret; 2531 int ret;
2349 2532
2350 ret = init_vmlinux(); 2533 ret = init_symbol_maps(user);
2351 if (ret < 0) 2534 if (ret < 0)
2352 return ret; 2535 return ret;
2353 2536
2354 map = kernel_get_module_map(module); 2537 /* Get a symbol map */
2538 if (user)
2539 map = dso__new_map(target);
2540 else
2541 map = kernel_get_module_map(target);
2355 if (!map) { 2542 if (!map) {
2356 pr_err("Failed to find %s map.\n", (module) ? : "kernel"); 2543 pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
2357 return -EINVAL; 2544 return -EINVAL;
2358 } 2545 }
2359 return __show_available_funcs(map);
2360}
2361
2362static int available_user_funcs(const char *target)
2363{
2364 struct map *map;
2365 int ret;
2366
2367 ret = init_user_exec();
2368 if (ret < 0)
2369 return ret;
2370
2371 map = dso__new_map(target);
2372 ret = __show_available_funcs(map);
2373 dso__delete(map->dso);
2374 map__delete(map);
2375 return ret;
2376}
2377 2546
2378int show_available_funcs(const char *target, struct strfilter *_filter, 2547 /* Load symbols with given filter */
2379 bool user)
2380{
2381 setup_pager();
2382 available_func_filter = _filter; 2548 available_func_filter = _filter;
2383
2384 if (!user)
2385 return available_kernel_funcs(target);
2386
2387 return available_user_funcs(target);
2388}
2389
2390/*
2391 * uprobe_events only accepts address:
2392 * Convert function and any offset to address
2393 */
2394static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
2395{
2396 struct perf_probe_point *pp = &pev->point;
2397 struct symbol *sym;
2398 struct map *map = NULL;
2399 char *function = NULL;
2400 int ret = -EINVAL;
2401 unsigned long long vaddr = 0;
2402
2403 if (!pp->function) {
2404 pr_warning("No function specified for uprobes");
2405 goto out;
2406 }
2407
2408 function = strdup(pp->function);
2409 if (!function) {
2410 pr_warning("Failed to allocate memory by strdup.\n");
2411 ret = -ENOMEM;
2412 goto out;
2413 }
2414
2415 map = dso__new_map(exec);
2416 if (!map) {
2417 pr_warning("Cannot find appropriate DSO for %s.\n", exec);
2418 goto out;
2419 }
2420 available_func_filter = strfilter__new(function, NULL);
2421 if (map__load(map, filter_available_functions)) { 2549 if (map__load(map, filter_available_functions)) {
2422 pr_err("Failed to load map.\n"); 2550 pr_err("Failed to load symbols in %s\n", (target) ? : "kernel");
2423 goto out; 2551 goto end;
2424 }
2425
2426 sym = map__find_symbol_by_name(map, function, NULL);
2427 if (!sym) {
2428 pr_warning("Cannot find %s in DSO %s\n", function, exec);
2429 goto out;
2430 }
2431
2432 if (map->start > sym->start)
2433 vaddr = map->start;
2434 vaddr += sym->start + pp->offset + map->pgoff;
2435 pp->offset = 0;
2436
2437 if (!pev->event) {
2438 pev->event = function;
2439 function = NULL;
2440 }
2441 if (!pev->group) {
2442 char *ptr1, *ptr2, *exec_copy;
2443
2444 pev->group = zalloc(sizeof(char *) * 64);
2445 exec_copy = strdup(exec);
2446 if (!exec_copy) {
2447 ret = -ENOMEM;
2448 pr_warning("Failed to copy exec string.\n");
2449 goto out;
2450 }
2451
2452 ptr1 = strdup(basename(exec_copy));
2453 if (ptr1) {
2454 ptr2 = strpbrk(ptr1, "-._");
2455 if (ptr2)
2456 *ptr2 = '\0';
2457 e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
2458 ptr1);
2459 free(ptr1);
2460 }
2461 free(exec_copy);
2462 }
2463 free(pp->function);
2464 pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
2465 if (!pp->function) {
2466 ret = -ENOMEM;
2467 pr_warning("Failed to allocate memory by zalloc.\n");
2468 goto out;
2469 } 2552 }
2470 e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr); 2553 if (!dso__sorted_by_name(map->dso, map->type))
2471 ret = 0; 2554 dso__sort_by_name(map->dso, map->type);
2472 2555
2473out: 2556 /* Show all (filtered) symbols */
2474 if (map) { 2557 setup_pager();
2558 dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
2559end:
2560 if (user) {
2475 dso__delete(map->dso); 2561 dso__delete(map->dso);
2476 map__delete(map); 2562 map__delete(map);
2477 } 2563 }
2478 if (function) 2564 exit_symbol_maps();
2479 free(function); 2565
2480 return ret; 2566 return ret;
2481} 2567}
2568