probe-finder.c 22.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * probe-finder.c : C expression to kprobe event converter
 *
 * Written by Masami Hiramatsu <mhiramat@redhat.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
34

35 36
#include "event.h"
#include "debug.h"
37
#include "util.h"
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
#include "probe-finder.h"


/* Dwarf_Die Linkage to parent Die */
struct die_link {
	struct die_link *parent;	/* Parent die */
	Dwarf_Die die;			/* Current die */
};

static Dwarf_Debug __dw_debug;
static Dwarf_Error __dw_error;

/*
 * Generic dwarf analysis helpers
 */

#define X86_32_MAX_REGS 8
const char *x86_32_regs_table[X86_32_MAX_REGS] = {
	"%ax",
	"%cx",
	"%dx",
	"%bx",
	"$stack",	/* Stack address instead of %sp */
	"%bp",
	"%si",
	"%di",
};

#define X86_64_MAX_REGS 16
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
	"%ax",
	"%dx",
	"%cx",
	"%bx",
	"%si",
	"%di",
	"%bp",
	"%sp",
	"%r8",
	"%r9",
	"%r10",
	"%r11",
	"%r12",
	"%r13",
	"%r14",
	"%r15",
};

/* TODO: switching by dwarf address size */
#ifdef __x86_64__
#define ARCH_MAX_REGS X86_64_MAX_REGS
#define arch_regs_table x86_64_regs_table
#else
#define ARCH_MAX_REGS X86_32_MAX_REGS
#define arch_regs_table x86_32_regs_table
#endif

/* Return architecture dependent register string (for kprobe-tracer) */
static const char *get_arch_regstr(unsigned int n)
{
	return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
}

/*
 * Compare the tail of two strings.
 * Return 0 if whole of either string is same as another's tail part.
 */
static int strtailcmp(const char *s1, const char *s2)
{
	int i1 = strlen(s1);
	int i2 = strlen(s2);
109
	while (--i1 >= 0 && --i2 >= 0) {
110 111 112 113 114 115 116
		if (s1[i1] != s2[i2])
			return s1[i1] - s2[i2];
	}
	return 0;
}

/* Find the fileno of the target file. */
117
static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
{
	Dwarf_Signed cnt, i;
	Dwarf_Unsigned found = 0;
	char **srcs;
	int ret;

	if (!fname)
		return 0;

	ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
	if (ret == DW_DLV_OK) {
		for (i = 0; i < cnt && !found; i++) {
			if (strtailcmp(srcs[i], fname) == 0)
				found = i + 1;
			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
		}
		for (; i < cnt; i++)
			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
		dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
	}
	if (found)
139
		pr_debug("found fno: %d\n", (int)found);
140 141 142
	return found;
}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf)
{
	Dwarf_Signed cnt, i;
	char **srcs;
	int ret = 0;

	if (!buf || !fno)
		return -EINVAL;

	ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
	if (ret == DW_DLV_OK) {
		if ((Dwarf_Unsigned)cnt > fno - 1) {
			*buf = strdup(srcs[fno - 1]);
			ret = 0;
			pr_debug("found filename: %s\n", *buf);
		} else
			ret = -ENOENT;
		for (i = 0; i < cnt; i++)
			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
		dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
	} else
		ret = -EINVAL;
	return ret;
}

168
/* Compare diename and tname */
169
static int die_compare_name(Dwarf_Die dw_die, const char *tname)
170 171 172
{
	char *name;
	int ret;
173
	ret = dwarf_diename(dw_die, &name, &__dw_error);
174
	DIE_IF(ret == DW_DLV_ERROR);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	if (ret == DW_DLV_OK) {
		ret = strcmp(tname, name);
		dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
	} else
		ret = -1;
	return ret;
}

/* Check the address is in the subprogram(function). */
static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
				 Dwarf_Signed *offs)
{
	Dwarf_Addr lopc, hipc;
	int ret;

	/* TODO: check ranges */
	ret = dwarf_lowpc(sp_die, &lopc, &__dw_error);
192
	DIE_IF(ret == DW_DLV_ERROR);
193 194 195
	if (ret == DW_DLV_NO_ENTRY)
		return 0;
	ret = dwarf_highpc(sp_die, &hipc, &__dw_error);
196
	DIE_IF(ret != DW_DLV_OK);
197 198 199 200 201 202 203 204
	if (lopc <= addr && addr < hipc) {
		*offs = addr - lopc;
		return 1;
	} else
		return 0;
}

