Mercurial > projects > doodle
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 } |