perf probe: Rename probe finder functions
[linux-2.6-block.git] / tools / perf / util / probe-finder.c
CommitLineData
4ea42b18
MH
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>
074fc0e4 34
89c69c0e
MH
35#include "event.h"
36#include "debug.h"
074fc0e4 37#include "util.h"
4ea42b18
MH
38#include "probe-finder.h"
39
40
41/* Dwarf_Die Linkage to parent Die */
42struct die_link {
43 struct die_link *parent; /* Parent die */
44 Dwarf_Die die; /* Current die */
45};
46
47static Dwarf_Debug __dw_debug;
48static Dwarf_Error __dw_error;
49
4ea42b18
MH
50/*
51 * Generic dwarf analysis helpers
52 */
53
54#define X86_32_MAX_REGS 8
55const char *x86_32_regs_table[X86_32_MAX_REGS] = {
56 "%ax",
57 "%cx",
58 "%dx",
59 "%bx",
60 "$stack", /* Stack address instead of %sp */
61 "%bp",
62 "%si",
63 "%di",
64};
65
66#define X86_64_MAX_REGS 16
67const char *x86_64_regs_table[X86_64_MAX_REGS] = {
68 "%ax",
69 "%dx",
70 "%cx",
71 "%bx",
72 "%si",
73 "%di",
74 "%bp",
75 "%sp",
76 "%r8",
77 "%r9",
78 "%r10",
79 "%r11",
80 "%r12",
81 "%r13",
82 "%r14",
83 "%r15",
84};
85
86/* TODO: switching by dwarf address size */
87#ifdef __x86_64__
88#define ARCH_MAX_REGS X86_64_MAX_REGS
89#define arch_regs_table x86_64_regs_table
90#else
91#define ARCH_MAX_REGS X86_32_MAX_REGS
92#define arch_regs_table x86_32_regs_table
93#endif
94
95/* Return architecture dependent register string (for kprobe-tracer) */
96static const char *get_arch_regstr(unsigned int n)
97{
98 return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
99}
100
101/*
102 * Compare the tail of two strings.
103 * Return 0 if whole of either string is same as another's tail part.
104 */
105static int strtailcmp(const char *s1, const char *s2)
106{
107 int i1 = strlen(s1);
108 int i2 = strlen(s2);
d56728b8 109 while (--i1 >= 0 && --i2 >= 0) {
4ea42b18
MH
110 if (s1[i1] != s2[i2])
111 return s1[i1] - s2[i2];
112 }
113 return 0;
114}
115
116/* Find the fileno of the target file. */
b0ef0732 117static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
4ea42b18
MH
118{
119 Dwarf_Signed cnt, i;
120 Dwarf_Unsigned found = 0;
121 char **srcs;
122 int ret;
123
124 if (!fname)
125 return 0;
126
127 ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
128 if (ret == DW_DLV_OK) {
129 for (i = 0; i < cnt && !found; i++) {
130 if (strtailcmp(srcs[i], fname) == 0)
131 found = i + 1;
132 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
133 }
134 for (; i < cnt; i++)
135 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
136 dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
137 }
138 if (found)
b7cb10e7 139 pr_debug("found fno: %d\n", (int)found);
4ea42b18
MH
140 return found;
141}
142
631c9def
MH
143static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf)
144{
145 Dwarf_Signed cnt, i;
146 char **srcs;
147 int ret = 0;
148
149 if (!buf || !fno)
150 return -EINVAL;
151
152 ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
153 if (ret == DW_DLV_OK) {
154 if ((Dwarf_Unsigned)cnt > fno - 1) {
155 *buf = strdup(srcs[fno - 1]);
156 ret = 0;
157 pr_debug("found filename: %s\n", *buf);
158 } else
159 ret = -ENOENT;
160 for (i = 0; i < cnt; i++)
161 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
162 dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
163 } else
164 ret = -EINVAL;
165 return ret;
166}
167
4ea42b18 168/* Compare diename and tname */
074fc0e4 169static int die_compare_name(Dwarf_Die dw_die, const char *tname)
4ea42b18
MH
170{
171 char *name;
172 int ret;
074fc0e4 173 ret = dwarf_diename(dw_die, &name, &__dw_error);
9769833b 174 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
175 if (ret == DW_DLV_OK) {
176 ret = strcmp(tname, name);
177 dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
178 } else
179 ret = -1;
180 return ret;
181}
182
183/* Check the address is in the subprogram(function). */
184static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
185 Dwarf_Signed *offs)
186{
187 Dwarf_Addr lopc, hipc;
188 int ret;
189
190 /* TODO: check ranges */
191 ret = dwarf_lowpc(sp_die, &lopc, &__dw_error);
9769833b 192 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
193 if (ret == DW_DLV_NO_ENTRY)
194 return 0;
195 ret = dwarf_highpc(sp_die, &hipc, &__dw_error);
9769833b 196 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
197 if (lopc <= addr && addr < hipc) {
198 *offs = addr - lopc;
199 return 1;
200 } else
201 return 0;
202}
203
204/* Check the die is inlined function */
074fc0e4 205static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die)
4ea42b18
MH
206{
207 /* TODO: check strictly */
208 Dwarf_Bool inl;
209 int ret;
210
074fc0e4 211 ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error);
9769833b 212 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
213 return inl;
214}
215
216/* Get the offset of abstruct_origin */
074fc0e4 217static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die)
4ea42b18
MH
218{
219 Dwarf_Attribute attr;
220 Dwarf_Off cu_offs;
221 int ret;
222
074fc0e4 223 ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error);
9769833b 224 DIE_IF(ret != DW_DLV_OK);
4ea42b18 225 ret = dwarf_formref(attr, &cu_offs, &__dw_error);
9769833b 226 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
227 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
228 return cu_offs;
229}
230
231/* Get entry pc(or low pc, 1st entry of ranges) of the die */
074fc0e4 232static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die)
4ea42b18
MH
233{
234 Dwarf_Attribute attr;
235 Dwarf_Addr addr;
236 Dwarf_Off offs;
237 Dwarf_Ranges *ranges;
238 Dwarf_Signed cnt;
239 int ret;
240
241 /* Try to get entry pc */
074fc0e4 242 ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error);
9769833b 243 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
244 if (ret == DW_DLV_OK) {
245 ret = dwarf_formaddr(attr, &addr, &__dw_error);
9769833b 246 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
247 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
248 return addr;
249 }
250
251 /* Try to get low pc */
074fc0e4 252 ret = dwarf_lowpc(dw_die, &addr, &__dw_error);
9769833b 253 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
254 if (ret == DW_DLV_OK)
255 return addr;
256
257 /* Try to get ranges */
074fc0e4 258 ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error);
9769833b 259 DIE_IF(ret != DW_DLV_OK);
4ea42b18 260 ret = dwarf_formref(attr, &offs, &__dw_error);
9769833b 261 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
262 ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL,
263 &__dw_error);
9769833b 264 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
265 addr = ranges[0].dwr_addr1;
266 dwarf_ranges_dealloc(__dw_debug, ranges, cnt);
267 return addr;
268}
269
270/*
271 * Search a Die from Die tree.
272 * Note: cur_link->die should be deallocated in this function.
273 */
274static int __search_die_tree(struct die_link *cur_link,
275 int (*die_cb)(struct die_link *, void *),
276 void *data)
277{
278 Dwarf_Die new_die;
279 struct die_link new_link;
280 int ret;
281
282 if (!die_cb)
283 return 0;
284
285 /* Check current die */
286 while (!(ret = die_cb(cur_link, data))) {
287 /* Check child die */
288 ret = dwarf_child(cur_link->die, &new_die, &__dw_error);
9769833b 289 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
290 if (ret == DW_DLV_OK) {
291 new_link.parent = cur_link;
292 new_link.die = new_die;
293 ret = __search_die_tree(&new_link, die_cb, data);
294 if (ret)
295 break;
296 }
297
298 /* Move to next sibling */
299 ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die,
300 &__dw_error);
9769833b 301 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
302 dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
303 cur_link->die = new_die;
304 if (ret == DW_DLV_NO_ENTRY)
305 return 0;
306 }
307 dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
308 return ret;
309}
310
311/* Search a die in its children's die tree */
312static int search_die_from_children(Dwarf_Die parent_die,
313 int (*die_cb)(struct die_link *, void *),
314 void *data)
315{
316 struct die_link new_link;
317 int ret;
318
319 new_link.parent = NULL;
320 ret = dwarf_child(parent_die, &new_link.die, &__dw_error);
9769833b 321 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
322 if (ret == DW_DLV_OK)
323 return __search_die_tree(&new_link, die_cb, data);
324 else
325 return 0;
326}
327
328/* Find a locdesc corresponding to the address */
329static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
330 Dwarf_Addr addr)
331{
332 Dwarf_Signed lcnt;
333 Dwarf_Locdesc **llbuf;
334 int ret, i;
335
336 ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error);
9769833b 337 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
338 ret = DW_DLV_NO_ENTRY;
339 for (i = 0; i < lcnt; ++i) {
340 if (llbuf[i]->ld_lopc <= addr &&
341 llbuf[i]->ld_hipc > addr) {
342 memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
343 desc->ld_s =
344 malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
9769833b 345 DIE_IF(desc->ld_s == NULL);
4ea42b18
MH
346 memcpy(desc->ld_s, llbuf[i]->ld_s,
347 sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
348 ret = DW_DLV_OK;
349 break;
350 }
351 dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
352 dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
353 }
354 /* Releasing loop */
355 for (; i < lcnt; ++i) {
356 dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
357 dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
358 }
359 dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST);
360 return ret;
361}
362
b0ef0732
MH
363/* Get decl_file attribute value (file number) */
364static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die)
365{
366 Dwarf_Attribute attr;
367 Dwarf_Unsigned fno;
368 int ret;
369
370 ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error);
371 DIE_IF(ret != DW_DLV_OK);
372 dwarf_formudata(attr, &fno, &__dw_error);
373 DIE_IF(ret != DW_DLV_OK);
374 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
375 return fno;
376}
377
378/* Get decl_line attribute value (line number) */
379static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die)
380{
381 Dwarf_Attribute attr;
382 Dwarf_Unsigned lno;
383 int ret;
384
385 ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error);
386 DIE_IF(ret != DW_DLV_OK);
387 dwarf_formudata(attr, &lno, &__dw_error);
388 DIE_IF(ret != DW_DLV_OK);
389 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
390 return lno;
391}
392
4ea42b18
MH
393/*
394 * Probe finder related functions
395 */
396
397/* Show a location */
398static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
399{
400 Dwarf_Small op;
401 Dwarf_Unsigned regn;
402 Dwarf_Signed offs;
403 int deref = 0, ret;
404 const char *regs;
405
406 op = loc->lr_atom;
407
408 /* If this is based on frame buffer, set the offset */
409 if (op == DW_OP_fbreg) {
410 deref = 1;
411 offs = (Dwarf_Signed)loc->lr_number;
412 op = pf->fbloc.ld_s[0].lr_atom;
413 loc = &pf->fbloc.ld_s[0];
414 } else
415 offs = 0;
416
417 if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
418 regn = op - DW_OP_breg0;
419 offs += (Dwarf_Signed)loc->lr_number;
420 deref = 1;
421 } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
422 regn = op - DW_OP_reg0;
423 } else if (op == DW_OP_bregx) {
424 regn = loc->lr_number;
425 offs += (Dwarf_Signed)loc->lr_number2;
426 deref = 1;
427 } else if (op == DW_OP_regx) {
428 regn = loc->lr_number;
429 } else
bbaa46fa 430 die("Dwarf_OP %d is not supported.", op);
4ea42b18
MH
431
432 regs = get_arch_regstr(regn);
433 if (!regs)
bbaa46fa 434 die("%lld exceeds max register number.", regn);
4ea42b18
MH
435
436 if (deref)
437 ret = snprintf(pf->buf, pf->len,
438 " %s=%+lld(%s)", pf->var, offs, regs);
439 else
440 ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs);
9769833b
MH
441 DIE_IF(ret < 0);
442 DIE_IF(ret >= pf->len);
4ea42b18
MH
443}
444
445/* Show a variables in kprobe event format */
446static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
447{
448 Dwarf_Attribute attr;
449 Dwarf_Locdesc ld;
450 int ret;
451
452 ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error);
453 if (ret != DW_DLV_OK)
454 goto error;
455 ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
456 if (ret != DW_DLV_OK)
457 goto error;
458 /* TODO? */
9769833b 459 DIE_IF(ld.ld_cents != 1);
4ea42b18
MH
460 show_location(&ld.ld_s[0], pf);
461 free(ld.ld_s);
462 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
463 return ;
464error:
074fc0e4 465 die("Failed to find the location of %s at this address.\n"
bbaa46fa 466 " Perhaps, it has been optimized out.", pf->var);
4ea42b18
MH
467}
468
469static int variable_callback(struct die_link *dlink, void *data)
470{
471 struct probe_finder *pf = (struct probe_finder *)data;
472 Dwarf_Half tag;
473 int ret;
474
475 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
9769833b 476 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
477 if ((tag == DW_TAG_formal_parameter ||
478 tag == DW_TAG_variable) &&
479 (die_compare_name(dlink->die, pf->var) == 0)) {
480 show_variable(dlink->die, pf);
481 return 1;
482 }
483 /* TODO: Support struct members and arrays */
484 return 0;
485}
486
487/* Find a variable in a subprogram die */
488static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
489{
490 int ret;
491
492 if (!is_c_varname(pf->var)) {
493 /* Output raw parameters */
494 ret = snprintf(pf->buf, pf->len, " %s", pf->var);
9769833b
MH
495 DIE_IF(ret < 0);
496 DIE_IF(ret >= pf->len);
4ea42b18
MH
497 return ;
498 }
499
b7cb10e7 500 pr_debug("Searching '%s' variable in context.\n", pf->var);
4ea42b18
MH
501 /* Search child die for local variables and parameters. */
502 ret = search_die_from_children(sp_die, variable_callback, pf);
503 if (!ret)
bbaa46fa 504 die("Failed to find '%s' in this function.", pf->var);
4ea42b18
MH
505}
506
507/* Get a frame base on the address */
508static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
509{
510 Dwarf_Attribute attr;
511 int ret;
512
513 ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error);
9769833b 514 DIE_IF(ret != DW_DLV_OK);
4ea42b18 515 ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
9769833b 516 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
517 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
518}
519
520static void free_current_frame_base(struct probe_finder *pf)
521{
522 free(pf->fbloc.ld_s);
523 memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
524}
525
526/* Show a probe point to output buffer */
81cb8aa3
MH
527static void show_probe_point(Dwarf_Die sp_die, Dwarf_Signed offs,
528 struct probe_finder *pf)
4ea42b18
MH
529{
530 struct probe_point *pp = pf->pp;
531 char *name;
532 char tmp[MAX_PROBE_BUFFER];
533 int ret, i, len;
534
535 /* Output name of probe point */
536 ret = dwarf_diename(sp_die, &name, &__dw_error);
9769833b 537 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
538 if (ret == DW_DLV_OK) {
539 ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
540 (unsigned int)offs);
253977b0
MH
541 /* Copy the function name if possible */
542 if (!pp->function) {
543 pp->function = strdup(name);
544 pp->offset = offs;
545 }
4ea42b18
MH
546 dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
547 } else {
548 /* This function has no name. */
549 ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr);
253977b0
MH
550 if (!pp->function) {
551 /* TODO: Use _stext */
552 pp->function = strdup("");
553 pp->offset = (int)pf->addr;
554 }
4ea42b18 555 }
9769833b
MH
556 DIE_IF(ret < 0);
557 DIE_IF(ret >= MAX_PROBE_BUFFER);
4ea42b18 558 len = ret;
b0ef0732 559 pr_debug("Probe point found: %s\n", tmp);
4ea42b18
MH
560
561 /* Find each argument */
562 get_current_frame_base(sp_die, pf);
563 for (i = 0; i < pp->nr_args; i++) {
564 pf->var = pp->args[i];
565 pf->buf = &tmp[len];
566 pf->len = MAX_PROBE_BUFFER - len;
567 find_variable(sp_die, pf);
568 len += strlen(pf->buf);
569 }
570 free_current_frame_base(pf);
571
572 pp->probes[pp->found] = strdup(tmp);
573 pp->found++;
574}
575
576static int probeaddr_callback(struct die_link *dlink, void *data)
577{
578 struct probe_finder *pf = (struct probe_finder *)data;
579 Dwarf_Half tag;
580 Dwarf_Signed offs;
581 int ret;
582
583 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
9769833b 584 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
585 /* Check the address is in this subprogram */
586 if (tag == DW_TAG_subprogram &&
587 die_within_subprogram(dlink->die, pf->addr, &offs)) {
81cb8aa3 588 show_probe_point(dlink->die, offs, pf);
4ea42b18
MH
589 return 1;
590 }
591 return 0;
592}
593
594/* Find probe point from its line number */
631c9def 595static void find_probe_point_by_line(struct probe_finder *pf)
4ea42b18 596{
b0ef0732 597 Dwarf_Signed cnt, i, clm;
4ea42b18
MH
598 Dwarf_Line *lines;
599 Dwarf_Unsigned lineno = 0;
600 Dwarf_Addr addr;
601 Dwarf_Unsigned fno;
602 int ret;
603
b0ef0732 604 ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error);
9769833b 605 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
606
607 for (i = 0; i < cnt; i++) {
608 ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
9769833b 609 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
610 if (fno != pf->fno)
611 continue;
612
613 ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
9769833b 614 DIE_IF(ret != DW_DLV_OK);
b0ef0732 615 if (lineno != pf->lno)
4ea42b18
MH
616 continue;
617
b0ef0732
MH
618 ret = dwarf_lineoff(lines[i], &clm, &__dw_error);
619 DIE_IF(ret != DW_DLV_OK);
620
4ea42b18 621 ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
9769833b 622 DIE_IF(ret != DW_DLV_OK);
b0ef0732
MH
623 pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n",
624 (int)i, (unsigned)lineno, (int)clm, addr);
4ea42b18
MH
625 pf->addr = addr;
626 /* Search a real subprogram including this line, */
b0ef0732
MH
627 ret = search_die_from_children(pf->cu_die,
628 probeaddr_callback, pf);
4ea42b18 629 if (ret == 0)
bbaa46fa 630 die("Probe point is not found in subprograms.");
4ea42b18
MH
631 /* Continuing, because target line might be inlined. */
632 }
633 dwarf_srclines_dealloc(__dw_debug, lines, cnt);
634}
635
636/* Search function from function name */
637static int probefunc_callback(struct die_link *dlink, void *data)
638{
639 struct probe_finder *pf = (struct probe_finder *)data;
640 struct probe_point *pp = pf->pp;
641 struct die_link *lk;
642 Dwarf_Signed offs;
643 Dwarf_Half tag;
644 int ret;
645
646 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
9769833b 647 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
648 if (tag == DW_TAG_subprogram) {
649 if (die_compare_name(dlink->die, pp->function) == 0) {
b0ef0732
MH
650 if (pp->line) { /* Function relative line */
651 pf->fno = die_get_decl_file(dlink->die);
652 pf->lno = die_get_decl_line(dlink->die)
653 + pp->line;
631c9def 654 find_probe_point_by_line(pf);
b0ef0732
MH
655 return 1;
656 }
4ea42b18
MH
657 if (die_inlined_subprogram(dlink->die)) {
658 /* Inlined function, save it. */
659 ret = dwarf_die_CU_offset(dlink->die,
660 &pf->inl_offs,
661 &__dw_error);
9769833b 662 DIE_IF(ret != DW_DLV_OK);
b7cb10e7
ACM
663 pr_debug("inline definition offset %lld\n",
664 pf->inl_offs);
8030c5f5 665 return 0; /* Continue to search */
4ea42b18
MH
666 }
667 /* Get probe address */
668 pf->addr = die_get_entrypc(dlink->die);
669 pf->addr += pp->offset;
670 /* TODO: Check the address in this function */
81cb8aa3 671 show_probe_point(dlink->die, pp->offset, pf);
8030c5f5 672 return 1; /* Exit; no same symbol in this CU. */
4ea42b18
MH
673 }
674 } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
675 if (die_get_abstract_origin(dlink->die) == pf->inl_offs) {
676 /* Get probe address */
677 pf->addr = die_get_entrypc(dlink->die);
678 pf->addr += pp->offset;
b7cb10e7 679 pr_debug("found inline addr: 0x%llx\n", pf->addr);
4ea42b18
MH
680 /* Inlined function. Get a real subprogram */
681 for (lk = dlink->parent; lk != NULL; lk = lk->parent) {
682 tag = 0;
683 dwarf_tag(lk->die, &tag, &__dw_error);
9769833b 684 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
685 if (tag == DW_TAG_subprogram &&
686 !die_inlined_subprogram(lk->die))
687 goto found;
688 }
bbaa46fa 689 die("Failed to find real subprogram.");
4ea42b18
MH
690found:
691 /* Get offset from subprogram */
692 ret = die_within_subprogram(lk->die, pf->addr, &offs);
9769833b 693 DIE_IF(!ret);
81cb8aa3 694 show_probe_point(lk->die, offs, pf);
4ea42b18
MH
695 /* Continue to search */
696 }
697 }
698 return 0;
699}
700
631c9def 701static void find_probe_point_by_func(struct probe_finder *pf)
4ea42b18 702{
b0ef0732 703 search_die_from_children(pf->cu_die, probefunc_callback, pf);
4ea42b18
MH
704}
705
706/* Find a probe point */
81cb8aa3 707int find_probe_point(int fd, struct probe_point *pp)
4ea42b18
MH
708{
709 Dwarf_Half addr_size = 0;
710 Dwarf_Unsigned next_cuh = 0;
4ea42b18
MH
711 int cu_number = 0, ret;
712 struct probe_finder pf = {.pp = pp};
713
714 ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
411edfe5 715 if (ret != DW_DLV_OK)
a225a1d9 716 return -ENOENT;
4ea42b18
MH
717
718 pp->found = 0;
719 while (++cu_number) {
720 /* Search CU (Compilation Unit) */
721 ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
722 &addr_size, &next_cuh, &__dw_error);
9769833b 723 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
724 if (ret == DW_DLV_NO_ENTRY)
725 break;
726
727 /* Get the DIE(Debugging Information Entry) of this CU */
b0ef0732 728 ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error);
9769833b 729 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
730
731 /* Check if target file is included. */
732 if (pp->file)
b0ef0732 733 pf.fno = cu_find_fileno(pf.cu_die, pp->file);
4ea42b18
MH
734
735 if (!pp->file || pf.fno) {
736 /* Save CU base address (for frame_base) */
b0ef0732 737 ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error);
9769833b 738 DIE_IF(ret == DW_DLV_ERROR);
4ea42b18
MH
739 if (ret == DW_DLV_NO_ENTRY)
740 pf.cu_base = 0;
4ea42b18 741 if (pp->function)
631c9def 742 find_probe_point_by_func(&pf);
b0ef0732
MH
743 else {
744 pf.lno = pp->line;
631c9def 745 find_probe_point_by_line(&pf);
b0ef0732 746 }
4ea42b18 747 }
b0ef0732 748 dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
4ea42b18
MH
749 }
750 ret = dwarf_finish(__dw_debug, &__dw_error);
9769833b 751 DIE_IF(ret != DW_DLV_OK);
4ea42b18
MH
752
753 return pp->found;
754}
755
631c9def
MH
756
757static void line_range_add_line(struct line_range *lr, unsigned int line)
758{
759 struct line_node *ln;
760 struct list_head *p;
761
762 /* Reverse search, because new line will be the last one */
763 list_for_each_entry_reverse(ln, &lr->line_list, list) {
764 if (ln->line < line) {
765 p = &ln->list;
766 goto found;
767 } else if (ln->line == line) /* Already exist */
768 return ;
769 }
770 /* List is empty, or the smallest entry */
771 p = &lr->line_list;
772found:
773 pr_debug("Debug: add a line %u\n", line);
774 ln = zalloc(sizeof(struct line_node));
775 DIE_IF(ln == NULL);
776 ln->line = line;
777 INIT_LIST_HEAD(&ln->list);
778 list_add(&ln->list, p);
779}
780
781/* Find line range from its line number */
782static void find_line_range_by_line(struct line_finder *lf)
783{
784 Dwarf_Signed cnt, i;
785 Dwarf_Line *lines;
786 Dwarf_Unsigned lineno = 0;
787 Dwarf_Unsigned fno;
788 Dwarf_Addr addr;
789 int ret;
790
3cb8bc6a 791 INIT_LIST_HEAD(&lf->lr->line_list);
631c9def
MH
792 ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
793 DIE_IF(ret != DW_DLV_OK);
794
795 for (i = 0; i < cnt; i++) {
796 ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
797 DIE_IF(ret != DW_DLV_OK);
798 if (fno != lf->fno)
799 continue;
800
801 ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
802 DIE_IF(ret != DW_DLV_OK);
803 if (lf->lno_s > lineno || lf->lno_e < lineno)
804 continue;
805
806 /* Filter line in the function address range */
807 if (lf->addr_s && lf->addr_e) {
808 ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
809 DIE_IF(ret != DW_DLV_OK);
810 if (lf->addr_s > addr || lf->addr_e <= addr)
811 continue;
812 }
813 line_range_add_line(lf->lr, (unsigned int)lineno);
814 }
815 dwarf_srclines_dealloc(__dw_debug, lines, cnt);
816 if (!list_empty(&lf->lr->line_list))
817 lf->found = 1;
818}
819
820/* Search function from function name */
821static int linefunc_callback(struct die_link *dlink, void *data)
822{
823 struct line_finder *lf = (struct line_finder *)data;
824 struct line_range *lr = lf->lr;
825 Dwarf_Half tag;
826 int ret;
827
828 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
829 DIE_IF(ret == DW_DLV_ERROR);
830 if (tag == DW_TAG_subprogram &&
831 die_compare_name(dlink->die, lr->function) == 0) {
832 /* Get the address range of this function */
833 ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
834 if (ret == DW_DLV_OK)
835 ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
836 DIE_IF(ret == DW_DLV_ERROR);
837 if (ret == DW_DLV_NO_ENTRY) {
838 lf->addr_s = 0;
839 lf->addr_e = 0;
840 }
841
842 lf->fno = die_get_decl_file(dlink->die);
843 lr->offset = die_get_decl_line(dlink->die);;
844 lf->lno_s = lr->offset + lr->start;
845 if (!lr->end)
846 lf->lno_e = (Dwarf_Unsigned)-1;
847 else
848 lf->lno_e = lr->offset + lr->end;
849 lr->start = lf->lno_s;
850 lr->end = lf->lno_e;
851 find_line_range_by_line(lf);
631c9def
MH
852 return 1;
853 }
854 return 0;
855}
856
857static void find_line_range_by_func(struct line_finder *lf)
858{
859 search_die_from_children(lf->cu_die, linefunc_callback, lf);
860}
861
862int find_line_range(int fd, struct line_range *lr)
863{
864 Dwarf_Half addr_size = 0;
865 Dwarf_Unsigned next_cuh = 0;
866 int ret;
867 struct line_finder lf = {.lr = lr};
868
869 ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
870 if (ret != DW_DLV_OK)
871 return -ENOENT;
872
873 while (!lf.found) {
874 /* Search CU (Compilation Unit) */
875 ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
876 &addr_size, &next_cuh, &__dw_error);
877 DIE_IF(ret == DW_DLV_ERROR);
878 if (ret == DW_DLV_NO_ENTRY)
879 break;
880
881 /* Get the DIE(Debugging Information Entry) of this CU */
882 ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
883 DIE_IF(ret != DW_DLV_OK);
884
885 /* Check if target file is included. */
886 if (lr->file)
887 lf.fno = cu_find_fileno(lf.cu_die, lr->file);
888
889 if (!lr->file || lf.fno) {
890 if (lr->function)
891 find_line_range_by_func(&lf);
892 else {
893 lf.lno_s = lr->start;
894 if (!lr->end)
895 lf.lno_e = (Dwarf_Unsigned)-1;
896 else
897 lf.lno_e = lr->end;
898 find_line_range_by_line(&lf);
899 }
900 /* Get the real file path */
901 if (lf.found)
902 cu_get_filename(lf.cu_die, lf.fno, &lr->path);
903 }
904 dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
905 }
906 ret = dwarf_finish(__dw_debug, &__dw_error);
907 DIE_IF(ret != DW_DLV_OK);
908 return lf.found;
909}
910