[Sheepdog] [PATCH v4 03/12] trace: add low level ip2function support

Liu Yuan namei.unix at gmail.com
Tue Apr 10 09:01:58 CEST 2012


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




More information about the sheepdog mailing list