diff options
Diffstat (limited to 'tools/perf/util/dso.c')
-rw-r--r-- | tools/perf/util/dso.c | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c new file mode 100644 index 000000000000..db24a3f0c820 --- /dev/null +++ b/tools/perf/util/dso.c | |||
@@ -0,0 +1,594 @@ | |||
1 | #include "symbol.h" | ||
2 | #include "dso.h" | ||
3 | #include "util.h" | ||
4 | #include "debug.h" | ||
5 | |||
6 | char dso__symtab_origin(const struct dso *dso) | ||
7 | { | ||
8 | static const char origin[] = { | ||
9 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | ||
10 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | ||
11 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | ||
12 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | ||
13 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | ||
14 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', | ||
15 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', | ||
16 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | ||
17 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | ||
18 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | ||
19 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | ||
20 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | ||
21 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
22 | }; | ||
23 | |||
24 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | ||
25 | return '!'; | ||
26 | return origin[dso->symtab_type]; | ||
27 | } | ||
28 | |||
29 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
30 | char *root_dir, char *file, size_t size) | ||
31 | { | ||
32 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
33 | int ret = 0; | ||
34 | |||
35 | switch (type) { | ||
36 | case DSO_BINARY_TYPE__DEBUGLINK: { | ||
37 | char *debuglink; | ||
38 | |||
39 | strncpy(file, dso->long_name, size); | ||
40 | debuglink = file + dso->long_name_len; | ||
41 | while (debuglink != file && *debuglink != '/') | ||
42 | debuglink--; | ||
43 | if (*debuglink == '/') | ||
44 | debuglink++; | ||
45 | filename__read_debuglink(dso->long_name, debuglink, | ||
46 | size - (debuglink - file)); | ||
47 | } | ||
48 | break; | ||
49 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: | ||
50 | /* skip the locally configured cache if a symfs is given */ | ||
51 | if (symbol_conf.symfs[0] || | ||
52 | (dso__build_id_filename(dso, file, size) == NULL)) | ||
53 | ret = -1; | ||
54 | break; | ||
55 | |||
56 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | ||
57 | snprintf(file, size, "%s/usr/lib/debug%s.debug", | ||
58 | symbol_conf.symfs, dso->long_name); | ||
59 | break; | ||
60 | |||
61 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | ||
62 | snprintf(file, size, "%s/usr/lib/debug%s", | ||
63 | symbol_conf.symfs, dso->long_name); | ||
64 | break; | ||
65 | |||
66 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | ||
67 | if (!dso->has_build_id) { | ||
68 | ret = -1; | ||
69 | break; | ||
70 | } | ||
71 | |||
72 | build_id__sprintf(dso->build_id, | ||
73 | sizeof(dso->build_id), | ||
74 | build_id_hex); | ||
75 | snprintf(file, size, | ||
76 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
77 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | ||
78 | break; | ||
79 | |||
80 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | ||
81 | snprintf(file, size, "%s%s", | ||
82 | symbol_conf.symfs, dso->long_name); | ||
83 | break; | ||
84 | |||
85 | case DSO_BINARY_TYPE__GUEST_KMODULE: | ||
86 | snprintf(file, size, "%s%s%s", symbol_conf.symfs, | ||
87 | root_dir, dso->long_name); | ||
88 | break; | ||
89 | |||
90 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | ||
91 | snprintf(file, size, "%s%s", symbol_conf.symfs, | ||
92 | dso->long_name); | ||
93 | break; | ||
94 | |||
95 | default: | ||
96 | case DSO_BINARY_TYPE__KALLSYMS: | ||
97 | case DSO_BINARY_TYPE__VMLINUX: | ||
98 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | ||
99 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
100 | case DSO_BINARY_TYPE__JAVA_JIT: | ||
101 | case DSO_BINARY_TYPE__NOT_FOUND: | ||
102 | ret = -1; | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | static int open_dso(struct dso *dso, struct machine *machine) | ||
110 | { | ||
111 | char *root_dir = (char *) ""; | ||
112 | char *name; | ||
113 | int fd; | ||
114 | |||
115 | name = malloc(PATH_MAX); | ||
116 | if (!name) | ||
117 | return -ENOMEM; | ||
118 | |||
119 | if (machine) | ||
120 | root_dir = machine->root_dir; | ||
121 | |||
122 | if (dso__binary_type_file(dso, dso->data_type, | ||
123 | root_dir, name, PATH_MAX)) { | ||
124 | free(name); | ||
125 | return -EINVAL; | ||
126 | } | ||
127 | |||
128 | fd = open(name, O_RDONLY); | ||
129 | free(name); | ||
130 | return fd; | ||
131 | } | ||
132 | |||
133 | int dso__data_fd(struct dso *dso, struct machine *machine) | ||
134 | { | ||
135 | static enum dso_binary_type binary_type_data[] = { | ||
136 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
137 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
138 | DSO_BINARY_TYPE__NOT_FOUND, | ||
139 | }; | ||
140 | int i = 0; | ||
141 | |||
142 | if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) | ||
143 | return open_dso(dso, machine); | ||
144 | |||
145 | do { | ||
146 | int fd; | ||
147 | |||
148 | dso->data_type = binary_type_data[i++]; | ||
149 | |||
150 | fd = open_dso(dso, machine); | ||
151 | if (fd >= 0) | ||
152 | return fd; | ||
153 | |||
154 | } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); | ||
155 | |||
156 | return -EINVAL; | ||
157 | } | ||
158 | |||
159 | static void | ||
160 | dso_cache__free(struct rb_root *root) | ||
161 | { | ||
162 | struct rb_node *next = rb_first(root); | ||
163 | |||
164 | while (next) { | ||
165 | struct dso_cache *cache; | ||
166 | |||
167 | cache = rb_entry(next, struct dso_cache, rb_node); | ||
168 | next = rb_next(&cache->rb_node); | ||
169 | rb_erase(&cache->rb_node, root); | ||
170 | free(cache); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | static struct dso_cache* | ||
175 | dso_cache__find(struct rb_root *root, u64 offset) | ||
176 | { | ||
177 | struct rb_node **p = &root->rb_node; | ||
178 | struct rb_node *parent = NULL; | ||
179 | struct dso_cache *cache; | ||
180 | |||
181 | while (*p != NULL) { | ||
182 | u64 end; | ||
183 | |||
184 | parent = *p; | ||
185 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
186 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
187 | |||
188 | if (offset < cache->offset) | ||
189 | p = &(*p)->rb_left; | ||
190 | else if (offset >= end) | ||
191 | p = &(*p)->rb_right; | ||
192 | else | ||
193 | return cache; | ||
194 | } | ||
195 | return NULL; | ||
196 | } | ||
197 | |||
198 | static void | ||
199 | dso_cache__insert(struct rb_root *root, struct dso_cache *new) | ||
200 | { | ||
201 | struct rb_node **p = &root->rb_node; | ||
202 | struct rb_node *parent = NULL; | ||
203 | struct dso_cache *cache; | ||
204 | u64 offset = new->offset; | ||
205 | |||
206 | while (*p != NULL) { | ||
207 | u64 end; | ||
208 | |||
209 | parent = *p; | ||
210 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
211 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
212 | |||
213 | if (offset < cache->offset) | ||
214 | p = &(*p)->rb_left; | ||
215 | else if (offset >= end) | ||
216 | p = &(*p)->rb_right; | ||
217 | } | ||
218 | |||
219 | rb_link_node(&new->rb_node, parent, p); | ||
220 | rb_insert_color(&new->rb_node, root); | ||
221 | } | ||
222 | |||
223 | static ssize_t | ||
224 | dso_cache__memcpy(struct dso_cache *cache, u64 offset, | ||
225 | u8 *data, u64 size) | ||
226 | { | ||
227 | u64 cache_offset = offset - cache->offset; | ||
228 | u64 cache_size = min(cache->size - cache_offset, size); | ||
229 | |||
230 | memcpy(data, cache->data + cache_offset, cache_size); | ||
231 | return cache_size; | ||
232 | } | ||
233 | |||
234 | static ssize_t | ||
235 | dso_cache__read(struct dso *dso, struct machine *machine, | ||
236 | u64 offset, u8 *data, ssize_t size) | ||
237 | { | ||
238 | struct dso_cache *cache; | ||
239 | ssize_t ret; | ||
240 | int fd; | ||
241 | |||
242 | fd = dso__data_fd(dso, machine); | ||
243 | if (fd < 0) | ||
244 | return -1; | ||
245 | |||
246 | do { | ||
247 | u64 cache_offset; | ||
248 | |||
249 | ret = -ENOMEM; | ||
250 | |||
251 | cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); | ||
252 | if (!cache) | ||
253 | break; | ||
254 | |||
255 | cache_offset = offset & DSO__DATA_CACHE_MASK; | ||
256 | ret = -EINVAL; | ||
257 | |||
258 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | ||
259 | break; | ||
260 | |||
261 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | ||
262 | if (ret <= 0) | ||
263 | break; | ||
264 | |||
265 | cache->offset = cache_offset; | ||
266 | cache->size = ret; | ||
267 | dso_cache__insert(&dso->cache, cache); | ||
268 | |||
269 | ret = dso_cache__memcpy(cache, offset, data, size); | ||
270 | |||
271 | } while (0); | ||
272 | |||
273 | if (ret <= 0) | ||
274 | free(cache); | ||
275 | |||
276 | close(fd); | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | ||
281 | u64 offset, u8 *data, ssize_t size) | ||
282 | { | ||
283 | struct dso_cache *cache; | ||
284 | |||
285 | cache = dso_cache__find(&dso->cache, offset); | ||
286 | if (cache) | ||
287 | return dso_cache__memcpy(cache, offset, data, size); | ||
288 | else | ||
289 | return dso_cache__read(dso, machine, offset, data, size); | ||
290 | } | ||
291 | |||
292 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
293 | u64 offset, u8 *data, ssize_t size) | ||
294 | { | ||
295 | ssize_t r = 0; | ||
296 | u8 *p = data; | ||
297 | |||
298 | do { | ||
299 | ssize_t ret; | ||
300 | |||
301 | ret = dso_cache_read(dso, machine, offset, p, size); | ||
302 | if (ret < 0) | ||
303 | return ret; | ||
304 | |||
305 | /* Reached EOF, return what we have. */ | ||
306 | if (!ret) | ||
307 | break; | ||
308 | |||
309 | BUG_ON(ret > size); | ||
310 | |||
311 | r += ret; | ||
312 | p += ret; | ||
313 | offset += ret; | ||
314 | size -= ret; | ||
315 | |||
316 | } while (size); | ||
317 | |||
318 | return r; | ||
319 | } | ||
320 | |||
321 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
322 | struct machine *machine, u64 addr, | ||
323 | u8 *data, ssize_t size) | ||
324 | { | ||
325 | u64 offset = map->map_ip(map, addr); | ||
326 | return dso__data_read_offset(dso, machine, offset, data, size); | ||
327 | } | ||
328 | |||
329 | struct map *dso__new_map(const char *name) | ||
330 | { | ||
331 | struct map *map = NULL; | ||
332 | struct dso *dso = dso__new(name); | ||
333 | |||
334 | if (dso) | ||
335 | map = map__new2(0, dso, MAP__FUNCTION); | ||
336 | |||
337 | return map; | ||
338 | } | ||
339 | |||
340 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | ||
341 | const char *short_name, int dso_type) | ||
342 | { | ||
343 | /* | ||
344 | * The kernel dso could be created by build_id processing. | ||
345 | */ | ||
346 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); | ||
347 | |||
348 | /* | ||
349 | * We need to run this in all cases, since during the build_id | ||
350 | * processing we had no idea this was the kernel dso. | ||
351 | */ | ||
352 | if (dso != NULL) { | ||
353 | dso__set_short_name(dso, short_name); | ||
354 | dso->kernel = dso_type; | ||
355 | } | ||
356 | |||
357 | return dso; | ||
358 | } | ||
359 | |||
360 | void dso__set_long_name(struct dso *dso, char *name) | ||
361 | { | ||
362 | if (name == NULL) | ||
363 | return; | ||
364 | dso->long_name = name; | ||
365 | dso->long_name_len = strlen(name); | ||
366 | } | ||
367 | |||
368 | void dso__set_short_name(struct dso *dso, const char *name) | ||
369 | { | ||
370 | if (name == NULL) | ||
371 | return; | ||
372 | dso->short_name = name; | ||
373 | dso->short_name_len = strlen(name); | ||
374 | } | ||
375 | |||
376 | static void dso__set_basename(struct dso *dso) | ||
377 | { | ||
378 | dso__set_short_name(dso, basename(dso->long_name)); | ||
379 | } | ||
380 | |||
381 | int dso__name_len(const struct dso *dso) | ||
382 | { | ||
383 | if (!dso) | ||
384 | return strlen("[unknown]"); | ||
385 | if (verbose) | ||
386 | return dso->long_name_len; | ||
387 | |||
388 | return dso->short_name_len; | ||
389 | } | ||
390 | |||
391 | bool dso__loaded(const struct dso *dso, enum map_type type) | ||
392 | { | ||
393 | return dso->loaded & (1 << type); | ||
394 | } | ||
395 | |||
396 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) | ||
397 | { | ||
398 | return dso->sorted_by_name & (1 << type); | ||
399 | } | ||
400 | |||
401 | void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | ||
402 | { | ||
403 | dso->sorted_by_name |= (1 << type); | ||
404 | } | ||
405 | |||
406 | struct dso *dso__new(const char *name) | ||
407 | { | ||
408 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); | ||
409 | |||
410 | if (dso != NULL) { | ||
411 | int i; | ||
412 | strcpy(dso->name, name); | ||
413 | dso__set_long_name(dso, dso->name); | ||
414 | dso__set_short_name(dso, dso->name); | ||
415 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
416 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | ||
417 | dso->cache = RB_ROOT; | ||
418 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
419 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
420 | dso->loaded = 0; | ||
421 | dso->sorted_by_name = 0; | ||
422 | dso->has_build_id = 0; | ||
423 | dso->kernel = DSO_TYPE_USER; | ||
424 | dso->needs_swap = DSO_SWAP__UNSET; | ||
425 | INIT_LIST_HEAD(&dso->node); | ||
426 | } | ||
427 | |||
428 | return dso; | ||
429 | } | ||
430 | |||
431 | void dso__delete(struct dso *dso) | ||
432 | { | ||
433 | int i; | ||
434 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
435 | symbols__delete(&dso->symbols[i]); | ||
436 | if (dso->sname_alloc) | ||
437 | free((char *)dso->short_name); | ||
438 | if (dso->lname_alloc) | ||
439 | free(dso->long_name); | ||
440 | dso_cache__free(&dso->cache); | ||
441 | free(dso); | ||
442 | } | ||
443 | |||
444 | void dso__set_build_id(struct dso *dso, void *build_id) | ||
445 | { | ||
446 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); | ||
447 | dso->has_build_id = 1; | ||
448 | } | ||
449 | |||
450 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
451 | { | ||
452 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | ||
453 | } | ||
454 | |||
455 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | ||
456 | { | ||
457 | char path[PATH_MAX]; | ||
458 | |||
459 | if (machine__is_default_guest(machine)) | ||
460 | return; | ||
461 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); | ||
462 | if (sysfs__read_build_id(path, dso->build_id, | ||
463 | sizeof(dso->build_id)) == 0) | ||
464 | dso->has_build_id = true; | ||
465 | } | ||
466 | |||
467 | int dso__kernel_module_get_build_id(struct dso *dso, | ||
468 | const char *root_dir) | ||
469 | { | ||
470 | char filename[PATH_MAX]; | ||
471 | /* | ||
472 | * kernel module short names are of the form "[module]" and | ||
473 | * we need just "module" here. | ||
474 | */ | ||
475 | const char *name = dso->short_name + 1; | ||
476 | |||
477 | snprintf(filename, sizeof(filename), | ||
478 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", | ||
479 | root_dir, (int)strlen(name) - 1, name); | ||
480 | |||
481 | if (sysfs__read_build_id(filename, dso->build_id, | ||
482 | sizeof(dso->build_id)) == 0) | ||
483 | dso->has_build_id = true; | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | ||
489 | { | ||
490 | bool have_build_id = false; | ||
491 | struct dso *pos; | ||
492 | |||
493 | list_for_each_entry(pos, head, node) { | ||
494 | if (with_hits && !pos->hit) | ||
495 | continue; | ||
496 | if (pos->has_build_id) { | ||
497 | have_build_id = true; | ||
498 | continue; | ||
499 | } | ||
500 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
501 | sizeof(pos->build_id)) > 0) { | ||
502 | have_build_id = true; | ||
503 | pos->has_build_id = true; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | return have_build_id; | ||
508 | } | ||
509 | |||
510 | void dsos__add(struct list_head *head, struct dso *dso) | ||
511 | { | ||
512 | list_add_tail(&dso->node, head); | ||
513 | } | ||
514 | |||
515 | struct dso *dsos__find(struct list_head *head, const char *name) | ||
516 | { | ||
517 | struct dso *pos; | ||
518 | |||
519 | list_for_each_entry(pos, head, node) | ||
520 | if (strcmp(pos->long_name, name) == 0) | ||
521 | return pos; | ||
522 | return NULL; | ||
523 | } | ||
524 | |||
525 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | ||
526 | { | ||
527 | struct dso *dso = dsos__find(head, name); | ||
528 | |||
529 | if (!dso) { | ||
530 | dso = dso__new(name); | ||
531 | if (dso != NULL) { | ||
532 | dsos__add(head, dso); | ||
533 | dso__set_basename(dso); | ||
534 | } | ||
535 | } | ||
536 | |||
537 | return dso; | ||
538 | } | ||
539 | |||
540 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
541 | bool with_hits) | ||
542 | { | ||
543 | struct dso *pos; | ||
544 | size_t ret = 0; | ||
545 | |||
546 | list_for_each_entry(pos, head, node) { | ||
547 | if (with_hits && !pos->hit) | ||
548 | continue; | ||
549 | ret += dso__fprintf_buildid(pos, fp); | ||
550 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
551 | } | ||
552 | return ret; | ||
553 | } | ||
554 | |||
555 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) | ||
556 | { | ||
557 | struct dso *pos; | ||
558 | size_t ret = 0; | ||
559 | |||
560 | list_for_each_entry(pos, head, node) { | ||
561 | int i; | ||
562 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
563 | ret += dso__fprintf(pos, i, fp); | ||
564 | } | ||
565 | |||
566 | return ret; | ||
567 | } | ||
568 | |||
569 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) | ||
570 | { | ||
571 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
572 | |||
573 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
574 | return fprintf(fp, "%s", sbuild_id); | ||
575 | } | ||
576 | |||
577 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | ||
578 | { | ||
579 | struct rb_node *nd; | ||
580 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); | ||
581 | |||
582 | if (dso->short_name != dso->long_name) | ||
583 | ret += fprintf(fp, "%s, ", dso->long_name); | ||
584 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
585 | dso->loaded ? "" : "NOT "); | ||
586 | ret += dso__fprintf_buildid(dso, fp); | ||
587 | ret += fprintf(fp, ")\n"); | ||
588 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
589 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
590 | ret += symbol__fprintf(pos, fp); | ||
591 | } | ||
592 | |||
593 | return ret; | ||
594 | } | ||