aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/probe-finder.c
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@redhat.com>2009-10-08 17:17:38 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2009-10-12 17:31:52 -0400
commit4ea42b181434bfc6a0a18d32214130a242d489bf (patch)
tree2c467d795d90440e0293951087c41caca8397584 /tools/perf/util/probe-finder.c
parente93f4d8539d5e9dd59f4af9d8ef4e9b62cfa1f81 (diff)
perf: Add perf probe subcommand, a kprobe-event setup helper
Add perf probe subcommand that implements a kprobe-event setup helper to the perf command. This allows user to define kprobe events using C expressions (C line numbers, C function names, and C local variables). Usage ----- perf probe [<options>] -P 'PROBEDEF' [-P 'PROBEDEF' ...] -k, --vmlinux <file> vmlinux/module pathname -P, --probe <p|r:[GRP/]NAME FUNC[+OFFS][@SRC]|@SRC:LINE [ARG ...]> probe point definition, where p: kprobe probe r: kretprobe probe GRP: Group name (optional) NAME: Event name FUNC: Function name OFFS: Offset from function entry (in byte) SRC: Source code path LINE: Line number ARG: Probe argument (local variable name or kprobe-tracer argument format is supported.) Changes in v4: - Add _GNU_SOURCE macro for strndup(). Changes in v3: - Remove -r option because perf always be used for online kernel. - Check malloc/calloc results. Changes in v2: - Check synthesized string length. - Rename perf kprobe to perf probe. - Use spaces for separator and update usage comment. - Check error paths in parse_probepoint(). - Check optimized-out variables. Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Christoph Hellwig <hch@infradead.org> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Cc: Jim Keniston <jkenisto@us.ibm.com> Cc: Frank Ch. Eigler <fche@redhat.com> LKML-Reference: <20091008211737.29299.14784.stgit@dhcp-100-2-132.bos.redhat.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r--tools/perf/util/probe-finder.c690
1 files changed, 690 insertions, 0 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
new file mode 100644
index 000000000000..ec6f53f29e00
--- /dev/null
+++ b/tools/perf/util/probe-finder.c
@@ -0,0 +1,690 @@
1/*
2 * probe-finder.c : C expression to kprobe event 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
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include <sys/utsname.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <getopt.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdarg.h>
33#include <ctype.h>
34#include "probe-finder.h"
35
36
37/* Dwarf_Die Linkage to parent Die */
38struct die_link {
39 struct die_link *parent; /* Parent die */
40 Dwarf_Die die; /* Current die */
41};
42
43static Dwarf_Debug __dw_debug;
44static Dwarf_Error __dw_error;
45
46static void msg_exit(int ret, const char *fmt, ...)
47{
48 va_list ap;
49
50 va_start(ap, fmt);
51 fprintf(stderr, "Error: ");
52 vfprintf(stderr, fmt, ap);
53 va_end(ap);
54
55 fprintf(stderr, "\n");
56 exit(ret);
57}
58
59
60/*
61 * Generic dwarf analysis helpers
62 */
63
64#define X86_32_MAX_REGS 8
65const char *x86_32_regs_table[X86_32_MAX_REGS] = {
66 "%ax",
67 "%cx",
68 "%dx",
69 "%bx",
70 "$stack", /* Stack address instead of %sp */
71 "%bp",
72 "%si",
73 "%di",
74};
75
76#define X86_64_MAX_REGS 16
77const char *x86_64_regs_table[X86_64_MAX_REGS] = {
78 "%ax",
79 "%dx",
80 "%cx",
81 "%bx",
82 "%si",
83 "%di",
84 "%bp",
85 "%sp",
86 "%r8",
87 "%r9",
88 "%r10",
89 "%r11",
90 "%r12",
91 "%r13",
92 "%r14",
93 "%r15",
94};
95
96/* TODO: switching by dwarf address size */
97#ifdef __x86_64__
98#define ARCH_MAX_REGS X86_64_MAX_REGS
99#define arch_regs_table x86_64_regs_table
100#else
101#define ARCH_MAX_REGS X86_32_MAX_REGS
102#define arch_regs_table x86_32_regs_table
103#endif
104
105/* Return architecture dependent register string (for kprobe-tracer) */
106static const char *get_arch_regstr(unsigned int n)
107{
108 return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
109}
110
111/*
112 * Compare the tail of two strings.
113 * Return 0 if whole of either string is same as another's tail part.
114 */
115static int strtailcmp(const char *s1, const char *s2)
116{
117 int i1 = strlen(s1);
118 int i2 = strlen(s2);
119 while (--i1 > 0 && --i2 > 0) {
120 if (s1[i1] != s2[i2])
121 return s1[i1] - s2[i2];
122 }
123 return 0;
124}
125
126/* Find the fileno of the target file. */
127static Dwarf_Unsigned die_get_fileno(Dwarf_Die cu_die, const char *fname)
128{
129 Dwarf_Signed cnt, i;
130 Dwarf_Unsigned found = 0;
131 char **srcs;
132 int ret;
133
134 if (!fname)
135 return 0;
136
137 ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
138 if (ret == DW_DLV_OK) {
139 for (i = 0; i < cnt && !found; i++) {
140 if (strtailcmp(srcs[i], fname) == 0)
141 found = i + 1;
142 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
143 }
144 for (; i < cnt; i++)
145 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
146 dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
147 }
148 if (found)
149 debug("found fno: %d\n", (int)found);
150 return found;
151}
152
153/* Compare diename and tname */
154static int die_compare_name(Dwarf_Die die, const char *tname)
155{
156 char *name;
157 int ret;
158 ret = dwarf_diename(die, &name, &__dw_error);
159 ERR_IF(ret == DW_DLV_ERROR);
160 if (ret == DW_DLV_OK) {
161 ret = strcmp(tname, name);
162 dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
163 } else
164 ret = -1;
165 return ret;
166}
167
168/* Check the address is in the subprogram(function). */
169static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
170 Dwarf_Signed *offs)
171{
172 Dwarf_Addr lopc, hipc;
173 int ret;
174
175 /* TODO: check ranges */
176 ret = dwarf_lowpc(sp_die, &lopc, &__dw_error);
177 ERR_IF(ret == DW_DLV_ERROR);
178 if (ret == DW_DLV_NO_ENTRY)
179 return 0;
180 ret = dwarf_highpc(sp_die, &hipc, &__dw_error);
181 ERR_IF(ret != DW_DLV_OK);
182 if (lopc <= addr && addr < hipc) {
183 *offs = addr - lopc;
184 return 1;
185 } else
186 return 0;
187}
188
189/* Check the die is inlined function */
190static Dwarf_Bool die_inlined_subprogram(Dwarf_Die die)
191{
192 /* TODO: check strictly */
193 Dwarf_Bool inl;
194 int ret;
195
196 ret = dwarf_hasattr(die, DW_AT_inline, &inl, &__dw_error);
197 ERR_IF(ret == DW_DLV_ERROR);
198 return inl;
199}
200
201/* Get the offset of abstruct_origin */
202static Dwarf_Off die_get_abstract_origin(Dwarf_Die die)
203{
204 Dwarf_Attribute attr;
205 Dwarf_Off cu_offs;
206 int ret;
207
208 ret = dwarf_attr(die, DW_AT_abstract_origin, &attr, &__dw_error);
209 ERR_IF(ret != DW_DLV_OK);
210 ret = dwarf_formref(attr, &cu_offs, &__dw_error);
211 ERR_IF(ret != DW_DLV_OK);
212 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
213 return cu_offs;
214}
215
216/* Get entry pc(or low pc, 1st entry of ranges) of the die */
217static Dwarf_Addr die_get_entrypc(Dwarf_Die die)
218{
219 Dwarf_Attribute attr;
220 Dwarf_Addr addr;
221 Dwarf_Off offs;
222 Dwarf_Ranges *ranges;
223 Dwarf_Signed cnt;
224 int ret;
225
226 /* Try to get entry pc */
227 ret = dwarf_attr(die, DW_AT_entry_pc, &attr, &__dw_error);
228 ERR_IF(ret == DW_DLV_ERROR);
229 if (ret == DW_DLV_OK) {
230 ret = dwarf_formaddr(attr, &addr, &__dw_error);
231 ERR_IF(ret != DW_DLV_OK);
232 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
233 return addr;
234 }
235
236 /* Try to get low pc */
237 ret = dwarf_lowpc(die, &addr, &__dw_error);
238 ERR_IF(ret == DW_DLV_ERROR);
239 if (ret == DW_DLV_OK)
240 return addr;
241
242 /* Try to get ranges */
243 ret = dwarf_attr(die, DW_AT_ranges, &attr, &__dw_error);
244 ERR_IF(ret != DW_DLV_OK);
245 ret = dwarf_formref(attr, &offs, &__dw_error);
246 ERR_IF(ret != DW_DLV_OK);
247 ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL,
248 &__dw_error);
249 ERR_IF(ret != DW_DLV_OK);
250 addr = ranges[0].dwr_addr1;
251 dwarf_ranges_dealloc(__dw_debug, ranges, cnt);
252 return addr;
253}
254
255/*
256 * Search a Die from Die tree.
257 * Note: cur_link->die should be deallocated in this function.
258 */
259static int __search_die_tree(struct die_link *cur_link,
260 int (*die_cb)(struct die_link *, void *),
261 void *data)
262{
263 Dwarf_Die new_die;
264 struct die_link new_link;
265 int ret;
266
267 if (!die_cb)
268 return 0;
269
270 /* Check current die */
271 while (!(ret = die_cb(cur_link, data))) {
272 /* Check child die */
273 ret = dwarf_child(cur_link->die, &new_die, &__dw_error);
274 ERR_IF(ret == DW_DLV_ERROR);
275 if (ret == DW_DLV_OK) {
276 new_link.parent = cur_link;
277 new_link.die = new_die;
278 ret = __search_die_tree(&new_link, die_cb, data);
279 if (ret)
280 break;
281 }
282
283 /* Move to next sibling */
284 ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die,
285 &__dw_error);
286 ERR_IF(ret == DW_DLV_ERROR);
287 dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
288 cur_link->die = new_die;
289 if (ret == DW_DLV_NO_ENTRY)
290 return 0;
291 }
292 dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
293 return ret;
294}
295
296/* Search a die in its children's die tree */
297static int search_die_from_children(Dwarf_Die parent_die,
298 int (*die_cb)(struct die_link *, void *),
299 void *data)
300{
301 struct die_link new_link;
302 int ret;
303
304 new_link.parent = NULL;
305 ret = dwarf_child(parent_die, &new_link.die, &__dw_error);
306 ERR_IF(ret == DW_DLV_ERROR);
307 if (ret == DW_DLV_OK)
308 return __search_die_tree(&new_link, die_cb, data);
309 else
310 return 0;
311}
312
313/* Find a locdesc corresponding to the address */
314static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
315 Dwarf_Addr addr)
316{
317 Dwarf_Signed lcnt;
318 Dwarf_Locdesc **llbuf;
319 int ret, i;
320
321 ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error);
322 ERR_IF(ret != DW_DLV_OK);
323 ret = DW_DLV_NO_ENTRY;
324 for (i = 0; i < lcnt; ++i) {
325 if (llbuf[i]->ld_lopc <= addr &&
326 llbuf[i]->ld_hipc > addr) {
327 memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
328 desc->ld_s =
329 malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
330 ERR_IF(desc->ld_s == NULL);
331 memcpy(desc->ld_s, llbuf[i]->ld_s,
332 sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
333 ret = DW_DLV_OK;
334 break;
335 }
336 dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
337 dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
338 }
339 /* Releasing loop */
340 for (; i < lcnt; ++i) {
341 dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
342 dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
343 }
344 dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST);
345 return ret;
346}
347
348/*
349 * Probe finder related functions
350 */
351
352/* Show a location */
353static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
354{
355 Dwarf_Small op;
356 Dwarf_Unsigned regn;
357 Dwarf_Signed offs;
358 int deref = 0, ret;
359 const char *regs;
360
361 op = loc->lr_atom;
362
363 /* If this is based on frame buffer, set the offset */
364 if (op == DW_OP_fbreg) {
365 deref = 1;
366 offs = (Dwarf_Signed)loc->lr_number;
367 op = pf->fbloc.ld_s[0].lr_atom;
368 loc = &pf->fbloc.ld_s[0];
369 } else
370 offs = 0;
371
372 if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
373 regn = op - DW_OP_breg0;
374 offs += (Dwarf_Signed)loc->lr_number;
375 deref = 1;
376 } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
377 regn = op - DW_OP_reg0;
378 } else if (op == DW_OP_bregx) {
379 regn = loc->lr_number;
380 offs += (Dwarf_Signed)loc->lr_number2;
381 deref = 1;
382 } else if (op == DW_OP_regx) {
383 regn = loc->lr_number;
384 } else
385 msg_exit(-EINVAL, "Dwarf_OP %d is not supported.\n", op);
386
387 regs = get_arch_regstr(regn);
388 if (!regs)
389 msg_exit(-EINVAL, "%lld exceeds max register number.\n", regn);
390
391 if (deref)
392 ret = snprintf(pf->buf, pf->len,
393 " %s=%+lld(%s)", pf->var, offs, regs);
394 else
395 ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs);
396 ERR_IF(ret < 0);
397 ERR_IF(ret >= pf->len);
398}
399
400/* Show a variables in kprobe event format */
401static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
402{
403 Dwarf_Attribute attr;
404 Dwarf_Locdesc ld;
405 int ret;
406
407 ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error);
408 if (ret != DW_DLV_OK)
409 goto error;
410 ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
411 if (ret != DW_DLV_OK)
412 goto error;
413 /* TODO? */
414 ERR_IF(ld.ld_cents != 1);
415 show_location(&ld.ld_s[0], pf);
416 free(ld.ld_s);
417 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
418 return ;
419error:
420 msg_exit(-1, "Failed to find the location of %s at this address.\n"
421 " Perhaps, it was optimized out.\n", pf->var);
422}
423
424static int variable_callback(struct die_link *dlink, void *data)
425{
426 struct probe_finder *pf = (struct probe_finder *)data;
427 Dwarf_Half tag;
428 int ret;
429
430 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
431 ERR_IF(ret == DW_DLV_ERROR);
432 if ((tag == DW_TAG_formal_parameter ||
433 tag == DW_TAG_variable) &&
434 (die_compare_name(dlink->die, pf->var) == 0)) {
435 show_variable(dlink->die, pf);
436 return 1;
437 }
438 /* TODO: Support struct members and arrays */
439 return 0;
440}
441
442/* Find a variable in a subprogram die */
443static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
444{
445 int ret;
446
447 if (!is_c_varname(pf->var)) {
448 /* Output raw parameters */
449 ret = snprintf(pf->buf, pf->len, " %s", pf->var);
450 ERR_IF(ret < 0);
451 ERR_IF(ret >= pf->len);
452 return ;
453 }
454
455 debug("Searching '%s' variable in context.\n", pf->var);
456 /* Search child die for local variables and parameters. */
457 ret = search_die_from_children(sp_die, variable_callback, pf);
458 if (!ret)
459 msg_exit(-1, "Failed to find '%s' in this function.\n",
460 pf->var);
461}
462
463/* Get a frame base on the address */
464static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
465{
466 Dwarf_Attribute attr;
467 int ret;
468
469 ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error);
470 ERR_IF(ret != DW_DLV_OK);
471 ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
472 ERR_IF(ret != DW_DLV_OK);
473 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
474}
475
476static void free_current_frame_base(struct probe_finder *pf)
477{
478 free(pf->fbloc.ld_s);
479 memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
480}
481
482/* Show a probe point to output buffer */
483static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
484 struct probe_finder *pf)
485{
486 struct probe_point *pp = pf->pp;
487 char *name;
488 char tmp[MAX_PROBE_BUFFER];
489 int ret, i, len;
490
491 /* Output name of probe point */
492 ret = dwarf_diename(sp_die, &name, &__dw_error);
493 ERR_IF(ret == DW_DLV_ERROR);
494 if (ret == DW_DLV_OK) {
495 ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
496 (unsigned int)offs);
497 dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
498 } else {
499 /* This function has no name. */
500 ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr);
501 }
502 ERR_IF(ret < 0);
503 ERR_IF(ret >= MAX_PROBE_BUFFER);
504 len = ret;
505
506 /* Find each argument */
507 get_current_frame_base(sp_die, pf);
508 for (i = 0; i < pp->nr_args; i++) {
509 pf->var = pp->args[i];
510 pf->buf = &tmp[len];
511 pf->len = MAX_PROBE_BUFFER - len;
512 find_variable(sp_die, pf);
513 len += strlen(pf->buf);
514 }
515 free_current_frame_base(pf);
516
517 pp->probes[pp->found] = strdup(tmp);
518 pp->found++;
519}
520
521static int probeaddr_callback(struct die_link *dlink, void *data)
522{
523 struct probe_finder *pf = (struct probe_finder *)data;
524 Dwarf_Half tag;
525 Dwarf_Signed offs;
526 int ret;
527
528 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
529 ERR_IF(ret == DW_DLV_ERROR);
530 /* Check the address is in this subprogram */
531 if (tag == DW_TAG_subprogram &&
532 die_within_subprogram(dlink->die, pf->addr, &offs)) {
533 show_probepoint(dlink->die, offs, pf);
534 return 1;
535 }
536 return 0;
537}
538
539/* Find probe point from its line number */
540static void find_by_line(Dwarf_Die cu_die, struct probe_finder *pf)
541{
542 struct probe_point *pp = pf->pp;
543 Dwarf_Signed cnt, i;
544 Dwarf_Line *lines;
545 Dwarf_Unsigned lineno = 0;
546 Dwarf_Addr addr;
547 Dwarf_Unsigned fno;
548 int ret;
549
550 ret = dwarf_srclines(cu_die, &lines, &cnt, &__dw_error);
551 ERR_IF(ret != DW_DLV_OK);
552
553 for (i = 0; i < cnt; i++) {
554 ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
555 ERR_IF(ret != DW_DLV_OK);
556 if (fno != pf->fno)
557 continue;
558
559 ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
560 ERR_IF(ret != DW_DLV_OK);
561 if (lineno != (Dwarf_Unsigned)pp->line)
562 continue;
563
564 ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
565 ERR_IF(ret != DW_DLV_OK);
566 debug("Probe point found: 0x%llx\n", addr);
567 pf->addr = addr;
568 /* Search a real subprogram including this line, */
569 ret = search_die_from_children(cu_die, probeaddr_callback, pf);
570 if (ret == 0)
571 msg_exit(-1,
572 "Probe point is not found in subprograms.\n");
573 /* Continuing, because target line might be inlined. */
574 }
575 dwarf_srclines_dealloc(__dw_debug, lines, cnt);
576}
577
578/* Search function from function name */
579static int probefunc_callback(struct die_link *dlink, void *data)
580{
581 struct probe_finder *pf = (struct probe_finder *)data;
582 struct probe_point *pp = pf->pp;
583 struct die_link *lk;
584 Dwarf_Signed offs;
585 Dwarf_Half tag;
586 int ret;
587
588 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
589 ERR_IF(ret == DW_DLV_ERROR);
590 if (tag == DW_TAG_subprogram) {
591 if (die_compare_name(dlink->die, pp->function) == 0) {
592 if (die_inlined_subprogram(dlink->die)) {
593 /* Inlined function, save it. */
594 ret = dwarf_die_CU_offset(dlink->die,
595 &pf->inl_offs,
596 &__dw_error);
597 ERR_IF(ret != DW_DLV_OK);
598 debug("inline definition offset %lld\n",
599 pf->inl_offs);
600 return 0;
601 }
602 /* Get probe address */
603 pf->addr = die_get_entrypc(dlink->die);
604 pf->addr += pp->offset;
605 /* TODO: Check the address in this function */
606 show_probepoint(dlink->die, pp->offset, pf);
607 /* Continue to search */
608 }
609 } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
610 if (die_get_abstract_origin(dlink->die) == pf->inl_offs) {
611 /* Get probe address */
612 pf->addr = die_get_entrypc(dlink->die);
613 pf->addr += pp->offset;
614 debug("found inline addr: 0x%llx\n", pf->addr);
615 /* Inlined function. Get a real subprogram */
616 for (lk = dlink->parent; lk != NULL; lk = lk->parent) {
617 tag = 0;
618 dwarf_tag(lk->die, &tag, &__dw_error);
619 ERR_IF(ret == DW_DLV_ERROR);
620 if (tag == DW_TAG_subprogram &&
621 !die_inlined_subprogram(lk->die))
622 goto found;
623 }
624 msg_exit(-1, "Failed to find real subprogram.\n");
625found:
626 /* Get offset from subprogram */
627 ret = die_within_subprogram(lk->die, pf->addr, &offs);
628 ERR_IF(!ret);
629 show_probepoint(lk->die, offs, pf);
630 /* Continue to search */
631 }
632 }
633 return 0;
634}
635
636static void find_by_func(Dwarf_Die cu_die, struct probe_finder *pf)
637{
638 search_die_from_children(cu_die, probefunc_callback, pf);
639}
640
641/* Find a probe point */
642int find_probepoint(int fd, struct probe_point *pp)
643{
644 Dwarf_Half addr_size = 0;
645 Dwarf_Unsigned next_cuh = 0;
646 Dwarf_Die cu_die = 0;
647 int cu_number = 0, ret;
648 struct probe_finder pf = {.pp = pp};
649
650 ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
651 if (ret != DW_DLV_OK)
652 msg_exit(-1, "Failed to call dwarf_init(). "
653 "Maybe, not a dwarf file?\n");
654
655 pp->found = 0;
656 while (++cu_number) {
657 /* Search CU (Compilation Unit) */
658 ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
659 &addr_size, &next_cuh, &__dw_error);
660 ERR_IF(ret == DW_DLV_ERROR);
661 if (ret == DW_DLV_NO_ENTRY)
662 break;
663
664 /* Get the DIE(Debugging Information Entry) of this CU */
665 ret = dwarf_siblingof(__dw_debug, 0, &cu_die, &__dw_error);
666 ERR_IF(ret != DW_DLV_OK);
667
668 /* Check if target file is included. */
669 if (pp->file)
670 pf.fno = die_get_fileno(cu_die, pp->file);
671
672 if (!pp->file || pf.fno) {
673 /* Save CU base address (for frame_base) */
674 ret = dwarf_lowpc(cu_die, &pf.cu_base, &__dw_error);
675 ERR_IF(ret == DW_DLV_ERROR);
676 if (ret == DW_DLV_NO_ENTRY)
677 pf.cu_base = 0;
678 if (pp->line)
679 find_by_line(cu_die, &pf);
680 if (pp->function)
681 find_by_func(cu_die, &pf);
682 }
683 dwarf_dealloc(__dw_debug, cu_die, DW_DLA_DIE);
684 }
685 ret = dwarf_finish(__dw_debug, &__dw_error);
686 ERR_IF(ret != DW_DLV_OK);
687
688 return pp->found;
689}
690