inlinecall

Print call sites of an inline function
git clone git://git.margiolis.net/inlinecall.git
Log | Files | Refs | README | LICENSE

commit 9c99dbdee8a1543ec0376eac65e478a293e129f1
parent cae81184b5b5bea7341d40adc9a9d19314aa63aa
Author: Christos Margiolis <christos@margiolis.net>
Date:   Sun, 12 Feb 2023 23:16:15 +0200

fixed bugs, packed info into a struct

Diffstat:
Minlinecall.c | 307++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
1 file changed, 186 insertions(+), 121 deletions(-)

diff --git a/inlinecall.c b/inlinecall.c @@ -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));