blob: 1b2124d12f6831fad08e9a0a0edddd148a4576c2 [file] [log] [blame]
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -04001/*
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>
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -040034
Masami Hiramatsu89c69c02009-10-16 20:08:10 -040035#include "event.h"
36#include "debug.h"
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -040037#include "util.h"
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -040038#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
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -040050/*
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);
Juha Leppanend56728b2009-12-07 12:00:40 -0500109 while (--i1 >= 0 && --i2 >= 0) {
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400110 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. */
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400117static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400118{
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)
Arnaldo Carvalho de Melob7cb10e2009-10-21 17:34:06 -0200139 pr_debug("found fno: %d\n", (int)found);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400140 return found;
141}
142
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500143static 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
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400168/* Compare diename and tname */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400169static int die_compare_name(Dwarf_Die dw_die, const char *tname)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400170{
171 char *name;
172 int ret;
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400173 ret = dwarf_diename(dw_die, &name, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400174 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400175 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400192 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400193 if (ret == DW_DLV_NO_ENTRY)
194 return 0;
195 ret = dwarf_highpc(sp_die, &hipc, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400196 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400197 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 */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400205static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400206{
207 /* TODO: check strictly */
208 Dwarf_Bool inl;
209 int ret;
210
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400211 ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400212 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400213 return inl;
214}
215
216/* Get the offset of abstruct_origin */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400217static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400218{
219 Dwarf_Attribute attr;
220 Dwarf_Off cu_offs;
221 int ret;
222
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400223 ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400224 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400225 ret = dwarf_formref(attr, &cu_offs, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400226 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400227 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 */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400232static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400233{
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 */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400242 ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400243 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400244 if (ret == DW_DLV_OK) {
245 ret = dwarf_formaddr(attr, &addr, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400246 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400247 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
248 return addr;
249 }
250
251 /* Try to get low pc */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400252 ret = dwarf_lowpc(dw_die, &addr, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400253 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400254 if (ret == DW_DLV_OK)
255 return addr;
256
257 /* Try to get ranges */
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400258 ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400259 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400260 ret = dwarf_formref(attr, &offs, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400261 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400262 ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL,
263 &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400264 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400265 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400289 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400290 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400301 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400302 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400321 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400322 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400337 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400338 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400345 DIE_IF(desc->ld_s == NULL);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400346 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
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400363/* 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
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400393/*
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
Masami Hiramatsubbaa46f2010-01-05 17:47:03 -0500430 die("Dwarf_OP %d is not supported.", op);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400431
432 regs = get_arch_regstr(regn);
433 if (!regs)
Masami Hiramatsubbaa46f2010-01-05 17:47:03 -0500434 die("%lld exceeds max register number.", regn);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400435
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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400441 DIE_IF(ret < 0);
442 DIE_IF(ret >= pf->len);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400443}
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? */
Masami Hiramatsu97698332009-10-16 20:08:18 -0400459 DIE_IF(ld.ld_cents != 1);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400460 show_location(&ld.ld_s[0], pf);
461 free(ld.ld_s);
462 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
463 return ;
464error:
Masami Hiramatsu074fc0e2009-10-16 20:08:01 -0400465 die("Failed to find the location of %s at this address.\n"
Masami Hiramatsubbaa46f2010-01-05 17:47:03 -0500466 " Perhaps, it has been optimized out.", pf->var);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400467}
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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400476 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400477 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400495 DIE_IF(ret < 0);
496 DIE_IF(ret >= pf->len);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400497 return ;
498 }
499
Arnaldo Carvalho de Melob7cb10e2009-10-21 17:34:06 -0200500 pr_debug("Searching '%s' variable in context.\n", pf->var);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400501 /* Search child die for local variables and parameters. */
502 ret = search_die_from_children(sp_die, variable_callback, pf);
503 if (!ret)
Masami Hiramatsubbaa46f2010-01-05 17:47:03 -0500504 die("Failed to find '%s' in this function.", pf->var);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400505}
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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400514 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400515 ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
Masami Hiramatsu97698332009-10-16 20:08:18 -0400516 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400517 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 */
527static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
528 struct probe_finder *pf)
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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400537 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400538 if (ret == DW_DLV_OK) {
539 ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
540 (unsigned int)offs);
Masami Hiramatsu253977b2009-10-27 16:43:10 -0400541 /* Copy the function name if possible */
542 if (!pp->function) {
543 pp->function = strdup(name);
544 pp->offset = offs;
545 }
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400546 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);
Masami Hiramatsu253977b2009-10-27 16:43:10 -0400550 if (!pp->function) {
551 /* TODO: Use _stext */
552 pp->function = strdup("");
553 pp->offset = (int)pf->addr;
554 }
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400555 }
Masami Hiramatsu97698332009-10-16 20:08:18 -0400556 DIE_IF(ret < 0);
557 DIE_IF(ret >= MAX_PROBE_BUFFER);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400558 len = ret;
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400559 pr_debug("Probe point found: %s\n", tmp);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400560
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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400584 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400585 /* Check the address is in this subprogram */
586 if (tag == DW_TAG_subprogram &&
587 die_within_subprogram(dlink->die, pf->addr, &offs)) {
588 show_probepoint(dlink->die, offs, pf);
589 return 1;
590 }
591 return 0;
592}
593
594/* Find probe point from its line number */
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500595static void find_probe_point_by_line(struct probe_finder *pf)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400596{
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400597 Dwarf_Signed cnt, i, clm;
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400598 Dwarf_Line *lines;
599 Dwarf_Unsigned lineno = 0;
600 Dwarf_Addr addr;
601 Dwarf_Unsigned fno;
602 int ret;
603
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400604 ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400605 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400606
607 for (i = 0; i < cnt; i++) {
608 ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400609 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400610 if (fno != pf->fno)
611 continue;
612
613 ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400614 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400615 if (lineno != pf->lno)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400616 continue;
617
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400618 ret = dwarf_lineoff(lines[i], &clm, &__dw_error);
619 DIE_IF(ret != DW_DLV_OK);
620
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400621 ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400622 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400623 pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n",
624 (int)i, (unsigned)lineno, (int)clm, addr);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400625 pf->addr = addr;
626 /* Search a real subprogram including this line, */
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400627 ret = search_die_from_children(pf->cu_die,
628 probeaddr_callback, pf);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400629 if (ret == 0)
Masami Hiramatsubbaa46f2010-01-05 17:47:03 -0500630 die("Probe point is not found in subprograms.");
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400631 /* 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400647 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400648 if (tag == DW_TAG_subprogram) {
649 if (die_compare_name(dlink->die, pp->function) == 0) {
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400650 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;
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500654 find_probe_point_by_line(pf);
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400655 return 1;
656 }
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400657 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400662 DIE_IF(ret != DW_DLV_OK);
Arnaldo Carvalho de Melob7cb10e2009-10-21 17:34:06 -0200663 pr_debug("inline definition offset %lld\n",
664 pf->inl_offs);
Masami Hiramatsu8030c5f2009-10-27 16:42:53 -0400665 return 0; /* Continue to search */
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400666 }
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 */
671 show_probepoint(dlink->die, pp->offset, pf);
Masami Hiramatsu8030c5f2009-10-27 16:42:53 -0400672 return 1; /* Exit; no same symbol in this CU. */
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400673 }
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;
Arnaldo Carvalho de Melob7cb10e2009-10-21 17:34:06 -0200679 pr_debug("found inline addr: 0x%llx\n", pf->addr);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400680 /* 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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400684 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400685 if (tag == DW_TAG_subprogram &&
686 !die_inlined_subprogram(lk->die))
687 goto found;
688 }
Masami Hiramatsubbaa46f2010-01-05 17:47:03 -0500689 die("Failed to find real subprogram.");
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400690found:
691 /* Get offset from subprogram */
692 ret = die_within_subprogram(lk->die, pf->addr, &offs);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400693 DIE_IF(!ret);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400694 show_probepoint(lk->die, offs, pf);
695 /* Continue to search */
696 }
697 }
698 return 0;
699}
700
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500701static void find_probe_point_by_func(struct probe_finder *pf)
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400702{
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400703 search_die_from_children(pf->cu_die, probefunc_callback, pf);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400704}
705
706/* Find a probe point */
707int find_probepoint(int fd, struct probe_point *pp)
708{
709 Dwarf_Half addr_size = 0;
710 Dwarf_Unsigned next_cuh = 0;
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400711 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);
Masami Hiramatsu411edfe2009-12-15 10:31:35 -0500715 if (ret != DW_DLV_OK)
Masami Hiramatsua225a1d2009-11-03 19:12:30 -0500716 return -ENOENT;
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400717
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);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400723 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400724 if (ret == DW_DLV_NO_ENTRY)
725 break;
726
727 /* Get the DIE(Debugging Information Entry) of this CU */
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400728 ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400729 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400730
731 /* Check if target file is included. */
732 if (pp->file)
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400733 pf.fno = cu_find_fileno(pf.cu_die, pp->file);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400734
735 if (!pp->file || pf.fno) {
736 /* Save CU base address (for frame_base) */
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400737 ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400738 DIE_IF(ret == DW_DLV_ERROR);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400739 if (ret == DW_DLV_NO_ENTRY)
740 pf.cu_base = 0;
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400741 if (pp->function)
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500742 find_probe_point_by_func(&pf);
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400743 else {
744 pf.lno = pp->line;
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500745 find_probe_point_by_line(&pf);
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400746 }
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400747 }
Masami Hiramatsub0ef0732009-10-27 16:43:19 -0400748 dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400749 }
750 ret = dwarf_finish(__dw_debug, &__dw_error);
Masami Hiramatsu97698332009-10-16 20:08:18 -0400751 DIE_IF(ret != DW_DLV_OK);
Masami Hiramatsu4ea42b12009-10-08 17:17:38 -0400752
753 return pp->found;
754}
755
Masami Hiramatsu631c9de2010-01-06 09:45:34 -0500756
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
791 ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
792 DIE_IF(ret != DW_DLV_OK);
793
794 for (i = 0; i < cnt; i++) {
795 ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
796 DIE_IF(ret != DW_DLV_OK);
797 if (fno != lf->fno)
798 continue;
799
800 ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
801 DIE_IF(ret != DW_DLV_OK);
802 if (lf->lno_s > lineno || lf->lno_e < lineno)
803 continue;
804
805 /* Filter line in the function address range */
806 if (lf->addr_s && lf->addr_e) {
807 ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
808 DIE_IF(ret != DW_DLV_OK);
809 if (lf->addr_s > addr || lf->addr_e <= addr)
810 continue;
811 }
812 line_range_add_line(lf->lr, (unsigned int)lineno);
813 }
814 dwarf_srclines_dealloc(__dw_debug, lines, cnt);
815 if (!list_empty(&lf->lr->line_list))
816 lf->found = 1;
817}
818
819/* Search function from function name */
820static int linefunc_callback(struct die_link *dlink, void *data)
821{
822 struct line_finder *lf = (struct line_finder *)data;
823 struct line_range *lr = lf->lr;
824 Dwarf_Half tag;
825 int ret;
826
827 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
828 DIE_IF(ret == DW_DLV_ERROR);
829 if (tag == DW_TAG_subprogram &&
830 die_compare_name(dlink->die, lr->function) == 0) {
831 /* Get the address range of this function */
832 ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
833 if (ret == DW_DLV_OK)
834 ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
835 DIE_IF(ret == DW_DLV_ERROR);
836 if (ret == DW_DLV_NO_ENTRY) {
837 lf->addr_s = 0;
838 lf->addr_e = 0;
839 }
840
841 lf->fno = die_get_decl_file(dlink->die);
842 lr->offset = die_get_decl_line(dlink->die);;
843 lf->lno_s = lr->offset + lr->start;
844 if (!lr->end)
845 lf->lno_e = (Dwarf_Unsigned)-1;
846 else
847 lf->lno_e = lr->offset + lr->end;
848 lr->start = lf->lno_s;
849 lr->end = lf->lno_e;
850 find_line_range_by_line(lf);
851 /* If we find a target function, this should be end. */
852 lf->found = 1;
853 return 1;
854 }
855 return 0;
856}
857
858static void find_line_range_by_func(struct line_finder *lf)
859{
860 search_die_from_children(lf->cu_die, linefunc_callback, lf);
861}
862
863int find_line_range(int fd, struct line_range *lr)
864{
865 Dwarf_Half addr_size = 0;
866 Dwarf_Unsigned next_cuh = 0;
867 int ret;
868 struct line_finder lf = {.lr = lr};
869
870 ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
871 if (ret != DW_DLV_OK)
872 return -ENOENT;
873
874 while (!lf.found) {
875 /* Search CU (Compilation Unit) */
876 ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
877 &addr_size, &next_cuh, &__dw_error);
878 DIE_IF(ret == DW_DLV_ERROR);
879 if (ret == DW_DLV_NO_ENTRY)
880 break;
881
882 /* Get the DIE(Debugging Information Entry) of this CU */
883 ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
884 DIE_IF(ret != DW_DLV_OK);
885
886 /* Check if target file is included. */
887 if (lr->file)
888 lf.fno = cu_find_fileno(lf.cu_die, lr->file);
889
890 if (!lr->file || lf.fno) {
891 if (lr->function)
892 find_line_range_by_func(&lf);
893 else {
894 lf.lno_s = lr->start;
895 if (!lr->end)
896 lf.lno_e = (Dwarf_Unsigned)-1;
897 else
898 lf.lno_e = lr->end;
899 find_line_range_by_line(&lf);
900 }
901 /* Get the real file path */
902 if (lf.found)
903 cu_get_filename(lf.cu_die, lf.fno, &lr->path);
904 }
905 dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
906 }
907 ret = dwarf_finish(__dw_debug, &__dw_error);
908 DIE_IF(ret != DW_DLV_OK);
909 return lf.found;
910}
911