~crm/inlinecall

9c99dbdee8a1543ec0376eac65e478a293e129f1 — Christos Margiolis 1 year, 4 months ago cae8118
fixed bugs, packed info into a struct
1 files changed, 186 insertions(+), 121 deletions(-)

M inlinecall.c
M inlinecall.c => inlinecall.c +186 -121
@@ 1,9 1,12 @@
#include <sys/queue.h>

#include <dwarf.h>
#include <err.h>
#include <fcntl.h>
#include <libdwarf.h>
#include <libelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>



@@ 12,127 15,28 @@ enum {
	F_INLINE_COPY,
};

static char **srcfiles;
TAILQ_HEAD(, die_info) diehead = TAILQ_HEAD_INITIALIZER(diehead);

static void
print_info(Dwarf_Debug dbg, Dwarf_Die die_root, Dwarf_Die die,
    Dwarf_Off dieoff, int flag)
{
	Dwarf_Ranges *ranges, *rp;
	Dwarf_Attribute attp;
	Dwarf_Addr v_addr;
	Dwarf_Off v_off;
	Dwarf_Unsigned v_udata, line, nbytes;
	Dwarf_Signed nranges;
	Dwarf_Half attr;
	Dwarf_Bool v_flag;
	Dwarf_Error error;
	char *file = NULL;
	int res, i;
struct die_info {
	Dwarf_Off		*addr_lo;
	Dwarf_Off		*addr_hi;
	int			naddrs;
	Dwarf_Unsigned		line;
	const char		*file;
	int			flag;
	TAILQ_ENTRY(die_info)	next;
};

	attr = (flag == F_SUBPROGRAM) ? DW_AT_decl_file : DW_AT_call_file;
	res = dwarf_attr(die, attr, &attp, &error);
	if (res != DW_DLV_OK) {
		if (res == DW_DLV_ERROR)
			warnx("%s", dwarf_errmsg(error));
		goto skip;
	}
	if (dwarf_formudata(attp, &v_udata, &error) != DW_DLV_OK) {
		warnx("%s", dwarf_errmsg(error));
		return;
	}
	file = srcfiles[v_udata - 1];
static char **srcfiles;

	attr = (flag == F_SUBPROGRAM) ? DW_AT_decl_line: DW_AT_call_line;
	res = dwarf_attr(die, attr, &attp, &error);
	if (res != DW_DLV_OK) {
		if (res == DW_DLV_ERROR)
			warnx("%s", dwarf_errmsg(error));
		goto skip;
	}
	if (dwarf_formudata(attp, &line, &error) != DW_DLV_OK) {
		warnx("%s", dwarf_errmsg(error));
		return;
	}
static void *
emalloc(size_t nb)
{
	void *p;

skip:
	if (flag == F_INLINE_COPY) {
		if (dwarf_hasattr(die, DW_AT_ranges, &v_flag, &error) !=
		    DW_DLV_OK) {
			warnx("%s", dwarf_errmsg(error));
			return;
		}
		if (v_flag) {
			/* DIE has ranges */
			res = dwarf_attr(die, DW_AT_ranges, &attp, &error);
			if (res != DW_DLV_OK) {
				if (res == DW_DLV_ERROR)
					warnx("%s", dwarf_errmsg(error));
				return;
			}
			if (dwarf_global_formref(attp, &v_off, &error) !=
			    DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				return;
			}
			if (dwarf_get_ranges(dbg, v_off, &ranges, &nranges,
			    &nbytes, &error) != DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				return;
			}
			for (i = 0; i < nranges - 1; i++) {
				rp = &ranges[i];
				res = dwarf_attr(die_root, DW_AT_low_pc, &attp,
				    &error);
				if (res != DW_DLV_OK) {
					if (res == DW_DLV_ERROR)
						warnx("%s", dwarf_errmsg(error));
					break;
				}
				if (dwarf_formaddr(attp, &v_addr, &error) !=
				    DW_DLV_OK) {
					warnx("%s", dwarf_errmsg(error));
					break;
				}
				printf("\t[0x%jx - 0x%jx]",
				    v_addr + rp->dwr_addr1,
				    v_addr + rp->dwr_addr2);
				if (file != NULL)
					printf("\t%s:%lu\n", file, line);
			}
			dwarf_ranges_dealloc(dbg, ranges, nranges);
		} else {
			/* DIE has high/low PC boundaries */
			res = dwarf_attr(die, DW_AT_low_pc, &attp, &error);
			if (res != DW_DLV_OK) {
				if (res == DW_DLV_ERROR)
					warnx("%s", dwarf_errmsg(error));
				return;
			}
			if (dwarf_formaddr(attp, &v_addr, &error) != DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				return;
			}
			res = dwarf_attr(die, DW_AT_high_pc, &attp, &error);
			if (res != DW_DLV_OK) {
				if (res == DW_DLV_ERROR)
					warnx("%s", dwarf_errmsg(error));
				return;
			}
			if (dwarf_formudata(attp, &v_udata, &error) !=
			    DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				return;
			}
			printf("\t[0x%jx - 0x%jx]", v_addr, v_addr + v_udata);
			if (file != NULL)
				printf("\t%s:%lu\n", file, line);
			else
				putchar('\n');
		}
	} else {
		printf("%s:%lu\n", file, line);
	}
	if ((p = malloc(nb)) == NULL)
		err(1, "malloc");
	return (p);
}

static void


@@ 140,16 44,21 @@ parse_die(Dwarf_Debug dbg, Dwarf_Die die, void *data, int level, int flag)
{
	static Dwarf_Die die_root;
	Dwarf_Die die_next;
	Dwarf_Ranges *ranges, *rp;
	Dwarf_Attribute attp, *attr_list;
	Dwarf_Off dieoff, cuoff, culen, v_off;
	Dwarf_Addr v_addr;
	Dwarf_Unsigned v_udata;
	Dwarf_Signed nattr, v_sdata;
	Dwarf_Addr *addr_lo, *addr_hi;
	Dwarf_Unsigned v_udata, line, nbytes;
	Dwarf_Signed nattr, v_sdata, nranges;
	Dwarf_Half tag, attr, form;
	Dwarf_Bool v_flag;
	Dwarf_Error error;
	struct die_info *di;
	const char *str;
	char *file = NULL;
	char *v_str;
	int naddrs;
	int res, i, found = 0;

	/* Save the root DIE so that we can re-parse it. */


@@ 210,13 119,137 @@ parse_die(Dwarf_Debug dbg, Dwarf_Die die, void *data, int level, int flag)
		v_off += cuoff;
		if (v_off != (Dwarf_Off)data)
			goto cont;
		if (dwarf_hasattr(die, DW_AT_ranges, &v_flag, &error) !=
		    DW_DLV_OK) {
			warnx("%s", dwarf_errmsg(error));
			goto cont;
		}
		if (v_flag) {
			/* DIE has ranges */
			res = dwarf_attr(die, DW_AT_ranges, &attp, &error);
			if (res != DW_DLV_OK) {
				if (res == DW_DLV_ERROR)
					warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			if (dwarf_global_formref(attp, &v_off, &error) !=
			    DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			if (dwarf_get_ranges(dbg, v_off, &ranges, &nranges,
			    &nbytes, &error) != DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			naddrs = nranges - 1;
			addr_lo = emalloc(naddrs * sizeof(Dwarf_Off));
			addr_hi = emalloc(naddrs * sizeof(Dwarf_Off));
			for (i = 0; i < naddrs; i++) {
				rp = &ranges[i];
				res = dwarf_attr(die_root, DW_AT_low_pc, &attp,
				    &error);
				if (res != DW_DLV_OK) {
					if (res == DW_DLV_ERROR)
						warnx("%s", dwarf_errmsg(error));
					break;
				}
				if (dwarf_formaddr(attp, &v_addr, &error) !=
				    DW_DLV_OK) {
					warnx("%s", dwarf_errmsg(error));
					break;
				}
				addr_lo[i] = v_addr + rp->dwr_addr1;
				addr_hi[i] = v_addr + rp->dwr_addr2;
			}
			dwarf_ranges_dealloc(dbg, ranges, nranges);
		} else {
			/* DIE has high/low PC boundaries */
			res = dwarf_attr(die, DW_AT_low_pc, &attp, &error);
			if (res != DW_DLV_OK) {
				if (res == DW_DLV_ERROR)
					warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			if (dwarf_formaddr(attp, &v_addr, &error) != DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			res = dwarf_attr(die, DW_AT_high_pc, &attp, &error);
			if (res != DW_DLV_OK) {
				if (res == DW_DLV_ERROR)
					warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			if (dwarf_formudata(attp, &v_udata, &error) !=
			    DW_DLV_OK) {
				warnx("%s", dwarf_errmsg(error));
				goto cont;
			}
			naddrs = 1;
			addr_lo = emalloc(sizeof(Dwarf_Off));
			addr_hi = emalloc(sizeof(Dwarf_Off));
			addr_lo[0] = v_addr;
			addr_hi[0] = v_addr + v_udata;
		}
	} else
		goto cont;
	print_info(dbg, die_root, die, dieoff, flag);

	/* Get file:line */
	attr = (flag == F_SUBPROGRAM) ? DW_AT_decl_file : DW_AT_call_file;
	res = dwarf_attr(die, attr, &attp, &error);
	if (res != DW_DLV_OK) {
		if (res == DW_DLV_ERROR)
			warnx("%s", dwarf_errmsg(error));
		goto skip;
	}
	if (dwarf_formudata(attp, &v_udata, &error) != DW_DLV_OK) {
		warnx("%s", dwarf_errmsg(error));
		goto cont;
	}
	file = srcfiles[v_udata - 1];

	attr = (flag == F_SUBPROGRAM) ? DW_AT_decl_line: DW_AT_call_line;
	res = dwarf_attr(die, attr, &attp, &error);
	if (res != DW_DLV_OK) {
		if (res == DW_DLV_ERROR)
			warnx("%s", dwarf_errmsg(error));
		goto skip;
	}
	if (dwarf_formudata(attp, &line, &error) != DW_DLV_OK) {
		warnx("%s", dwarf_errmsg(error));
		goto cont;
	}
skip:
	di = emalloc(sizeof(struct die_info));
	di->flag = flag;
	if (file != NULL) {
		di->file = file;
		di->line = line;
	} else
		di->file = NULL;
	if (di->flag == F_INLINE_COPY) {
		di->naddrs = naddrs;
		di->addr_lo = addr_lo;
		di->addr_hi = addr_hi;
	}
	TAILQ_INSERT_TAIL(&diehead, di, next);
cont:
	/*
	 * Inline copies might appear before the declaration, so we need to
	 * re-parse the CU.
	 *
	 * The rationale for choosing to re-parse the CU instead of using a
	 * hash table of DIEs is that, because we re-parse only when an inline
	 * definition of the function we want is found. This means that,
	 * statistically, we won't have to re-parse many times at all
	 * considering that only a handful of CUs will define the function,
	 * whereas if we have used a hash table, we would first need to parse
	 * the whole CU at once and store all DW_TAG_inlined_subroutine DIEs
	 * (so that we can match them afterwards). In this case, we always have
	 * to "parse" twice -- first the CU, then the DIE table -- and also,
	 * the program would use much more memory since we would have allocated
	 * DIEs, which most of them would never be used.
	 */
	if (found) {
		die = die_root;


@@ 245,6 278,36 @@ cont:
		dwarf_dealloc(dbg, die, DW_DLA_DIE);
}

static void
print_info(void)
{
	struct die_info *di;
	int i;

	/* Clean up as well */
	while (!TAILQ_EMPTY(&diehead)) {
		di = TAILQ_FIRST(&diehead);
		TAILQ_REMOVE(&diehead, di, next);
		if (di->flag == F_INLINE_COPY) {
			for (i = 0; i < di->naddrs; i++) {
				printf("\t[0x%jx - 0x%jx]",
				    di->addr_lo[i],
				    di->addr_hi[i]);
				if (di->file != NULL) {
					printf("\t%s:%lu\n",
					    di->file, di->line);
				} else
					putchar('\n');
			}
			free(di->addr_lo);
			free(di->addr_hi);
		} else if (di->flag == F_SUBPROGRAM) {
			printf("%s:%lu\n", di->file, di->line);
		}
		free(di);
	}
}

int
main(int argc, char *argv[])
{


@@ 280,6 343,7 @@ main(int argc, char *argv[])
		while ((res = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL,
		    NULL, &error)) == DW_DLV_OK) {
			die = NULL;
			TAILQ_INIT(&diehead);
			while (dwarf_siblingof(dbg, die, &die, &error) ==
			    DW_DLV_OK) {
				srcfiles = NULL;


@@ 287,8 351,9 @@ main(int argc, char *argv[])
				    &error) != DW_DLV_OK)
					warnx("%s", dwarf_errmsg(error));
				parse_die(dbg, die, func, 0, F_SUBPROGRAM);
				dwarf_dealloc(dbg, die, DW_DLA_DIE);
			}
			dwarf_dealloc(dbg, die, DW_DLA_DIE);
			print_info();
		}
		if (res == DW_DLV_ERROR)
			warnx("%s", dwarf_errmsg(error));