From: Liu Yuan <tailai.ly at taobao.com> We use GCC's stabs to map IP to its symbol(Function Name). Normally, loader doesn't load symbol into memory, we have to manually code ld script to load it. Signed-off-by: Liu Yuan <tailai.ly at taobao.com> --- sheep/trace/stabs.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++ sheep/trace/trace.h | 14 ++++ sheep/trace/trace.ld | 18 +++++ 3 files changed, 230 insertions(+), 0 deletions(-) create mode 100644 sheep/trace/stabs.c create mode 100644 sheep/trace/trace.ld diff --git a/sheep/trace/stabs.c b/sheep/trace/stabs.c new file mode 100644 index 0000000..3f79f96 --- /dev/null +++ b/sheep/trace/stabs.c @@ -0,0 +1,198 @@ +#include <stab.h> +#include <stdint.h> +#include <string.h> + +#include "trace.h" + +/* referrence to the MIT JOS code */ + +/* Entries in the STABS table are formatted as follows */ +struct stab { + uint32_t index; /* index into string table of name */ + uint8_t type; /* type of symbol */ + uint8_t misc; /* misc info (usually empty) */ + uint16_t desc; /* description field */ + uint32_t value; /* value of symbol */ +}; + +extern const struct stab __STAB_BEGIN__[]; +extern const struct stab __STAB_END__[]; +extern const char __STABSTR_BEGIN__[]; +extern const char __STABSTR_END__[]; + +/* + stab_bsearch(stabs, region_left, region_right, type, addr) + + Some stab types are arranged in increasing order by instruction + address. For example, N_FUN stabs (stab entries with type == + N_FUN), which mark functions, and N_SO stabs, which mark source files. + + Given an instruction address, this function finds the single stab + entry of type 'type' that contains that address. + + The search modifies *region_left and *region_right to bracket the + 'addr'. *region_left points to the matching stab that contains + 'addr', and *region_right points just before the next stab. If + *region_left > *region_right, then 'addr' is not contained in any + matching stab. + + For example, given these N_SO stabs: + Index Type Address + 0 SO f0100000 + 13 SO f0100040 + 117 SO f0100176 + 118 SO f0100178 + 555 SO f0100652 + 556 SO f0100654 + 657 SO f0100849 + this code: + left = 0, right = 657; + stab_bsearch(stabs, &left, &right, N_SO, 0xf0100184); + will exit setting left = 118, right = 554. + */ +static notrace void stab_bsearch(const struct stab *stabs, int *region_left, int *region_right, + int type, uintptr_t addr) +{ + int l = *region_left, r = *region_right, any_matches = 0; + + while (l <= r) { + int true_m = (l + r) / 2, m = true_m; + + /* search for earliest stab with right type */ + while (m >= l && stabs[m].type != type) + m--; + if (m < l) { /* no match in [l, m] */ + l = true_m + 1; + continue; + } + + /* actual binary search */ + any_matches = 1; + if (stabs[m].value < addr) { + *region_left = m; + l = true_m + 1; + } else if (stabs[m].value > addr) { + *region_right = m - 1; + r = m - 1; + } else { + /* exact match for 'addr', but continue loop to find + * *region_right + */ + *region_left = m; + l = m; + addr++; + } + } + + if (!any_matches) + *region_right = *region_left - 1; + else { + /* find rightmost region containing 'addr' */ + for (l = *region_right; + l > *region_left && stabs[l].type != type; + l--) + /* do nothing */; + *region_left = l; + } +} + +/* + * Fill in the 'info' structure with information about the specified + * instruction address, 'addr'. + * + * Returns + * 0 if information was found + * -1 if not. + * + * NB: But even if it returns negative it has stored some + * information into '*info'. + */ + +notrace int get_ipinfo(uintptr_t addr, struct ipinfo *info) +{ + const struct stab *stabs, *stab_end; + const char *stabstr, *stabstr_end; + int lfile, rfile, lfun, rfun, lline, rline; + + info->file = "<unknown>"; + info->line = 0; + info->fn_name = "<unknown>"; + info->fn_namelen = 9; + info->fn_addr = addr; + info->fn_narg = 0; + + stabs = __STAB_BEGIN__; + stab_end = __STAB_END__; + stabstr = __STABSTR_BEGIN__; + stabstr_end = __STABSTR_END__; + + if (stabstr_end <= stabstr || stabstr_end[-1] != 0) + return -1; + + /* Now we find the right stabs that define the function containing + * 'eip'. First, we find the basic source file containing 'eip'. + * Then, we look in that source file for the function. Then we look + * for the line number. + */ + + lfile = 0; + rfile = (stab_end - stabs) - 1; + stab_bsearch(stabs, &lfile, &rfile, N_SO, addr); + if (lfile == 0) + return -1; + + lfun = lfile; + rfun = rfile; + stab_bsearch(stabs, &lfun, &rfun, N_FUN, addr); + + if (lfun <= rfun) { + /* stabs[lfun] points to the function name + * in the string table, but check bounds just in case. + */ + if (stabs[lfun].index < stabstr_end - stabstr) + info->fn_name = stabstr + stabs[lfun].index; + info->fn_addr = stabs[lfun].value; + addr -= info->fn_addr; + /* Search within the function definition for the line number. */ + lline = lfun; + rline = rfun; + } else { + /* Couldn't find function stab! Maybe we're in an assembly + * file. Search the whole file for the line number. + */ + info->fn_addr = addr; + lline = lfile; + rline = rfile; + } + /* Ignore stuff after the colon. */ + info->fn_namelen = strchr(info->fn_name, ':') - info->fn_name; + + + /* Search within [lline, rline] for the line number stab. */ + stab_bsearch(stabs, &lline, &rline, N_SLINE, addr); + if (lline <= rline) + info->line = stabs[lline].desc; + else + return -1; + /* Search backwards from the line number for the relevant filename + * stab. + * We can't just use the "lfile" stab because inlined functions + * can interpolate code from a different file! + * Such included source files use the N_SOL stab type. + */ + while (lline >= lfile && + stabs[lline].type != N_SOL && + (stabs[lline].type != N_SO || !stabs[lline].value)) + lline--; + if (lline >= lfile && stabs[lline].index < stabstr_end - stabstr) + info->file = stabstr + stabs[lline].index; + /* Set fn_narg to the number of arguments taken by the function, + * or 0 if there was no containing function. + */ + if (lfun < rfun) + for (lline = lfun + 1; lline < rfun && stabs[lline].type == N_PSYM; + lline++) + info->fn_narg++; + + return 0; +} diff --git a/sheep/trace/trace.h b/sheep/trace/trace.h index 3e675e1..92154c6 100644 --- a/sheep/trace/trace.h +++ b/sheep/trace/trace.h @@ -1,6 +1,20 @@ #ifndef TRACE_H #define TRACE_H +#include "util.h" + +struct ipinfo { + const char *file; /* Source code filename for EIP */ + int line; /* Source code linenumber for EIP */ + const char *fn_name; /* Name of function containing EIP */ + int fn_namelen; /* Length of function name */ + unsigned long fn_addr; /* Address of start of function */ + int fn_narg; /* Number of function arguments */ +}; + +/* stabs.c */ +extern int get_ipinfo(unsigned long ip, struct ipinfo *info); + /* mcount.S */ extern void mcount(void); extern void mcount_call(void); diff --git a/sheep/trace/trace.ld b/sheep/trace/trace.ld new file mode 100644 index 0000000..031ada7 --- /dev/null +++ b/sheep/trace/trace.ld @@ -0,0 +1,18 @@ +SECTIONS +{ + stab : { + PROVIDE(__STAB_BEGIN__ = .); + *(.stab); + PROVIDE(__STAB_END__ = .); + BYTE(0) /* Force linker allocate memeory for this section */ + } + + stabstr : { + PROVIDE(__STABSTR_BEGIN__ = .); + *(.stabstr); + PROVIDE(__STABSTR_END__ = .); + BYTE(0) + } +} + +INSERT AFTER .rodata -- 1.7.8.2 |