/* Check the die is inlined function */
205
static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die)
206 207 208 209 210
{
	/* TODO: check strictly */
	Dwarf_Bool inl;
	int ret;

211
	ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error);
212
	DIE_IF(ret == DW_DLV_ERROR);
213 214 215 216
	return inl;
}

/* Get the offset of abstruct_origin */
217
static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die)
218 219 220 221 222
{
	Dwarf_Attribute attr;
	Dwarf_Off cu_offs;
	int ret;

223
	ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error);
224
	DIE_IF(ret != DW_DLV_OK);
225
	ret = dwarf_formref(attr, &cu_offs, &__dw_error);
226
	DIE_IF(ret != DW_DLV_OK);
227 228 229 230 231
	dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
	return cu_offs;
}

/* Get entry pc(or low pc, 1st entry of ranges)  of the die */
232
static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die)
233 234 235 236 237 238 239 240 241
{
	Dwarf_Attribute attr;
	Dwarf_Addr addr;
	Dwarf_Off offs;
	Dwarf_Ranges *ranges;
	Dwarf_Signed cnt;
	int ret;

	/* Try to get entry pc */
242
	ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error);
243
	DIE_IF(ret == DW_DLV_ERROR);
244 245
	if (ret == DW_DLV_OK) {
		ret = dwarf_formaddr(attr, &addr, &__dw_error);
246
		DIE_IF(ret != DW_DLV_OK);
247 248 249 250 251
		dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
		return addr;
	}

	/* Try to get low pc */
252
	ret = dwarf_lowpc(dw_die, &addr, &__dw_error);
253
	DIE_IF(ret == DW_DLV_ERROR);
254 255 256 257
	if (ret == DW_DLV_OK)
		return addr;

	/* Try to get ranges */
258
	ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error);
259
	DIE_IF(ret != DW_DLV_OK);
260
	ret = dwarf_formref(attr, &offs, &__dw_error);
261
	DIE_IF(ret != DW_DLV_OK);
262 263
	ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL,
				&__dw_error);
264
	DIE_IF(ret != DW_DLV_OK);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
	addr = ranges[0].dwr_addr1;
	dwarf_ranges_dealloc(__dw_debug, ranges, cnt);
	return addr;
}

/*
 * Search a Die from Die tree.
 * Note: cur_link->die should be deallocated in this function.
 */
static int __search_die_tree(struct die_link *cur_link,
			     int (*die_cb)(struct die_link *, void *),
			     void *data)
{
	Dwarf_Die new_die;
	struct die_link new_link;
	int ret;

	if (!die_cb)
		return 0;

	/* Check current die */
	while (!(ret = die_cb(cur_link, data))) {
		/* Check child die */
		ret = dwarf_child(cur_link->die, &new_die, &__dw_error);
289
		DIE_IF(ret == DW_DLV_ERROR);
290 291 292 293 294 295 296 297 298 299 300
		if (ret == DW_DLV_OK) {
			new_link.parent = cur_link;
			new_link.die = new_die;
			ret = __search_die_tree(&new_link, die_cb, data);
			if (ret)
				break;
		}

		/* Move to next sibling */
		ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die,
				      &__dw_error);
301
		DIE_IF(ret == DW_DLV_ERROR);
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
		dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
		cur_link->die = new_die;
		if (ret == DW_DLV_NO_ENTRY)
			return 0;
	}
	dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
	return ret;
}

/* Search a die in its children's die tree */
static int search_die_from_children(Dwarf_Die parent_die,
				    int (*die_cb)(struct die_link *, void *),
				    void *data)
{
	struct die_link new_link;
	int ret;

	new_link.parent = NULL;
	ret = dwarf_child(parent_die, &new_link.die, &__dw_error);
321
	DIE_IF(ret == DW_DLV_ERROR);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
	if (ret == DW_DLV_OK)
		return __search_die_tree(&new_link, die_cb, data);
	else
		return 0;
}

/* Find a locdesc corresponding to the address */
static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
			    Dwarf_Addr addr)
{
	Dwarf_Signed lcnt;
	Dwarf_Locdesc **llbuf;
	int ret, i;

	ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error);
337
	DIE_IF(ret != DW_DLV_OK);
