comparison doodle/core/backtrace.d @ 44:2b9329ed0f0e

Added backtrace support
author "David Bryant <bagnose@gmail.com>"
date Sun, 01 Aug 2010 02:06:14 +0930
parents
children 01bbf3f6f966
comparison
equal deleted inserted replaced
43:d0604b062db8 44:2b9329ed0f0e
1 module doodle.core.backtrace;
2
3 //
4 // Provides support for a readable backtrace on a program crash.
5 //
6 // Everything is private - you build this into a library and
7 // link to the library, and bingo (via static this).
8 //
9 // It works by registering a stacktrace handler with the runtime,
10 // which, unlike the default one, provides demangled symbols
11 // rather than just a list of addresses.
12 //
13
14 private {
15 import core.sys.posix.unistd;
16 import core.sys.posix.fcntl;
17 import core.sys.posix.sys.stat;
18 import core.sys.posix.sys.mman;
19
20 import core.stdc.string;
21 import core.stdc.signal;
22
23 import core.runtime;
24
25 import std.stdio;
26 import std.string;
27 import std.demangle;
28 import std.algorithm;
29
30 immutable int EI_NIDENT = 16;
31
32 immutable int SHT_SYMTAB = 2;
33 immutable int SHT_STRTAB = 3;
34
35 immutable int STT_FUNC = 2;
36
37 alias ushort Elf32_Half;
38 alias uint Elf32_Word;
39 alias uint Elf32_Addr;
40 alias uint Elf32_Off;
41 alias ushort Elf32_Section;
42
43 struct Elf32_Ehdr {
44 ubyte[EI_NIDENT] e_ident; /* Magic number and other info */
45 Elf32_Half e_type; /* Object file type */
46 Elf32_Half e_machine; /* Architecture */
47 Elf32_Word e_version; /* Object file version */
48 Elf32_Addr e_entry; /* Entry point virtual address */
49 Elf32_Off e_phoff; /* Program header table file offset */
50 Elf32_Off e_shoff; /* Section header table file offset */
51 Elf32_Word e_flags; /* Processor-specific flags */
52 Elf32_Half e_ehsize; /* ELF header size in bytes */
53 Elf32_Half e_phentsize; /* Program header table entry size */
54 Elf32_Half e_phnum; /* Program header table entry count */
55 Elf32_Half e_shentsize; /* Section header table entry size */
56 Elf32_Half e_shnum; /* Section header table entry count */
57 Elf32_Half e_shstrndx; /* Section header string table index */
58 };
59
60 struct Elf32_Shdr {
61 Elf32_Word sh_name; /* Section name (string tbl index) */
62 Elf32_Word sh_type; /* Section type */
63 Elf32_Word sh_flags; /* Section flags */
64 Elf32_Addr sh_addr; /* Section virtual addr at execution */
65 Elf32_Off sh_offset; /* Section file offset */
66 Elf32_Word sh_size; /* Section size in bytes */
67 Elf32_Word sh_link; /* Link to another section */
68 Elf32_Word sh_info; /* Additional section information */
69 Elf32_Word sh_addralign; /* Section alignment */
70 Elf32_Word sh_entsize; /* Entry size if section holds table */
71 };
72
73 struct Elf32_Sym {
74 Elf32_Word st_name; /* Symbol name (string tbl index) */
75 Elf32_Addr st_value; /* Symbol value */
76 Elf32_Word st_size; /* Symbol size */
77 ubyte st_info; /* Symbol type and binding */
78 ubyte st_other; /* Symbol visibility */
79 Elf32_Section st_shndx; /* Section index */
80 };
81
82 extern (C) int backtrace(void**, size_t);
83
84 struct Symbol {
85 this(void * a, size_t s, char * n) {
86 address = a;
87 size = s;
88 name = n;
89 }
90 void * address;
91 size_t size;
92 char * name;
93 void * end_address() { return address + size; }
94 };
95
96 // Linear traversal
97 Symbol lin_lookup(Symbol[] symbols, void * addr) {
98 foreach(s; symbols) {
99 if (addr >= s.address && addr < s.end_address) {
100 return s;
101 }
102 }
103 throw new Exception("Symbol not found");
104 }
105
106 // Bisection
107 Symbol bi_lookup(Symbol[] symbols, void * addr, int depth = 1) {
108 if (symbols.length == 0 ||
109 addr < symbols[0].address ||
110 addr >= symbols[$-1].end_address)
111 {
112 throw new Exception("Symbol not found");
113 }
114 else if (symbols.length == 1) {
115 //writefln("Depth: %s", depth);
116 return symbols[0];
117 }
118 else {
119 int i = symbols.length / 2;
120
121 if (addr < symbols[i].address) {
122 return bi_lookup(symbols[0..i], addr, depth + 1);
123 }
124 else {
125 return bi_lookup(symbols[i..$], addr, depth + 1);
126 }
127 }
128 }
129
130 // Newton-Raphson
131 Symbol nr_lookup(Symbol[] symbols, void * addr, int depth = 0) {
132 if (symbols.length == 0 ||
133 addr < symbols[0].address ||
134 addr >= symbols[$-1].end_address)
135 {
136 throw new Exception("Symbol not found");
137 }
138 else if (symbols.length == 1) {
139 //writefln("Depth: %s", depth);
140 return symbols[0];
141 }
142 else {
143 void * begin_addr = symbols[0].address;
144 void * end_addr = symbols[$-1].end_address;
145
146 int i = ((addr - begin_addr) * symbols.length) / (end_addr - begin_addr);
147 if (!(depth % 2) && i < symbols.length - 1) { ++i; }
148
149 /*
150 writefln("depth %s, index %s, size %s, left %s, right %s",
151 depth, i, symbols.length, (addr - begin_addr), (end_addr - addr));
152 writefln("depth %s, factor %s, length %s", depth, f, symbols.length);
153 */
154
155 if (addr < symbols[i].address) {
156 return nr_lookup(symbols[0..i], addr, depth + 1);
157 }
158 else {
159 return nr_lookup(symbols[i..$], addr, depth + 1);
160 }
161 }
162 }
163
164 int generate(void*[] addresses, scope int delegate(ref char[]) dg) {
165 static char[] get_exe() {
166 char buf[1024];
167 ssize_t s = readlink("/proc/self/exe", buf.ptr, buf.length);
168 if (s == -1) { throw new Exception(""); }
169 return buf[0..s];
170 }
171
172 int fd = open(toStringz(get_exe()), O_RDONLY);
173 if (fd == -1) { throw new Exception(""); }
174 scope(exit) close(fd);
175
176 stat_t st;
177 if (fstat(fd, &st) == -1) { throw new Exception(""); }
178
179 void * contents = mmap(null, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
180 if (!contents) { throw new Exception(""); }
181 scope(exit) munmap(contents, st.st_size);
182
183 Elf32_Ehdr * elf_hdr = cast(Elf32_Ehdr *)(contents);
184
185 Elf32_Shdr * sec_hdr = cast(Elf32_Shdr *)(contents + elf_hdr.e_shoff);
186
187 int symtab_idx = 0;
188 for (int i = 0; i < elf_hdr.e_shnum; ++i) {
189 if (sec_hdr[i].sh_type == SHT_SYMTAB) {
190 symtab_idx = i;
191 break;
192 }
193 }
194 if (symtab_idx == 0) { throw new Exception(""); }
195
196 int strtab_idx = sec_hdr[symtab_idx].sh_link;
197 if (strtab_idx == 0) { throw new Exception(""); } // No associated string table
198
199 if (sec_hdr[strtab_idx].sh_type != SHT_STRTAB) { throw new Exception(""); } // invalid string table
200
201 Elf32_Sym * sym_ent = cast(Elf32_Sym *)(contents + sec_hdr[symtab_idx].sh_offset);
202 char * strtab = cast(char *)(contents + sec_hdr[strtab_idx].sh_offset);
203 int num_syms = sec_hdr[symtab_idx].sh_size / sec_hdr[symtab_idx].sh_entsize;
204
205 if (num_syms == 0) { throw new Exception(""); } // No symbols
206
207 Symbol symbols[];
208
209 for (int i = 0; i < num_syms; ++i) {
210 if ((sym_ent[i].st_info & 0xf) != STT_FUNC) {
211 continue;
212 }
213
214 if (sym_ent[i].st_shndx == 0) {
215 continue;
216 }
217
218 void * address = cast(void *)(sym_ent[i].st_value); // inclusive
219 size_t size = sym_ent[i].st_size;
220 char * name = strtab + sym_ent[i].st_name;
221
222 symbols ~= Symbol(address, size, name);
223 }
224
225 sort!("a.address < b.address")(symbols);
226
227 int ret;
228 foreach (address; addresses) {
229 string str = format("[0x%0.8x] ", address);
230 try {
231 Symbol symbol = nr_lookup(symbols, address);
232 char[] s1 = symbol.name[0..strlen(symbol.name)];
233 str ~= format("%s", demangle(s1.idup));
234 }
235 catch (Exception e) {
236 str ~= "<unknown>";
237 }
238 char[] cstr = str.dup;
239 ret = dg(cstr);
240 if (ret) {
241 break;
242 }
243 }
244
245 return ret;
246 }
247
248
249 // signal handler for otherwise-fatal thread-specific signals
250 extern (C) void arghh(int sig) {
251 string name() {
252 switch (sig) {
253 case SIGSEGV: return "SIGSEGV";
254 case SIGFPE: return "SIGFPE";
255 case SIGILL: return "SIGILL";
256 case SIGABRT: return "SIGABRT";
257 default: return "";
258 }
259 }
260
261 throw new Error(format("Got signal %s %s", sig, name));
262 }
263
264 shared static this() {
265 // set up shared signal handlers for fatal thread-specific signals
266 //writeln("setting up shared signal handlers for ABRT, FPE, ILL, SEGV");
267 signal(SIGABRT, &arghh);
268 signal(SIGFPE, &arghh);
269 signal(SIGILL, &arghh);
270 signal(SIGSEGV, &arghh);
271 }
272
273 static this() {
274 // register our trace handler for each thread
275 //writeln("installing our traceHandler");
276 Runtime.traceHandler = &traceHandler;
277 }
278
279 // Captures backtrace info at the point of construction, stripping
280 // off the bits that concern itself and the ininteresting early stuff.
281 // Also provides opApply to traverse a nice text representation of the backtrace.
282 class TraceInfo : Throwable.TraceInfo {
283 void*[256] callstack;
284 int numframes;
285
286 this() {
287 numframes = backtrace(callstack.ptr, callstack.length);
288 }
289
290 override string toString() const {
291 return "Why does dmd require an override of this?";
292 }
293
294 override int opApply(scope int delegate(ref char[]) dg) {
295 return generate(callstack[4..numframes-5], dg);
296 }
297 }
298
299 Throwable.TraceInfo traceHandler(void * ptr = null) {
300 return new TraceInfo;
301 }
302 }