Hi all,
I've been using D under Ubuntu for some time and the kinds of backtraces
I get when assertion fails look like this:
core.exception.asserter...@doodle.dia.standard_tools(71): Assertion failure
----------------
./bin/doodler() [0x8102086]
./bin/doodler() [0x804e38f]
./bin/doodler() [0x81022b1]
./bin/doodler() [0x81021d9]
./bin/doodler() [0x8102188]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x289bd6]
./bin/doodler() [0x8049a01]
And for segment faults I don't get a stack trace at all.
I am interested in knowing if this is the case for other users and if
so, what is being done about it in druntime?
I've attached my own implementation of backtrace that I find useful. It
is quick and dirty, but it works. It gives an output like the following
when exceptions are thrown and segmentation faults triggered:
core.exception.asserter...@doodle.dia.standard_tools(71): Assertion failure
----------------
[0x0810bbf8] onAssertError
[0x081022c6] _d_assertm
[0x0804e43f] void doodle.dia.standard_tools.__assert(int)
[0x0804dfb8] bool doodle.dia.standard_tools.ZoomTool.handle_scroll(class
doodle.dia.icanvas.Viewport, const(class doodle.tk.events.ScrollEvent))
[0x0804e951] bool doodle.dia.tool_layer.ToolLayer.handle_scroll(class
doodle.dia.icanvas.Viewport, const(class doodle.tk.events.ScrollEvent))
[0x0804b607] bool doodle.gtk.canvas.Canvas.on_scroll(struct
gtkc.gdktypes.GdkEventScroll*, class gtk.Widget.Widget)
[0x080672c5] extern (C) int gtk.Widget.Widget.callBackScroll(struct
gtkc.gtktypes.GtkWidget*, struct gtkc.gdktypes.GdkEventScroll*, class
gtk.Widget.Widget)
[0x08062155] void gtk.Main.Main.run()
[0x08049e7b] _Dmain
[0x08102594] extern (C) int rt.dmain2.main(int, char**) . void runMain()
[0x081024f9] extern (C) int rt.dmain2.main(int, char**) . void
tryExec(void delegate())
Using it couldn't be easier, just import the module and link the object.
Regards,
Dave
module backtrace;
//
// Provides support for a readable backtrace on a program crash.
//
// Everything is private - you build this into a library and
// link to the library, and bingo (via static this).
//
// It works by registering a stacktrace handler with the runtime,
// which, unlike the default one, provides demangled symbols
// rather than just a list of addresses.
//
private {
import core.sys.posix.unistd;
import core.sys.posix.fcntl;
import core.sys.posix.sys.stat;
import core.sys.posix.sys.mman;
import core.stdc.string;
import core.stdc.signal;
import core.runtime;
import std.stdio;
import std.string;
import std.demangle;
import std.algorithm;
immutable int EI_NIDENT = 16;
immutable int SHT_SYMTAB = 2;
immutable int SHT_STRTAB = 3;
immutable int STT_FUNC = 2;
alias ushort Elf32_Half;
alias uint Elf32_Word;
alias uint Elf32_Addr;
alias uint Elf32_Off;
alias ushort Elf32_Section;
struct Elf32_Ehdr {
ubyte[EI_NIDENT] e_ident; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
};
struct Elf32_Shdr {
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
};
struct Elf32_Sym {
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
ubyte st_info; /* Symbol type and binding */
ubyte st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
};
extern (C) int backtrace(void**, size_t);
struct Symbol {
this(void * a, size_t s, char * n) {
address = a;
size = s;
name = n;
}
void * address;
size_t size;
char * name;
void * end_address() { return address + size; }
};
// Newton-Raphson
Symbol nr_lookup(Symbol[] symbols, void * addr, int depth = 0) {
if (symbols.length == 0 ||
addr < symbols[0].address ||
addr >= symbols[$-1].end_address)
{
throw new Exception("Symbol not found");
}
else if (symbols.length == 1) {
return symbols[0];
}
else {
void * begin_addr = symbols[0].address;
void * end_addr = symbols[$-1].end_address;
int i = ((addr - begin_addr) * symbols.length) / (end_addr - begin_addr);
if (!(depth % 2) && i < symbols.length - 1) { ++i; } // Some wiggle to force convergence
if (addr < symbols[i].address) {
return nr_lookup(symbols[0..i], addr, depth + 1);
}
else {
return nr_lookup(symbols[i..$], addr, depth + 1);
}
}
}
int generate(void*[] addresses, scope int delegate(ref char[]) dg) {
static char[] get_exe() {
char buf[1024];
ssize_t s = readlink("/proc/self/exe", buf.ptr, buf.length);
if (s == -1) { throw new Exception(""); }
return buf[0..s];
}
int fd = open(toStringz(get_exe()), O_RDONLY);
if (fd == -1) { throw new Exception(""); }
scope(exit) close(fd);
stat_t st;
if (fstat(fd, &st) == -1) { throw new Exception(""); }
void * contents = mmap(null, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (!contents) { throw new Exception(""); }
scope(exit) munmap(contents, st.st_size);
Elf32_Ehdr * elf_hdr = cast(Elf32_Ehdr *)(contents);
Elf32_Shdr * sec_hdr = cast(Elf32_Shdr *)(contents + elf_hdr.e_shoff);
int symtab_idx = 0;
for (int i = 0; i < elf_hdr.e_shnum; ++i) {
if (sec_hdr[i].sh_type == SHT_SYMTAB) {
symtab_idx = i;
break;
}
}
if (symtab_idx == 0) { throw new Exception(""); }
int strtab_idx = sec_hdr[symtab_idx].sh_link;
if (strtab_idx == 0) { throw new Exception(""); } // No associated string table
if (sec_hdr[strtab_idx].sh_type != SHT_STRTAB) { throw new Exception(""); } // invalid string table
Elf32_Sym * sym_ent = cast(Elf32_Sym *)(contents + sec_hdr[symtab_idx].sh_offset);
char * strtab = cast(char *)(contents + sec_hdr[strtab_idx].sh_offset);
int num_syms = sec_hdr[symtab_idx].sh_size / sec_hdr[symtab_idx].sh_entsize;
if (num_syms == 0) { throw new Exception(""); } // No symbols
Symbol symbols[];
for (int i = 0; i < num_syms; ++i) {
if ((sym_ent[i].st_info & 0xf) != STT_FUNC) {
continue;
}
if (sym_ent[i].st_shndx == 0) {
continue;
}
void * address = cast(void *)(sym_ent[i].st_value); // inclusive
size_t size = sym_ent[i].st_size;
char * name = strtab + sym_ent[i].st_name;
symbols ~= Symbol(address, size, name);
}
sort!("a.address < b.address")(symbols);
int ret;
foreach (address; addresses) {
string str = format("[0x%0.8x] ", address);
try {
Symbol symbol = nr_lookup(symbols, address);
char[] s1 = symbol.name[0..strlen(symbol.name)];
str ~= format("%s", demangle(s1.idup));
}
catch (Exception e) {
str ~= "<unknown>";
}
char[] cstr = str.dup;
ret = dg(cstr);
if (ret) {
break;
}
}
return ret;
}
// signal handler for otherwise-fatal thread-specific signals
extern (C) void arghh(int sig) {
string name() {
switch (sig) {
case SIGSEGV: return "SIGSEGV";
case SIGFPE: return "SIGFPE";
case SIGILL: return "SIGILL";
case SIGABRT: return "SIGABRT";
default: return "";
}
}
throw new Error(format("Got signal %s %s", sig, name));
}
shared static this() {
// set up shared signal handlers for fatal thread-specific signals
//writeln("setting up shared signal handlers for ABRT, FPE, ILL, SEGV");
signal(SIGABRT, &arghh);
signal(SIGFPE, &arghh);
signal(SIGILL, &arghh);
signal(SIGSEGV, &arghh);
}
static this() {
// register our trace handler for each thread
//writeln("installing our traceHandler");
Runtime.traceHandler = &traceHandler;
}
// Captures backtrace info at the point of construction, stripping
// off the bits that concern itself and the ininteresting early stuff.
// Also provides opApply to traverse a nice text representation of the backtrace.
class TraceInfo : Throwable.TraceInfo {
void*[256] callstack;
int numframes;
this() {
numframes = backtrace(callstack.ptr, callstack.length);
}
override string toString() const {
return "Why does dmd require an override of this?";
}
override int opApply(scope int delegate(ref char[]) dg) {
return generate(callstack[4..numframes-5], dg);
}
}
Throwable.TraceInfo traceHandler(void * ptr = null) {
return new TraceInfo;
}
}