338 339 340 341 342 343 344
	ret = DW_DLV_NO_ENTRY;
	for (i = 0; i < lcnt; ++i) {
		if (llbuf[i]->ld_lopc <= addr &&
		    llbuf[i]->ld_hipc > addr) {
			memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
			desc->ld_s =
				malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
345
			DIE_IF(desc->ld_s == NULL);
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
			memcpy(desc->ld_s, llbuf[i]->ld_s,
				sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
			ret = DW_DLV_OK;
			break;
		}
		dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
		dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
	}
	/* Releasing loop */
	for (; i < lcnt; ++i) {
		dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
		dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
	}
	dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST);
	return ret;
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
/* Get decl_file attribute value (file number) */
static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die)
{
	Dwarf_Attribute attr;
	Dwarf_Unsigned fno;
	int ret;

	ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error);
	DIE_IF(ret != DW_DLV_OK);
	dwarf_formudata(attr, &fno, &__dw_error);
	DIE_IF(ret != DW_DLV_OK);
	dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
	return fno;
}

/* Get decl_line attribute value (line number) */
static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die)
{
	Dwarf_Attribute attr;
	Dwarf_Unsigned lno;
	int ret;

	ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error);
	DIE_IF(ret != DW_DLV_OK);
	dwarf_formudata(attr, &lno, &__dw_error);
	DIE_IF(ret != DW_DLV_OK);
	dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
	return lno;
}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
/*
 * Probe finder related functions
 */

/* Show a location */
static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
{
	Dwarf_Small op;
	Dwarf_Unsigned regn;
	Dwarf_Signed offs;
	int deref = 0, ret;
	const char *regs;

	op = loc->lr_atom;

	/* If this is based on frame buffer, set the offset */
	if (op == DW_OP_fbreg) {
		deref = 1;
		offs = (Dwarf_Signed)loc->lr_number;
		op = pf->fbloc.ld_s[0].lr_atom;
		loc = &pf->fbloc.ld_s[0];
	} else
		offs = 0;

	if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
		regn = op - DW_OP_breg0;
		offs += (Dwarf_Signed)loc->lr_number;
		deref = 1;
	} else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
		regn = op - DW_OP_reg0;
	} else if (op == DW_OP_bregx) {
		regn = loc->lr_number;
		offs += (Dwarf_Signed)loc->lr_number2;
		deref = 1;
	} else if (op == DW_OP_regx) {
		regn = loc->lr_number;
	} else
430
		die("Dwarf_OP %d is not supported.", op);
431 432 433

	regs = get_arch_regstr(regn);
	if (!regs)
434
		die("%lld exceeds max register number.", regn);
435 436 437 438 439 440

	if (deref)
		ret = snprintf(pf->buf, pf->len,
				 " %s=%+lld(%s)", pf->var, offs, regs);
	else
		ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs);
441 442
	DIE_IF(ret < 0);
	DIE_IF(ret >= pf->len);
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
}

/* Show a variables in kprobe event format */
static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
{
	Dwarf_Attribute attr;
	Dwarf_Locdesc ld;
	int ret;

	ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error);
	if (ret != DW_DLV_OK)
		goto error;
	ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
	if (ret != DW_DLV_OK)
		goto error;
	/* TODO? */
459
	DIE_IF(ld.ld_cents != 1);
460 461 462 463 464
	show_location(&ld.ld_s[0], pf);
	free(ld.ld_s);
	dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
	return ;
error:
465
	die("Failed to find the location of %s at this address.\n"
466
	    " Perhaps, it has been optimized out.", pf->var);
467 468 469 470 471 472 473 474 475
}

static int variable_callback(struct die_link *dlink, void *data)
{
	struct probe_finder *pf = (struct probe_finder *)data;
	Dwarf_Half tag;
	int ret;

	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
476
	DIE_IF(ret == DW_DLV_ERROR);
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
	if ((tag == DW_TAG_formal_parameter ||
	     tag == DW_TAG_variable) &&
	    (die_compare_name(dlink->die, pf->var) == 0)) {
		show_variable(dlink->die, pf);
		return 1;
	}
	/* TODO: Support struct members and arrays */
	return 0;
}

/* Find a variable in a subprogram die */
static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
{
	int ret;

	if (!is_c_varname(pf->var)) {
		/* Output raw parameters */
		ret = snprintf(pf->buf, pf->len, " %s", pf->var);
495 496
		DIE_IF(ret < 0);
		DIE_IF(ret >= pf->len);
497 498 499
		return ;
	}

500
	pr_debug("Searching '%s' variable in context.\n", pf->var);
501 502 503
	/* Search child die for local variables and parameters. */
	ret = search_die_from_children(sp_die, variable_callback, pf);
	if (!ret)
504
		die("Failed to find '%s' in this function.", pf->var);
505 506 507 508 509 510 511 512 513
}

