aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>2014-02-06 00:32:25 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2014-02-18 07:38:43 -0500
commiteb948e50831bc64e6bb2589be7575ed7c159a429 (patch)
tree7f33996d921380f5393f1c8c72258ae448f7bf83
parent5a6f63145491f905de1c5c6c46c5cd62c004d0d1 (diff)
perf probe: Allow to add events on the local functions
Allow to add events on the local functions without debuginfo. (With the debuginfo, we can add events even on inlined functions) Currently, probing on local functions requires debuginfo to locate actual address. It is also possible without debuginfo since we have symbol maps. Without this change; ---- # ./perf probe -a t_show Added new event: probe:t_show (on t_show) You can now use it in all perf tools, such as: perf record -e probe:t_show -aR sleep 1 # ./perf probe -x perf -a identity__map_ip no symbols found in /kbuild/ksrc/linux-3/tools/perf/perf, maybe install a debug package? Failed to load map. Error: Failed to add events. (-22) ---- As the above results, perf probe just put one event on the first found symbol for kprobe event. Moreover, for uprobe event, perf probe failed to find local functions. With this change; ---- # ./perf probe -a t_show Added new events: probe:t_show (on t_show) probe:t_show_1 (on t_show) probe:t_show_2 (on t_show) probe:t_show_3 (on t_show) You can now use it in all perf tools, such as: perf record -e probe:t_show_3 -aR sleep 1 # ./perf probe -x perf -a identity__map_ip Added new events: probe_perf:identity__map_ip (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf) probe_perf:identity__map_ip_1 (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf) probe_perf:identity__map_ip_2 (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf) probe_perf:identity__map_ip_3 (on identity__map_ip in /kbuild/ksrc/linux-3/tools/perf/perf) You can now use it in all perf tools, such as: perf record -e probe_perf:identity__map_ip_3 -aR sleep 1 ---- Now we succeed to put events on every given local functions for both kprobes and uprobes. :) Note that this also introduces some symbol rbtree iteration macros; symbols__for_each, dso__for_each_symbol, and map__for_each_symbol. These are for walking through the symbol list in a map. Changes from v2: - Fix add_exec_to_probe_trace_events() not to convert address to tp->symbol any more. - Fix to set kernel probes based on ref_reloc_sym. Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: David Ahern <dsahern@gmail.com> Cc: "David A. Long" <dave.long@linaro.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20140206053225.29635.15026.stgit@kbuild-fedora.yrl.intra.hitachi.co.jp Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/util/dso.h10
-rw-r--r--tools/perf/util/map.h10
-rw-r--r--tools/perf/util/probe-event.c378
-rw-r--r--tools/perf/util/symbol.h11
4 files changed, 204 insertions, 205 deletions
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index cd7d6f078cdd..ab06f1c03655 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -102,6 +102,16 @@ struct dso {
102 char name[0]; 102 char name[0];
103}; 103};
104 104
105/* dso__for_each_symbol - iterate over the symbols of given type
106 *
107 * @dso: the 'struct dso *' in which symbols itereated
108 * @pos: the 'struct symbol *' to use as a loop cursor
109 * @n: the 'struct rb_node *' to use as a temporary storage
110 * @type: the 'enum map_type' type of symbols
111 */
112#define dso__for_each_symbol(dso, pos, n, type) \
113 symbols__for_each_entry(&(dso)->symbols[(type)], pos, n)
114
105static inline void dso__set_loaded(struct dso *dso, enum map_type type) 115static inline void dso__set_loaded(struct dso *dso, enum map_type type)
106{ 116{
107 dso->loaded |= (1 << type); 117 dso->loaded |= (1 << type);
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 257e513205ce..f00f058afb3b 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -90,6 +90,16 @@ u64 map__objdump_2mem(struct map *map, u64 ip);
90 90
91struct symbol; 91struct symbol;
92 92
93/* map__for_each_symbol - iterate over the symbols in the given map
94 *
95 * @map: the 'struct map *' in which symbols itereated
96 * @pos: the 'struct symbol *' to use as a loop cursor
97 * @n: the 'struct rb_node *' to use as a temporary storage
98 * Note: caller must ensure map->dso is not NULL (map is loaded).
99 */
100#define map__for_each_symbol(map, pos, n) \
101 dso__for_each_symbol(map->dso, pos, n, map->type)
102
93typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); 103typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
94 104
95void map__init(struct map *map, enum map_type type, 105void map__init(struct map *map, enum map_type type,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 3c35b7af2adb..42bec67aaa38 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -70,8 +70,6 @@ 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 *host_machine; 74static struct machine *host_machine;
77 75
@@ -249,6 +247,14 @@ out:
249 return ret; 247 return ret;
250} 248}
251 249
250static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
251{
252 int i;
253
254 for (i = 0; i < ntevs; i++)
255 clear_probe_trace_event(tevs + i);
256}
257
252#ifdef HAVE_DWARF_SUPPORT 258#ifdef HAVE_DWARF_SUPPORT
253/* Open new debuginfo of given module */ 259/* Open new debuginfo of given module */
254static struct debuginfo *open_debuginfo(const char *module) 260static struct debuginfo *open_debuginfo(const char *module)
@@ -353,8 +359,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
353 int ntevs, const char *exec) 359 int ntevs, const char *exec)
354{ 360{
355 int i, ret = 0; 361 int i, ret = 0;
356 unsigned long offset, stext = 0; 362 unsigned long stext = 0;
357 char buf[32];
358 363
359 if (!exec) 364 if (!exec)
360 return 0; 365 return 0;
@@ -365,15 +370,9 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
365 370
366 for (i = 0; i < ntevs && ret >= 0; i++) { 371 for (i = 0; i < ntevs && ret >= 0; i++) {
367 /* point.address is the addres of point.symbol + point.offset */ 372 /* point.address is the addres of point.symbol + point.offset */
368 offset = tevs[i].point.address - stext; 373 tevs[i].point.address -= stext;
369 tevs[i].point.offset = 0;
370 zfree(&tevs[i].point.symbol);
371 ret = e_snprintf(buf, 32, "0x%lx", offset);
372 if (ret < 0)
373 break;
374 tevs[i].point.module = strdup(exec); 374 tevs[i].point.module = strdup(exec);
375 tevs[i].point.symbol = strdup(buf); 375 if (!tevs[i].point.module) {
376 if (!tevs[i].point.symbol || !tevs[i].point.module) {
377 ret = -ENOMEM; 376 ret = -ENOMEM;
378 break; 377 break;
379 } 378 }
@@ -452,14 +451,6 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
452 return 0; 451 return 0;
453} 452}
454 453
455static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
456{
457 int i;
458
459 for (i = 0; i < ntevs; i++)
460 clear_probe_trace_event(tevs + i);
461}
462
463/* Try to find perf_probe_event with debuginfo */ 454/* Try to find perf_probe_event with debuginfo */
464static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 455static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
465 struct probe_trace_event **tevs, 456 struct probe_trace_event **tevs,
@@ -1586,20 +1577,27 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
1586 if (buf == NULL) 1577 if (buf == NULL)
1587 return NULL; 1578 return NULL;
1588 1579
1580 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p',
1581 tev->group, tev->event);
1582 if (len <= 0)
1583 goto error;
1584
1585 /* Uprobes must have tp->address and tp->module */
1586 if (tev->uprobes && (!tp->address || !tp->module))
1587 goto error;
1588
1589 /* Use the tp->address for uprobes */
1589 if (tev->uprobes) 1590 if (tev->uprobes)
1590 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s", 1591 ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
1591 tp->retprobe ? 'r' : 'p', 1592 tp->module, tp->address);
1592 tev->group, tev->event,
1593 tp->module, tp->symbol);
1594 else 1593 else
1595 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", 1594 ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",
1596 tp->retprobe ? 'r' : 'p',
1597 tev->group, tev->event,
1598 tp->module ?: "", tp->module ? ":" : "", 1595 tp->module ?: "", tp->module ? ":" : "",
1599 tp->symbol, tp->offset); 1596 tp->symbol, tp->offset);
1600 1597
1601 if (len <= 0) 1598 if (ret <= 0)
1602 goto error; 1599 goto error;
1600 len += ret;
1603 1601
1604 for (i = 0; i < tev->nargs; i++) { 1602 for (i = 0; i < tev->nargs; i++) {
1605 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, 1603 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
@@ -2150,113 +2148,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
2150 return ret; 2148 return ret;
2151} 2149}
2152 2150
2153static int convert_to_probe_trace_events(struct perf_probe_event *pev, 2151static char *looking_function_name;
2154 struct probe_trace_event **tevs, 2152static int num_matched_functions;
2155 int max_tevs, const char *target) 2153
2154static int probe_function_filter(struct map *map __maybe_unused,
2155 struct symbol *sym)
2156{
2157 if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
2158 strcmp(looking_function_name, sym->name) == 0) {
2159 num_matched_functions++;
2160 return 0;
2161 }
2162 return 1;
2163}
2164
2165#define strdup_or_goto(str, label) \
2166 ({ char *__p = strdup(str); if (!__p) goto label; __p; })
2167
2168/*
2169 * Find probe function addresses from map.
2170 * Return an error or the number of found probe_trace_event
2171 */
2172static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
2173 struct probe_trace_event **tevs,
2174 int max_tevs, const char *target)
2156{ 2175{
2176 struct map *map = NULL;
2177 struct kmap *kmap = NULL;
2178 struct ref_reloc_sym *reloc_sym = NULL;
2157 struct symbol *sym; 2179 struct symbol *sym;
2158 int ret, i; 2180 struct rb_node *nd;
2159 struct probe_trace_event *tev; 2181 struct probe_trace_event *tev;
2182 struct perf_probe_point *pp = &pev->point;
2183 struct probe_trace_point *tp;
2184 int ret, i;
2160 2185
2161 if (pev->uprobes && !pev->group) { 2186 /* Init maps of given executable or kernel */
2162 /* Replace group name if not given */ 2187 if (pev->uprobes)
2163 ret = convert_exec_to_group(target, &pev->group); 2188 map = dso__new_map(target);
2164 if (ret != 0) { 2189 else
2165 pr_warning("Failed to make a group name.\n"); 2190 map = kernel_get_module_map(target);
2166 return ret; 2191 if (!map) {
2167 } 2192 ret = -EINVAL;
2193 goto out;
2168 } 2194 }
2169 2195
2170 /* Convert perf_probe_event with debuginfo */ 2196 /*
2171 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); 2197 * Load matched symbols: Since the different local symbols may have
2172 if (ret != 0) 2198 * same name but different addresses, this lists all the symbols.
2173 return ret; /* Found in debuginfo or got an error */ 2199 */
2174 2200 num_matched_functions = 0;
2175 if (pev->uprobes) { 2201 looking_function_name = pp->function;
2176 ret = convert_name_to_addr(pev, target); 2202 ret = map__load(map, probe_function_filter);
2177 if (ret < 0) 2203 if (ret || num_matched_functions == 0) {
2178 return ret; 2204 pr_err("Failed to find symbol %s in %s\n", pp->function,
2205 target ? : "kernel");
2206 ret = -ENOENT;
2207 goto out;
2208 } else if (num_matched_functions > max_tevs) {
2209 pr_err("Too many functions matched in %s\n",
2210 target ? : "kernel");
2211 ret = -E2BIG;
2212 goto out;
2179 } 2213 }
2180 2214
2181 /* Allocate trace event buffer */ 2215 if (!pev->uprobes) {
2182 tev = *tevs = zalloc(sizeof(struct probe_trace_event)); 2216 kmap = map__kmap(map);
2183 if (tev == NULL) 2217 reloc_sym = kmap->ref_reloc_sym;
2184 return -ENOMEM; 2218 if (!reloc_sym) {
2219 pr_warning("Relocated base symbol is not found!\n");
2220 ret = -EINVAL;
2221 goto out;
2222 }
2223 }
2185 2224
2186 /* Copy parameters */ 2225 /* Setup result trace-probe-events */
2187 tev->point.symbol = strdup(pev->point.function); 2226 *tevs = zalloc(sizeof(*tev) * num_matched_functions);
2188 if (tev->point.symbol == NULL) { 2227 if (!*tevs) {
2189 ret = -ENOMEM; 2228 ret = -ENOMEM;
2190 goto error; 2229 goto out;
2191 } 2230 }
2192 2231
2193 if (target) { 2232 ret = 0;
2194 tev->point.module = strdup(target); 2233 map__for_each_symbol(map, sym, nd) {
2195 if (tev->point.module == NULL) { 2234 tev = (*tevs) + ret;
2196 ret = -ENOMEM; 2235 tp = &tev->point;
2197 goto error; 2236 if (ret == num_matched_functions) {
2237 pr_warning("Too many symbols are listed. Skip it.\n");
2238 break;
2198 } 2239 }
2199 } 2240 ret++;
2200 2241
2201 tev->point.offset = pev->point.offset; 2242 if (pp->offset > sym->end - sym->start) {
2202 tev->point.retprobe = pev->point.retprobe; 2243 pr_warning("Offset %ld is bigger than the size of %s\n",
2203 tev->nargs = pev->nargs; 2244 pp->offset, sym->name);
2204 tev->uprobes = pev->uprobes; 2245 ret = -ENOENT;
2205 2246 goto err_out;
2206 if (tev->nargs) { 2247 }
2207 tev->args = zalloc(sizeof(struct probe_trace_arg) 2248 /* Add one probe point */
2208 * tev->nargs); 2249 tp->address = map->unmap_ip(map, sym->start) + pp->offset;
2209 if (tev->args == NULL) { 2250 if (reloc_sym) {
2210 ret = -ENOMEM; 2251 tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
2211 goto error; 2252 tp->offset = tp->address - reloc_sym->addr;
2253 } else {
2254 tp->symbol = strdup_or_goto(sym->name, nomem_out);
2255 tp->offset = pp->offset;
2256 }
2257 tp->retprobe = pp->retprobe;
2258 if (target)
2259 tev->point.module = strdup_or_goto(target, nomem_out);
2260 tev->uprobes = pev->uprobes;
2261 tev->nargs = pev->nargs;
2262 if (tev->nargs) {
2263 tev->args = zalloc(sizeof(struct probe_trace_arg) *
2264 tev->nargs);
2265 if (tev->args == NULL)
2266 goto nomem_out;
2212 } 2267 }
2213 for (i = 0; i < tev->nargs; i++) { 2268 for (i = 0; i < tev->nargs; i++) {
2214 if (pev->args[i].name) { 2269 if (pev->args[i].name)
2215 tev->args[i].name = strdup(pev->args[i].name); 2270 tev->args[i].name =
2216 if (tev->args[i].name == NULL) { 2271 strdup_or_goto(pev->args[i].name,
2217 ret = -ENOMEM; 2272 nomem_out);
2218 goto error; 2273
2219 } 2274 tev->args[i].value = strdup_or_goto(pev->args[i].var,
2220 } 2275 nomem_out);
2221 tev->args[i].value = strdup(pev->args[i].var); 2276 if (pev->args[i].type)
2222 if (tev->args[i].value == NULL) { 2277 tev->args[i].type =
2223 ret = -ENOMEM; 2278 strdup_or_goto(pev->args[i].type,
2224 goto error; 2279 nomem_out);
2225 }
2226 if (pev->args[i].type) {
2227 tev->args[i].type = strdup(pev->args[i].type);
2228 if (tev->args[i].type == NULL) {
2229 ret = -ENOMEM;
2230 goto error;
2231 }
2232 }
2233 } 2280 }
2234 } 2281 }
2235 2282
2236 if (pev->uprobes) 2283out:
2237 return 1; 2284 if (map && pev->uprobes) {
2285 /* Only when using uprobe(exec) map needs to be released */
2286 dso__delete(map->dso);
2287 map__delete(map);
2288 }
2289 return ret;
2238 2290
2239 /* Currently just checking function name from symbol map */ 2291nomem_out:
2240 sym = __find_kernel_function_by_name(tev->point.symbol, NULL); 2292 ret = -ENOMEM;
2241 if (!sym) { 2293err_out:
2242 pr_warning("Kernel symbol \'%s\' not found.\n", 2294 clear_probe_trace_events(*tevs, num_matched_functions);
2243 tev->point.symbol); 2295 zfree(tevs);
2244 ret = -ENOENT; 2296 goto out;
2245 goto error; 2297}
2246 } else if (tev->point.offset > sym->end - sym->start) {
2247 pr_warning("Offset specified is greater than size of %s\n",
2248 tev->point.symbol);
2249 ret = -ENOENT;
2250 goto error;
2251 2298
2299static int convert_to_probe_trace_events(struct perf_probe_event *pev,
2300 struct probe_trace_event **tevs,
2301 int max_tevs, const char *target)
2302{
2303 int ret;
2304
2305 if (pev->uprobes && !pev->group) {
2306 /* Replace group name if not given */
2307 ret = convert_exec_to_group(target, &pev->group);
2308 if (ret != 0) {
2309 pr_warning("Failed to make a group name.\n");
2310 return ret;
2311 }
2252 } 2312 }
2253 2313
2254 return 1; 2314 /* Convert perf_probe_event with debuginfo */
2255error: 2315 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
2256 clear_probe_trace_event(tev); 2316 if (ret != 0)
2257 free(tev); 2317 return ret; /* Found in debuginfo or got an error */
2258 *tevs = NULL; 2318
2259 return ret; 2319 return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
2260} 2320}
2261 2321
2262struct __event_package { 2322struct __event_package {
@@ -2461,7 +2521,7 @@ static struct strfilter *available_func_filter;
2461static int filter_available_functions(struct map *map __maybe_unused, 2521static int filter_available_functions(struct map *map __maybe_unused,
2462 struct symbol *sym) 2522 struct symbol *sym)
2463{ 2523{
2464 if (sym->binding == STB_GLOBAL && 2524 if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
2465 strfilter__compare(available_func_filter, sym->name)) 2525 strfilter__compare(available_func_filter, sym->name))
2466 return 0; 2526 return 0;
2467 return 1; 2527 return 1;
@@ -2509,95 +2569,3 @@ end:
2509 return ret; 2569 return ret;
2510} 2570}
2511 2571
2512/*
2513 * uprobe_events only accepts address:
2514 * Convert function and any offset to address
2515 */
2516static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
2517{
2518 struct perf_probe_point *pp = &pev->point;
2519 struct symbol *sym;
2520 struct map *map = NULL;
2521 char *function = NULL;
2522 int ret = -EINVAL;
2523 unsigned long long vaddr = 0;
2524
2525 if (!pp->function) {
2526 pr_warning("No function specified for uprobes");
2527 goto out;
2528 }
2529
2530 function = strdup(pp->function);
2531 if (!function) {
2532 pr_warning("Failed to allocate memory by strdup.\n");
2533 ret = -ENOMEM;
2534 goto out;
2535 }
2536
2537 map = dso__new_map(exec);
2538 if (!map) {
2539 pr_warning("Cannot find appropriate DSO for %s.\n", exec);
2540 goto out;
2541 }
2542 available_func_filter = strfilter__new(function, NULL);
2543 if (map__load(map, filter_available_functions)) {
2544 pr_err("Failed to load map.\n");
2545 goto out;
2546 }
2547
2548 sym = map__find_symbol_by_name(map, function, NULL);
2549 if (!sym) {
2550 pr_warning("Cannot find %s in DSO %s\n", function, exec);
2551 goto out;
2552 }
2553
2554 if (map->start > sym->start)
2555 vaddr = map->start;
2556 vaddr += sym->start + pp->offset + map->pgoff;
2557 pp->offset = 0;
2558
2559 if (!pev->event) {
2560 pev->event = function;
2561 function = NULL;
2562 }
2563 if (!pev->group) {
2564 char *ptr1, *ptr2, *exec_copy;
2565
2566 pev->group = zalloc(sizeof(char *) * 64);
2567 exec_copy = strdup(exec);
2568 if (!exec_copy) {
2569 ret = -ENOMEM;
2570 pr_warning("Failed to copy exec string.\n");
2571 goto out;
2572 }
2573
2574 ptr1 = strdup(basename(exec_copy));
2575 if (ptr1) {
2576 ptr2 = strpbrk(ptr1, "-._");
2577 if (ptr2)
2578 *ptr2 = '\0';
2579 e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
2580 ptr1);
2581 free(ptr1);
2582 }
2583 free(exec_copy);
2584 }
2585 free(pp->function);
2586 pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
2587 if (!pp->function) {
2588 ret = -ENOMEM;
2589 pr_warning("Failed to allocate memory by zalloc.\n");
2590 goto out;
2591 }
2592 e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
2593 ret = 0;
2594
2595out:
2596 if (map) {
2597 dso__delete(map->dso);
2598 map__delete(map);
2599 }
2600 if (function)
2601 free(function);
2602 return ret;
2603}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 538d484fa6c5..2553ae04b788 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -79,6 +79,17 @@ struct symbol {
79void symbol__delete(struct symbol *sym); 79void symbol__delete(struct symbol *sym);
80void symbols__delete(struct rb_root *symbols); 80void symbols__delete(struct rb_root *symbols);
81 81
82/* symbols__for_each_entry - iterate over symbols (rb_root)
83 *
84 * @symbols: the rb_root of symbols
85 * @pos: the 'struct symbol *' to use as a loop cursor
86 * @nd: the 'struct rb_node *' to use as a temporary storage
87 */
88#define symbols__for_each_entry(symbols, pos, nd) \
89 for (nd = rb_first(symbols); \
90 nd && (pos = rb_entry(nd, struct symbol, rb_node)); \
91 nd = rb_next(nd))
92
82static inline size_t symbol__size(const struct symbol *sym) 93static inline size_t symbol__size(const struct symbol *sym)
83{ 94{
84 return sym->end - sym->start + 1; 95 return sym->end - sym->start + 1;