diff options
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 829 |
1 files changed, 829 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..c171a243d05b --- /dev/null +++ b/tools/perf/util/probe-finder.c | |||
@@ -0,0 +1,829 @@ | |||
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 | |||
35 | #include "string.h" | ||
36 | #include "event.h" | ||
37 | #include "debug.h" | ||
38 | #include "util.h" | ||
39 | #include "probe-finder.h" | ||
40 | |||
41 | |||
42 | /* | ||
43 | * Generic dwarf analysis helpers | ||
44 | */ | ||
45 | |||
46 | #define X86_32_MAX_REGS 8 | ||
47 | const char *x86_32_regs_table[X86_32_MAX_REGS] = { | ||
48 | "%ax", | ||
49 | "%cx", | ||
50 | "%dx", | ||
51 | "%bx", | ||
52 | "$stack", /* Stack address instead of %sp */ | ||
53 | "%bp", | ||
54 | "%si", | ||
55 | "%di", | ||
56 | }; | ||
57 | |||
58 | #define X86_64_MAX_REGS 16 | ||
59 | const char *x86_64_regs_table[X86_64_MAX_REGS] = { | ||
60 | "%ax", | ||
61 | "%dx", | ||
62 | "%cx", | ||
63 | "%bx", | ||
64 | "%si", | ||
65 | "%di", | ||
66 | "%bp", | ||
67 | "%sp", | ||
68 | "%r8", | ||
69 | "%r9", | ||
70 | "%r10", | ||
71 | "%r11", | ||
72 | "%r12", | ||
73 | "%r13", | ||
74 | "%r14", | ||
75 | "%r15", | ||
76 | }; | ||
77 | |||
78 | /* TODO: switching by dwarf address size */ | ||
79 | #ifdef __x86_64__ | ||
80 | #define ARCH_MAX_REGS X86_64_MAX_REGS | ||
81 | #define arch_regs_table x86_64_regs_table | ||
82 | #else | ||
83 | #define ARCH_MAX_REGS X86_32_MAX_REGS | ||
84 | #define arch_regs_table x86_32_regs_table | ||
85 | #endif | ||
86 | |||
87 | /* Return architecture dependent register string (for kprobe-tracer) */ | ||
88 | static const char *get_arch_regstr(unsigned int n) | ||
89 | { | ||
90 | return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Compare the tail of two strings. | ||
95 | * Return 0 if whole of either string is same as another's tail part. | ||
96 | */ | ||
97 | static int strtailcmp(const char *s1, const char *s2) | ||
98 | { | ||
99 | int i1 = strlen(s1); | ||
100 | int i2 = strlen(s2); | ||
101 | while (--i1 >= 0 && --i2 >= 0) { | ||
102 | if (s1[i1] != s2[i2]) | ||
103 | return s1[i1] - s2[i2]; | ||
104 | } | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* Line number list operations */ | ||
109 | |||
110 | /* Add a line to line number list */ | ||
111 | static void line_list__add_line(struct list_head *head, unsigned int line) | ||
112 | { | ||
113 | struct line_node *ln; | ||
114 | struct list_head *p; | ||
115 | |||
116 | /* Reverse search, because new line will be the last one */ | ||
117 | list_for_each_entry_reverse(ln, head, list) { | ||
118 | if (ln->line < line) { | ||
119 | p = &ln->list; | ||
120 | goto found; | ||
121 | } else if (ln->line == line) /* Already exist */ | ||
122 | return ; | ||
123 | } | ||
124 | /* List is empty, or the smallest entry */ | ||
125 | p = head; | ||
126 | found: | ||
127 | pr_debug("line list: add a line %u\n", line); | ||
128 | ln = zalloc(sizeof(struct line_node)); | ||
129 | DIE_IF(ln == NULL); | ||
130 | ln->line = line; | ||
131 | INIT_LIST_HEAD(&ln->list); | ||
132 | list_add(&ln->list, p); | ||
133 | } | ||
134 | |||
135 | /* Check if the line in line number list */ | ||
136 | static int line_list__has_line(struct list_head *head, unsigned int line) | ||
137 | { | ||
138 | struct line_node *ln; | ||
139 | |||
140 | /* Reverse search, because new line will be the last one */ | ||
141 | list_for_each_entry(ln, head, list) | ||
142 | if (ln->line == line) | ||
143 | return 1; | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* Init line number list */ | ||
149 | static void line_list__init(struct list_head *head) | ||
150 | { | ||
151 | INIT_LIST_HEAD(head); | ||
152 | } | ||
153 | |||
154 | /* Free line number list */ | ||
155 | static void line_list__free(struct list_head *head) | ||
156 | { | ||
157 | struct line_node *ln; | ||
158 | while (!list_empty(head)) { | ||
159 | ln = list_first_entry(head, struct line_node, list); | ||
160 | list_del(&ln->list); | ||
161 | free(ln); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /* Dwarf wrappers */ | ||
166 | |||
167 | /* Find the realpath of the target file. */ | ||
168 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
169 | { | ||
170 | Dwarf_Files *files; | ||
171 | size_t nfiles, i; | ||
172 | const char *src = NULL; | ||
173 | int ret; | ||
174 | |||
175 | if (!fname) | ||
176 | return NULL; | ||
177 | |||
178 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
179 | if (ret != 0) | ||
180 | return NULL; | ||
181 | |||
182 | for (i = 0; i < nfiles; i++) { | ||
183 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
184 | if (strtailcmp(src, fname) == 0) | ||
185 | break; | ||
186 | } | ||
187 | return src; | ||
188 | } | ||
189 | |||
190 | struct __addr_die_search_param { | ||
191 | Dwarf_Addr addr; | ||
192 | Dwarf_Die *die_mem; | ||
193 | }; | ||
194 | |||
195 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
196 | { | ||
197 | struct __addr_die_search_param *ad = data; | ||
198 | |||
199 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
200 | dwarf_haspc(fn_die, ad->addr)) { | ||
201 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
202 | return DWARF_CB_ABORT; | ||
203 | } | ||
204 | return DWARF_CB_OK; | ||
205 | } | ||
206 | |||
207 | /* Search a real subprogram including this line, */ | ||
208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
209 | Dwarf_Die *die_mem) | ||
210 | { | ||
211 | struct __addr_die_search_param ad; | ||
212 | ad.addr = addr; | ||
213 | ad.die_mem = die_mem; | ||
214 | /* dwarf_getscopes can't find subprogram. */ | ||
215 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
216 | return NULL; | ||
217 | else | ||
218 | return die_mem; | ||
219 | } | ||
220 | |||
221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | ||
222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
223 | Dwarf_Die *die_mem) | ||
224 | { | ||
225 | Dwarf_Die child_die; | ||
226 | int ret; | ||
227 | |||
228 | ret = dwarf_child(sp_die, die_mem); | ||
229 | if (ret != 0) | ||
230 | return NULL; | ||
231 | |||
232 | do { | ||
233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
234 | dwarf_haspc(die_mem, addr)) | ||
235 | return die_mem; | ||
236 | |||
237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { | ||
238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
239 | return die_mem; | ||
240 | } | ||
241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
242 | |||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | /* Compare diename and tname */ | ||
247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
248 | { | ||
249 | const char *name; | ||
250 | name = dwarf_diename(dw_die); | ||
251 | DIE_IF(name == NULL); | ||
252 | return strcmp(tname, name); | ||
253 | } | ||
254 | |||
255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | ||
256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | ||
257 | { | ||
258 | Dwarf_Addr epc; | ||
259 | int ret; | ||
260 | |||
261 | ret = dwarf_entrypc(dw_die, &epc); | ||
262 | DIE_IF(ret == -1); | ||
263 | return epc; | ||
264 | } | ||
265 | |||
266 | /* Get a variable die */ | ||
267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | ||
268 | Dwarf_Die *die_mem) | ||
269 | { | ||
270 | Dwarf_Die child_die; | ||
271 | int tag; | ||
272 | int ret; | ||
273 | |||
274 | ret = dwarf_child(sp_die, die_mem); | ||
275 | if (ret != 0) | ||
276 | return NULL; | ||
277 | |||
278 | do { | ||
279 | tag = dwarf_tag(die_mem); | ||
280 | if ((tag == DW_TAG_formal_parameter || | ||
281 | tag == DW_TAG_variable) && | ||
282 | (die_compare_name(die_mem, name) == 0)) | ||
283 | return die_mem; | ||
284 | |||
285 | if (die_find_variable(die_mem, name, &child_die)) { | ||
286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
287 | return die_mem; | ||
288 | } | ||
289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
290 | |||
291 | return NULL; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Probe finder related functions | ||
296 | */ | ||
297 | |||
298 | /* Show a location */ | ||
299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) | ||
300 | { | ||
301 | unsigned int regn; | ||
302 | Dwarf_Word offs = 0; | ||
303 | int deref = 0, ret; | ||
304 | const char *regs; | ||
305 | |||
306 | /* TODO: support CFA */ | ||
307 | /* If this is based on frame buffer, set the offset */ | ||
308 | if (op->atom == DW_OP_fbreg) { | ||
309 | if (pf->fb_ops == NULL) | ||
310 | die("The attribute of frame base is not supported.\n"); | ||
311 | deref = 1; | ||
312 | offs = op->number; | ||
313 | op = &pf->fb_ops[0]; | ||
314 | } | ||
315 | |||
316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { | ||
317 | regn = op->atom - DW_OP_breg0; | ||
318 | offs += op->number; | ||
319 | deref = 1; | ||
320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { | ||
321 | regn = op->atom - DW_OP_reg0; | ||
322 | } else if (op->atom == DW_OP_bregx) { | ||
323 | regn = op->number; | ||
324 | offs += op->number2; | ||
325 | deref = 1; | ||
326 | } else if (op->atom == DW_OP_regx) { | ||
327 | regn = op->number; | ||
328 | } else | ||
329 | die("DW_OP %d is not supported.", op->atom); | ||
330 | |||
331 | regs = get_arch_regstr(regn); | ||
332 | if (!regs) | ||
333 | die("%u exceeds max register number.", regn); | ||
334 | |||
335 | if (deref) | ||
336 | ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", | ||
337 | pf->var, (intmax_t)offs, regs); | ||
338 | else | ||
339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | ||
340 | DIE_IF(ret < 0); | ||
341 | DIE_IF(ret >= pf->len); | ||
342 | } | ||
343 | |||
344 | /* Show a variables in kprobe event format */ | ||
345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | ||
346 | { | ||
347 | Dwarf_Attribute attr; | ||
348 | Dwarf_Op *expr; | ||
349 | size_t nexpr; | ||
350 | int ret; | ||
351 | |||
352 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | ||
353 | goto error; | ||
354 | /* TODO: handle more than 1 exprs */ | ||
355 | ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); | ||
356 | if (ret <= 0 || nexpr == 0) | ||
357 | goto error; | ||
358 | |||
359 | show_location(expr, pf); | ||
360 | /* *expr will be cached in libdw. Don't free it. */ | ||
361 | return ; | ||
362 | error: | ||
363 | /* TODO: Support const_value */ | ||
364 | die("Failed to find the location of %s at this address.\n" | ||
365 | " Perhaps, it has been optimized out.", pf->var); | ||
366 | } | ||
367 | |||
368 | /* Find a variable in a subprogram die */ | ||
369 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
370 | { | ||
371 | int ret; | ||
372 | Dwarf_Die vr_die; | ||
373 | |||
374 | /* TODO: Support struct members and arrays */ | ||
375 | if (!is_c_varname(pf->var)) { | ||
376 | /* Output raw parameters */ | ||
377 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | ||
378 | DIE_IF(ret < 0); | ||
379 | DIE_IF(ret >= pf->len); | ||
380 | return ; | ||
381 | } | ||
382 | |||
383 | pr_debug("Searching '%s' variable in context.\n", pf->var); | ||
384 | /* Search child die for local variables and parameters. */ | ||
385 | if (!die_find_variable(sp_die, pf->var, &vr_die)) | ||
386 | die("Failed to find '%s' in this function.", pf->var); | ||
387 | |||
388 | show_variable(&vr_die, pf); | ||
389 | } | ||
390 | |||
391 | /* Show a probe point to output buffer */ | ||
392 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
393 | { | ||
394 | struct probe_point *pp = pf->pp; | ||
395 | Dwarf_Addr eaddr; | ||
396 | Dwarf_Die die_mem; | ||
397 | const char *name; | ||
398 | char tmp[MAX_PROBE_BUFFER]; | ||
399 | int ret, i, len; | ||
400 | Dwarf_Attribute fb_attr; | ||
401 | size_t nops; | ||
402 | |||
403 | /* If no real subprogram, find a real one */ | ||
404 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
405 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
406 | pf->addr, &die_mem); | ||
407 | if (!sp_die) | ||
408 | die("Probe point is not found in subprograms."); | ||
409 | } | ||
410 | |||
411 | /* Output name of probe point */ | ||
412 | name = dwarf_diename(sp_die); | ||
413 | if (name) { | ||
414 | dwarf_entrypc(sp_die, &eaddr); | ||
415 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, | ||
416 | (unsigned long)(pf->addr - eaddr)); | ||
417 | /* Copy the function name if possible */ | ||
418 | if (!pp->function) { | ||
419 | pp->function = strdup(name); | ||
420 | pp->offset = (size_t)(pf->addr - eaddr); | ||
421 | } | ||
422 | } else { | ||
423 | /* This function has no name. */ | ||
424 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", | ||
425 | (uintmax_t)pf->addr); | ||
426 | if (!pp->function) { | ||
427 | /* TODO: Use _stext */ | ||
428 | pp->function = strdup(""); | ||
429 | pp->offset = (size_t)pf->addr; | ||
430 | } | ||
431 | } | ||
432 | DIE_IF(ret < 0); | ||
433 | DIE_IF(ret >= MAX_PROBE_BUFFER); | ||
434 | len = ret; | ||
435 | pr_debug("Probe point found: %s\n", tmp); | ||
436 | |||
437 | /* Get the frame base attribute/ops */ | ||
438 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | ||
439 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | ||
440 | if (ret <= 0 || nops == 0) | ||
441 | pf->fb_ops = NULL; | ||
442 | |||
443 | /* Find each argument */ | ||
444 | /* TODO: use dwarf_cfi_addrframe */ | ||
445 | for (i = 0; i < pp->nr_args; i++) { | ||
446 | pf->var = pp->args[i]; | ||
447 | pf->buf = &tmp[len]; | ||
448 | pf->len = MAX_PROBE_BUFFER - len; | ||
449 | find_variable(sp_die, pf); | ||
450 | len += strlen(pf->buf); | ||
451 | } | ||
452 | |||
453 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | ||
454 | pf->fb_ops = NULL; | ||
455 | |||
456 | if (pp->found == MAX_PROBES) | ||
457 | die("Too many( > %d) probe point found.\n", MAX_PROBES); | ||
458 | |||
459 | pp->probes[pp->found] = strdup(tmp); | ||
460 | pp->found++; | ||
461 | } | ||
462 | |||
463 | /* Find probe point from its line number */ | ||
464 | static void find_probe_point_by_line(struct probe_finder *pf) | ||
465 | { | ||
466 | Dwarf_Lines *lines; | ||
467 | Dwarf_Line *line; | ||
468 | size_t nlines, i; | ||
469 | Dwarf_Addr addr; | ||
470 | int lineno; | ||
471 | int ret; | ||
472 | |||
473 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
474 | DIE_IF(ret != 0); | ||
475 | |||
476 | for (i = 0; i < nlines; i++) { | ||
477 | line = dwarf_onesrcline(lines, i); | ||
478 | dwarf_lineno(line, &lineno); | ||
479 | if (lineno != pf->lno) | ||
480 | continue; | ||
481 | |||
482 | /* TODO: Get fileno from line, but how? */ | ||
483 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
484 | continue; | ||
485 | |||
486 | ret = dwarf_lineaddr(line, &addr); | ||
487 | DIE_IF(ret != 0); | ||
488 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
489 | (int)i, lineno, (uintmax_t)addr); | ||
490 | pf->addr = addr; | ||
491 | |||
492 | show_probe_point(NULL, pf); | ||
493 | /* Continuing, because target line might be inlined. */ | ||
494 | } | ||
495 | } | ||
496 | |||
497 | /* Find lines which match lazy pattern */ | ||
498 | static int find_lazy_match_lines(struct list_head *head, | ||
499 | const char *fname, const char *pat) | ||
500 | { | ||
501 | char *fbuf, *p1, *p2; | ||
502 | int fd, line, nlines = 0; | ||
503 | struct stat st; | ||
504 | |||
505 | fd = open(fname, O_RDONLY); | ||
506 | if (fd < 0) | ||
507 | die("failed to open %s", fname); | ||
508 | DIE_IF(fstat(fd, &st) < 0); | ||
509 | fbuf = malloc(st.st_size + 2); | ||
510 | DIE_IF(fbuf == NULL); | ||
511 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | ||
512 | close(fd); | ||
513 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
514 | fbuf[st.st_size + 1] = '\0'; | ||
515 | p1 = fbuf; | ||
516 | line = 1; | ||
517 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
518 | *p2 = '\0'; | ||
519 | if (strlazymatch(p1, pat)) { | ||
520 | line_list__add_line(head, line); | ||
521 | nlines++; | ||
522 | } | ||
523 | line++; | ||
524 | p1 = p2 + 1; | ||
525 | } | ||
526 | free(fbuf); | ||
527 | return nlines; | ||
528 | } | ||
529 | |||
530 | /* Find probe points from lazy pattern */ | ||
531 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
532 | { | ||
533 | Dwarf_Lines *lines; | ||
534 | Dwarf_Line *line; | ||
535 | size_t nlines, i; | ||
536 | Dwarf_Addr addr; | ||
537 | Dwarf_Die die_mem; | ||
538 | int lineno; | ||
539 | int ret; | ||
540 | |||
541 | if (list_empty(&pf->lcache)) { | ||
542 | /* Matching lazy line pattern */ | ||
543 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | ||
544 | pf->pp->lazy_line); | ||
545 | if (ret <= 0) | ||
546 | die("No matched lines found in %s.", pf->fname); | ||
547 | } | ||
548 | |||
549 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
550 | DIE_IF(ret != 0); | ||
551 | for (i = 0; i < nlines; i++) { | ||
552 | line = dwarf_onesrcline(lines, i); | ||
553 | |||
554 | dwarf_lineno(line, &lineno); | ||
555 | if (!line_list__has_line(&pf->lcache, lineno)) | ||
556 | continue; | ||
557 | |||
558 | /* TODO: Get fileno from line, but how? */ | ||
559 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
560 | continue; | ||
561 | |||
562 | ret = dwarf_lineaddr(line, &addr); | ||
563 | DIE_IF(ret != 0); | ||
564 | if (sp_die) { | ||
565 | /* Address filtering 1: does sp_die include addr? */ | ||
566 | if (!dwarf_haspc(sp_die, addr)) | ||
567 | continue; | ||
568 | /* Address filtering 2: No child include addr? */ | ||
569 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
570 | continue; | ||
571 | } | ||
572 | |||
573 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
574 | (int)i, lineno, (unsigned long long)addr); | ||
575 | pf->addr = addr; | ||
576 | |||
577 | show_probe_point(sp_die, pf); | ||
578 | /* Continuing, because target line might be inlined. */ | ||
579 | } | ||
580 | /* TODO: deallocate lines, but how? */ | ||
581 | } | ||
582 | |||
583 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
584 | { | ||
585 | struct probe_finder *pf = (struct probe_finder *)data; | ||
586 | struct probe_point *pp = pf->pp; | ||
587 | |||
588 | if (pp->lazy_line) | ||
589 | find_probe_point_lazy(in_die, pf); | ||
590 | else { | ||
591 | /* Get probe address */ | ||
592 | pf->addr = die_get_entrypc(in_die); | ||
593 | pf->addr += pp->offset; | ||
594 | pr_debug("found inline addr: 0x%jx\n", | ||
595 | (uintmax_t)pf->addr); | ||
596 | |||
597 | show_probe_point(in_die, pf); | ||
598 | } | ||
599 | |||
600 | return DWARF_CB_OK; | ||
601 | } | ||
602 | |||
603 | /* Search function from function name */ | ||
604 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | ||
605 | { | ||
606 | struct probe_finder *pf = (struct probe_finder *)data; | ||
607 | struct probe_point *pp = pf->pp; | ||
608 | |||
609 | /* Check tag and diename */ | ||
610 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | ||
611 | die_compare_name(sp_die, pp->function) != 0) | ||
612 | return 0; | ||
613 | |||
614 | pf->fname = dwarf_decl_file(sp_die); | ||
615 | if (pp->line) { /* Function relative line */ | ||
616 | dwarf_decl_line(sp_die, &pf->lno); | ||
617 | pf->lno += pp->line; | ||
618 | find_probe_point_by_line(pf); | ||
619 | } else if (!dwarf_func_inline(sp_die)) { | ||
620 | /* Real function */ | ||
621 | if (pp->lazy_line) | ||
622 | find_probe_point_lazy(sp_die, pf); | ||
623 | else { | ||
624 | pf->addr = die_get_entrypc(sp_die); | ||
625 | pf->addr += pp->offset; | ||
626 | /* TODO: Check the address in this function */ | ||
627 | show_probe_point(sp_die, pf); | ||
628 | } | ||
629 | } else | ||
630 | /* Inlined function: search instances */ | ||
631 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); | ||
632 | |||
633 | return 1; /* Exit; no same symbol in this CU. */ | ||
634 | } | ||
635 | |||
636 | static void find_probe_point_by_func(struct probe_finder *pf) | ||
637 | { | ||
638 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); | ||
639 | } | ||
640 | |||
641 | /* Find a probe point */ | ||
642 | int find_probe_point(int fd, struct probe_point *pp) | ||
643 | { | ||
644 | struct probe_finder pf = {.pp = pp}; | ||
645 | Dwarf_Off off, noff; | ||
646 | size_t cuhl; | ||
647 | Dwarf_Die *diep; | ||
648 | Dwarf *dbg; | ||
649 | |||
650 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
651 | if (!dbg) | ||
652 | return -ENOENT; | ||
653 | |||
654 | pp->found = 0; | ||
655 | off = 0; | ||
656 | line_list__init(&pf.lcache); | ||
657 | /* Loop on CUs (Compilation Unit) */ | ||
658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | ||
659 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); | ||
661 | if (!diep) | ||
662 | continue; | ||
663 | |||
664 | /* Check if target file is included. */ | ||
665 | if (pp->file) | ||
666 | pf.fname = cu_find_realpath(&pf.cu_die, pp->file); | ||
667 | else | ||
668 | pf.fname = NULL; | ||
669 | |||
670 | if (!pp->file || pf.fname) { | ||
671 | if (pp->function) | ||
672 | find_probe_point_by_func(&pf); | ||
673 | else if (pp->lazy_line) | ||
674 | find_probe_point_lazy(NULL, &pf); | ||
675 | else { | ||
676 | pf.lno = pp->line; | ||
677 | find_probe_point_by_line(&pf); | ||
678 | } | ||
679 | } | ||
680 | off = noff; | ||
681 | } | ||
682 | line_list__free(&pf.lcache); | ||
683 | dwarf_end(dbg); | ||
684 | |||
685 | return pp->found; | ||
686 | } | ||
687 | |||
688 | /* Find line range from its line number */ | ||
689 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | ||
690 | { | ||
691 | Dwarf_Lines *lines; | ||
692 | Dwarf_Line *line; | ||
693 | size_t nlines, i; | ||
694 | Dwarf_Addr addr; | ||
695 | int lineno; | ||
696 | int ret; | ||
697 | const char *src; | ||
698 | Dwarf_Die die_mem; | ||
699 | |||
700 | line_list__init(&lf->lr->line_list); | ||
701 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); | ||
702 | DIE_IF(ret != 0); | ||
703 | |||
704 | for (i = 0; i < nlines; i++) { | ||
705 | line = dwarf_onesrcline(lines, i); | ||
706 | ret = dwarf_lineno(line, &lineno); | ||
707 | DIE_IF(ret != 0); | ||
708 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
709 | continue; | ||
710 | |||
711 | if (sp_die) { | ||
712 | /* Address filtering 1: does sp_die include addr? */ | ||
713 | ret = dwarf_lineaddr(line, &addr); | ||
714 | DIE_IF(ret != 0); | ||
715 | if (!dwarf_haspc(sp_die, addr)) | ||
716 | continue; | ||
717 | |||
718 | /* Address filtering 2: No child include addr? */ | ||
719 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
720 | continue; | ||
721 | } | ||
722 | |||
723 | /* TODO: Get fileno from line, but how? */ | ||
724 | src = dwarf_linesrc(line, NULL, NULL); | ||
725 | if (strtailcmp(src, lf->fname) != 0) | ||
726 | continue; | ||
727 | |||
728 | /* Copy real path */ | ||
729 | if (!lf->lr->path) | ||
730 | lf->lr->path = strdup(src); | ||
731 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
732 | } | ||
733 | /* Update status */ | ||
734 | if (!list_empty(&lf->lr->line_list)) | ||
735 | lf->found = 1; | ||
736 | else { | ||
737 | free(lf->lr->path); | ||
738 | lf->lr->path = NULL; | ||
739 | } | ||
740 | } | ||
741 | |||
742 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | ||
743 | { | ||
744 | find_line_range_by_line(in_die, (struct line_finder *)data); | ||
745 | return DWARF_CB_ABORT; /* No need to find other instances */ | ||
746 | } | ||
747 | |||
748 | /* Search function from function name */ | ||
749 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | ||
750 | { | ||
751 | struct line_finder *lf = (struct line_finder *)data; | ||
752 | struct line_range *lr = lf->lr; | ||
753 | |||
754 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | ||
755 | die_compare_name(sp_die, lr->function) == 0) { | ||
756 | lf->fname = dwarf_decl_file(sp_die); | ||
757 | dwarf_decl_line(sp_die, &lr->offset); | ||
758 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | ||
759 | lf->lno_s = lr->offset + lr->start; | ||
760 | if (!lr->end) | ||
761 | lf->lno_e = INT_MAX; | ||
762 | else | ||
763 | lf->lno_e = lr->offset + lr->end; | ||
764 | lr->start = lf->lno_s; | ||
765 | lr->end = lf->lno_e; | ||
766 | if (dwarf_func_inline(sp_die)) | ||
767 | dwarf_func_inline_instances(sp_die, | ||
768 | line_range_inline_cb, lf); | ||
769 | else | ||
770 | find_line_range_by_line(sp_die, lf); | ||
771 | return 1; | ||
772 | } | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static void find_line_range_by_func(struct line_finder *lf) | ||
777 | { | ||
778 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); | ||
779 | } | ||
780 | |||
781 | int find_line_range(int fd, struct line_range *lr) | ||
782 | { | ||
783 | struct line_finder lf = {.lr = lr, .found = 0}; | ||
784 | int ret; | ||
785 | Dwarf_Off off = 0, noff; | ||
786 | size_t cuhl; | ||
787 | Dwarf_Die *diep; | ||
788 | Dwarf *dbg; | ||
789 | |||
790 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
791 | if (!dbg) | ||
792 | return -ENOENT; | ||
793 | |||
794 | /* Loop on CUs (Compilation Unit) */ | ||
795 | while (!lf.found) { | ||
796 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); | ||
797 | if (ret != 0) | ||
798 | break; | ||
799 | |||
800 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
801 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | ||
802 | if (!diep) | ||
803 | continue; | ||
804 | |||
805 | /* Check if target file is included. */ | ||
806 | if (lr->file) | ||
807 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); | ||
808 | else | ||
809 | lf.fname = 0; | ||
810 | |||
811 | if (!lr->file || lf.fname) { | ||
812 | if (lr->function) | ||
813 | find_line_range_by_func(&lf); | ||
814 | else { | ||
815 | lf.lno_s = lr->start; | ||
816 | if (!lr->end) | ||
817 | lf.lno_e = INT_MAX; | ||
818 | else | ||
819 | lf.lno_e = lr->end; | ||
820 | find_line_range_by_line(NULL, &lf); | ||
821 | } | ||
822 | } | ||
823 | off = noff; | ||
824 | } | ||
825 | pr_debug("path: %lx\n", (unsigned long)lr->path); | ||
826 | dwarf_end(dbg); | ||
827 | return lf.found; | ||
828 | } | ||
829 | |||