/* Get a frame base on the address */
static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
{
	Dwarf_Attribute attr;
	int ret;

	ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error);
514
	DIE_IF(ret != DW_DLV_OK);
515
	ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
516
	DIE_IF(ret != DW_DLV_OK);
517 518 519 520 521 522 523 524 525 526
	dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
}

static void free_current_frame_base(struct probe_finder *pf)
{
	free(pf->fbloc.ld_s);
	memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
}

/* Show a probe point to output buffer */
527 528
static void show_probe_point(Dwarf_Die sp_die, Dwarf_Signed offs,
			     struct probe_finder *pf)
529 530 531 532 533 534 535 536
{
	struct probe_point *pp = pf->pp;
	char *name;
	char tmp[MAX_PROBE_BUFFER];
	int ret, i, len;

	/* Output name of probe point */
	ret = dwarf_diename(sp_die, &name, &__dw_error);
537
	DIE_IF(ret == DW_DLV_ERROR);
538 539 540
	if (ret == DW_DLV_OK) {
		ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
				(unsigned int)offs);
541 542 543 544 545
		/* Copy the function name if possible */
		if (!pp->function) {
			pp->function = strdup(name);
			pp->offset = offs;
		}
546 547 548 549
		dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
	} else {
		/* This function has no name. */
		ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr);
550 551 552 553 554
		if (!pp->function) {
			/* TODO: Use _stext */
			pp->function = strdup("");
			pp->offset = (int)pf->addr;
		}
555
	}
556 557
	DIE_IF(ret < 0);
	DIE_IF(ret >= MAX_PROBE_BUFFER);
558
	len = ret;
559
	pr_debug("Probe point found: %s\n", tmp);
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

	/* Find each argument */
	get_current_frame_base(sp_die, pf);
	for (i = 0; i < pp->nr_args; i++) {
		pf->var = pp->args[i];
		pf->buf = &tmp[len];
		pf->len = MAX_PROBE_BUFFER - len;
		find_variable(sp_die, pf);
		len += strlen(pf->buf);
	}
	free_current_frame_base(pf);

	pp->probes[pp->found] = strdup(tmp);
	pp->found++;
}

static int probeaddr_callback(struct die_link *dlink, void *data)
{
	struct probe_finder *pf = (struct probe_finder *)data;
	Dwarf_Half tag;
	Dwarf_Signed offs;
	int ret;

	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
584
	DIE_IF(ret == DW_DLV_ERROR);
585 586 587
	/* Check the address is in this subprogram */
	if (tag == DW_TAG_subprogram &&
	    die_within_subprogram(dlink->die, pf->addr, &offs)) {
588
		show_probe_point(dlink->die, offs, pf);
589 590 591 592 593 594
		return 1;
	}
	return 0;
}

/* Find probe point from its line number */
595
static void find_probe_point_by_line(struct probe_finder *pf)
596
{
597
	Dwarf_Signed cnt, i, clm;
598 599 600 601 602 603
	Dwarf_Line *lines;
	Dwarf_Unsigned lineno = 0;
	Dwarf_Addr addr;
	Dwarf_Unsigned fno;
	int ret;

604
	ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error);
605
	DIE_IF(ret != DW_DLV_OK);
606 607 608

	for (i = 0; i < cnt; i++) {
		ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
609
		DIE_IF(ret != DW_DLV_OK);
610 611 612 613
		if (fno != pf->fno)
			continue;

		ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
614
		DIE_IF(ret != DW_DLV_OK);
615
		if (lineno != pf->lno)
616 617
			continue;

618 619 620
		ret = dwarf_lineoff(lines[i], &clm, &__dw_error);
		DIE_IF(ret != DW_DLV_OK);

621
		ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
622
		DIE_IF(ret != DW_DLV_OK);
623 624
		pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n",
			 (int)i, (unsigned)lineno, (int)clm, addr);
625 626
		pf->addr = addr;
		/* Search a real subprogram including this line, */
627 628
		ret = search_die_from_children(pf->cu_die,
					       probeaddr_callback, pf);
629
		if (ret == 0)
630
			die("Probe point is not found in subprograms.");
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
		/* Continuing, because target line might be inlined. */
	}
	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
}

/* Search function from function name */
static int probefunc_callback(struct die_link *dlink, void *data)
{
	struct probe_finder *pf = (struct probe_finder *)data;
	struct probe_point *pp = pf->pp;
	struct die_link *lk;
	Dwarf_Signed offs;
	Dwarf_Half tag;
	int ret;

	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
647
	DIE_IF(ret == DW_DLV_ERROR);
648 649
	if (tag == DW_TAG_subprogram) {
		if (die_compare_name(dlink->die, pp->function) == 0) {
650 651 652 653
			if (pp->line) {	/* Function relative line */
				pf->fno = die_get_decl_file(dlink->die);
				pf->lno = die_get_decl_line(dlink->die)
					 + pp->line;
654
				find_probe_point_by_line(pf);
655 656
				return 1;
			}
657 658 659 660 661
			if (die_inlined_subprogram(dlink->die)) {
				/* Inlined function, save it. */
				ret = dwarf_die_CU_offset(dlink->die,
							  &pf->inl_offs,
							  &__dw_error);
662
				DIE_IF(ret != DW_DLV_OK);
663 664
				pr_debug("inline definition offset %lld\n",
					 pf->inl_offs);
665
				return 0;	/* Continue to search */
666 667 668 669 670
			}
			/* Get probe address */
			pf->addr = die_get_entrypc(dlink->die);
			pf->addr += pp->offset;
			/* TODO: Check the address in this function */
671
			show_probe_point(dlink->die, pp->offset, pf);
672
			return 1; /* Exit; no same symbol in this CU. */
673 674 675 676 677 678
		}
	} else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
		if (die_get_abstract_origin(dlink->die) == pf->inl_offs) {
			/* Get probe address */
			pf->addr = die_get_entrypc(dlink->die);
			pf->addr += pp->offset;
679
			pr_debug("found inline addr: 0x%llx\n", pf->addr);
680 681 682 683
			/* Inlined function. Get a real subprogram */
			for (lk = dlink->parent; lk != NULL; lk = lk->parent) {
				tag = 0;
				dwarf_tag(lk->die, &tag, &__dw_error);
684
				DIE_IF(ret == DW_DLV_ERROR);
685 686 687 688
				if (tag == DW_TAG_subprogram &&
				    !die_inlined_subprogram(lk->die))
					goto found;
			}
689
			die("Failed to find real subprogram.");
690 691 692
found:
			/* Get offset from subprogram */
			ret = die_within_subprogram(lk->die, pf->addr, &offs);
693
			DIE_IF(!ret);
694
			show_probe_point(lk->die, offs, pf);
695 696 697 698 699 700
			/* Continue to search */
		}
	}
	return 0;
}

701
static void find_probe_point_by_func(struct probe_finder *pf)
702
{
703
	search_die_from_children(pf->cu_die, probefunc_callback, pf);
704 705 706
}

/* Find a probe point */
707
int find_probe_point(int fd, struct probe_point *pp)
708 709 710 711 712 713 714
{
	Dwarf_Half addr_size = 0;
	Dwarf_Unsigned next_cuh = 0;
	int cu_number = 0, ret;
	struct probe_finder pf = {.pp = pp};

	ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
715
	if (ret != DW_DLV_OK)
716
		return -ENOENT;
717 718 719 720 721 722

	pp->found = 0;
	while (++cu_number) {
		/* Search CU (Compilation Unit) */
		ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
			&addr_size, &next_cuh, &__dw_error);
723
		DIE_IF(ret == DW_DLV_ERROR);
724 725 726 727
		if (ret == DW_DLV_NO_ENTRY)
			break;

		/* Get the DIE(Debugging Information Entry) of this CU */
728
		ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error);
729
		DIE_IF(ret != DW_DLV_OK);
730 731 732

		/* Check if target file is included. */
		if (pp->file)
733
			pf.fno = cu_find_fileno(pf.cu_die, pp->file);
734 735 736

		if (!pp->file || pf.fno) {
			/* Save CU base address (for frame_base) */
737
			ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error);
738
			DIE_IF(ret == DW_DLV_ERROR);
739 740 741
			if (ret == DW_DLV_NO_ENTRY)
				pf.cu_base = 0;
			if (pp->function)
742
				find_probe_point_by_func(&pf);
743 744
			else {
				pf.lno = pp->line;
745
				find_probe_point_by_line(&pf);
746
			}
747
		}
748
		dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
749 750
	}
	ret = dwarf_finish(__dw_debug, &__dw_error);
751
	DIE_IF(ret != DW_DLV_OK);
752 753 754 755

	return pp->found;
}

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

static void line_range_add_line(struct line_range *lr, unsigned int line)
{
	struct line_node *ln;
	struct list_head *p;

	/* Reverse search, because new line will be the last one */
	list_for_each_entry_reverse(ln, &lr->line_list, list) {
		if (ln->line < line) {
			p = &ln->list;
			goto found;
		} else if (ln->line == line)	/* Already exist */
			return ;
	}
	/* List is empty, or the smallest entry */
	p = &lr->line_list;
found:
	pr_debug("Debug: add a line %u\n", line);
	ln = zalloc(sizeof(struct line_node));
	DIE_IF(ln == NULL);
	ln->line = line;
	INIT_LIST_HEAD(&ln->list);
	list_add(&ln->list, p);
}

/* Find line range from its line number */
static void find_line_range_by_line(struct line_finder *lf)
{
	Dwarf_Signed cnt, i;
	Dwarf_Line *lines;
	Dwarf_Unsigned lineno = 0;
	Dwarf_Unsigned fno;
	Dwarf_Addr addr;
	int ret;

791
	INIT_LIST_HEAD(&lf->lr->line_list);
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
	ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
	DIE_IF(ret != DW_DLV_OK);

	for (i = 0; i < cnt; i++) {
		ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
		DIE_IF(ret != DW_DLV_OK);
		if (fno != lf->fno)
			continue;

		ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
		DIE_IF(ret != DW_DLV_OK);
		if (lf->lno_s > lineno || lf->lno_e < lineno)
			continue;

		/* Filter line in the function address range */
		if (lf->addr_s && lf->addr_e) {
			ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
			DIE_IF(ret != DW_DLV_OK);
			if (lf->addr_s > addr || lf->addr_e <= addr)
				continue;
		}
		line_range_add_line(lf->lr, (unsigned int)lineno);
	}
	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
	if (!list_empty(&lf->lr->line_list))
		lf->found = 1;
}

/* Search function from function name */
static int linefunc_callback(struct die_link *dlink, void *data)
{
	struct line_finder *lf = (struct line_finder *)data;
	struct line_range *lr = lf->lr;
	Dwarf_Half tag;
	int ret;

	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
	DIE_IF(ret == DW_DLV_ERROR);
	if (tag == DW_TAG_subprogram &&
	    die_compare_name(dlink->die, lr->function) == 0) {
		/* Get the address range of this function */
		ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
		if (ret == DW_DLV_OK)
			ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
		DIE_IF(ret == DW_DLV_ERROR);
		if (ret == DW_DLV_NO_ENTRY) {
			lf->addr_s = 0;
			lf->addr_e = 0;
		}

		lf->fno = die_get_decl_file(dlink->die);
		lr->offset = die_get_decl_line(dlink->die);;
		lf->lno_s = lr->offset + lr->start;
		if (!lr->end)
			lf->lno_e = (Dwarf_Unsigned)-1;
		else
			lf->lno_e = lr->offset + lr->end;
		lr->start = lf->lno_s;
		lr->end = lf->lno_e;
		find_line_range_by_line(lf);
		return 1;
	}
	return 0;
}

static void find_line_range_by_func(struct line_finder *lf)
{
	search_die_from_children(lf->cu_die, linefunc_callback, lf);
}

int find_line_range(int fd, struct line_range *lr)
{
	Dwarf_Half addr_size = 0;
	Dwarf_Unsigned next_cuh = 0;
	int ret;
	struct line_finder lf = {.lr = lr};

	ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
	if (ret != DW_DLV_OK)
		return -ENOENT;

	while (!lf.found) {
		/* Search CU (Compilation Unit) */
		ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
			&addr_size, &next_cuh, &__dw_error);
		DIE_IF(ret == DW_DLV_ERROR);
		if (ret == DW_DLV_NO_ENTRY)
			break;

		/* Get the DIE(Debugging Information Entry) of this CU */
		ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
		DIE_IF(ret != DW_DLV_OK);

		/* Check if target file is included. */
		if (lr->file)
			lf.fno = cu_find_fileno(lf.cu_die, lr->file);

		if (!lr->file || lf.fno) {
			if (lr->function)
				find_line_range_by_func(&lf);
			else {
				lf.lno_s = lr->start;
				if (!lr->end)
					lf.lno_e = (Dwarf_Unsigned)-1;
				else
					lf.lno_e = lr->end;
				find_line_range_by_line(&lf);
			}
			/* Get the real file path */
			if (lf.found)
				cu_get_filename(lf.cu_die, lf.fno, &lr->path);
		}
		dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
	}
	ret = dwarf_finish(__dw_debug, &__dw_error);
	DIE_IF(ret != DW_DLV_OK);
	return lf.found;
}