From 9a71bb00bb39ca4cc200df36b2d5bfd591169bef Mon Sep 17 00:00:00 2001 From: Luuk van Dijk Date: Fri, 3 Dec 2010 19:19:33 +0100 Subject: [PATCH] [68]l: generate debug info for builtin structured types. prettyprinting in gdb. R=rsc CC=golang-dev https://golang.org/cl/3309041 --- src/cmd/ld/dwarf.c | 635 +++++++++++++++++++++++++++------ src/pkg/runtime/runtime-gdb.py | 174 +++++++++ 2 files changed, 700 insertions(+), 109 deletions(-) create mode 100644 src/pkg/runtime/runtime-gdb.py diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 95ae0b59259..9342e1e284a 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// TODO: +// TODO/NICETOHAVE: // - eliminate DW_CLS_ if not used // - package info in compilation units // - assign global variables and types to their packages @@ -10,6 +10,7 @@ // - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg // ptype struct '[]uint8' and qualifiers need to be quoted away // - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. +// - file:line info for variables // #include "l.h" #include "lib.h" @@ -26,7 +27,7 @@ static vlong abbrevo; static vlong abbrevsize; static vlong lineo; static vlong linesize; -static vlong infoo; // also the base for DWDie->offs and reference attributes. +static vlong infoo; // also the base for DWDie->offs and reference attributes. static vlong infosize; static vlong frameo; static vlong framesize; @@ -36,6 +37,10 @@ static vlong pubtypeso; static vlong pubtypessize; static vlong arangeso; static vlong arangessize; +static vlong gdbscripto; +static vlong gdbscriptsize; + +static char gdbscript[1024]; /* * Basic I/O @@ -72,7 +77,6 @@ uleb128enc(uvlong v, char* dst) return len; }; - static int sleb128enc(vlong v, char *dst) { @@ -133,6 +137,8 @@ enum DW_ABRV_AUTO, DW_ABRV_PARAM, DW_ABRV_STRUCTFIELD, + DW_ABRV_FUNCTYPEPARAM, + DW_ABRV_DOTDOTDOT, DW_ABRV_ARRAYRANGE, DW_ABRV_NULLTYPE, DW_ABRV_BASETYPE, @@ -203,12 +209,25 @@ static struct DWAbbrev { }, /* STRUCTFIELD */ { - DW_TAG_member, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_data_member_location, DW_FORM_block1, + DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_data_member_location, DW_FORM_block1, DW_AT_type, DW_FORM_ref_addr, 0, 0 }, + /* FUNCTYPEPARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* DOTDOTDOT */ + { + DW_TAG_unspecified_parameters, DW_CHILDREN_no, + 0, 0 + }, /* ARRAYRANGE */ { DW_TAG_subrange_type, DW_CHILDREN_no, @@ -246,14 +265,16 @@ static struct DWAbbrev { /* CHANTYPE */ { DW_TAG_typedef, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, 0, 0 }, /* FUNCTYPE */ { - DW_TAG_typedef, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, + DW_TAG_subroutine_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, +// DW_AT_type, DW_FORM_ref_addr, 0, 0 }, @@ -268,6 +289,7 @@ static struct DWAbbrev { { DW_TAG_typedef, DW_CHILDREN_no, DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, 0, 0 }, @@ -280,7 +302,6 @@ static struct DWAbbrev { }, /* SLICETYPE */ - // Children are data, len and cap of runtime::struct Slice. { DW_TAG_structure_type, DW_CHILDREN_yes, DW_AT_name, DW_FORM_string, @@ -289,7 +310,6 @@ static struct DWAbbrev { }, /* STRINGTYPE */ - // Children are str and len of runtime::struct String. { DW_TAG_structure_type, DW_CHILDREN_yes, DW_AT_name, DW_FORM_string, @@ -431,6 +451,19 @@ getattr(DWDie *die, uint8 attr) return nil; } +static void +delattr(DWDie *die, uint8 attr) +{ + DWAttr **a; + + a = &die->attr; + while (*a != nil) + if ((*a)->atr == attr) + *a = (*a)->link; + else + a = &((*a)->link); +} + // Every DIE has at least a DW_AT_name attribute (but it will only be // written out if it is listed in the abbrev). If its parent is // keeping an index, the new DIE will be inserted there. @@ -483,6 +516,7 @@ find(DWDie *die, char* name) if (a == nil) return nil; + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) return a; @@ -505,7 +539,6 @@ static DWDie* find_or_diag(DWDie *die, char* name) { DWDie *r; - r = find(die, name); if (r == nil) diag("dwarf find: %s has no %s", getattr(die, DW_AT_name)->data, name); @@ -626,6 +659,7 @@ putattrs(int abbrev, DWAttr* attr) for( ; attr; attr = attr->link) if (attr->atr < nelem(attrs)) attrs[attr->atr] = attr; + for(af = abbrevs[abbrev].attr; af->attr; af++) if (attrs[af->attr]) putattr(af->form, @@ -700,6 +734,8 @@ newmemberoffsetattr(DWDie *die, int32 offs) memmove(die->attr->data, block, i); } +// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a +// location expression that evals to a const. static void newabslocexprattr(DWDie *die, vlong addr) { @@ -750,14 +786,14 @@ enum { KindNoPointers = 1<<7, }; -static Sym* +static Reloc* decode_reloc(Sym *s, int32 off) { int i; for (i = 0; i < s->nr; i++) if (s->r[i].off == off) - return s->r[i].sym; + return s->r + i; return nil; } @@ -804,11 +840,11 @@ decodetype_size(Sym *s) return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 } -// Type.ArrayType.elem +// Type.ArrayType.elem and Type.SliceType.Elem static Sym* decodetype_arrayelem(Sym *s) { - return decode_reloc(s, 5*PtrSize + 8); // 0x1c / 0x30 + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 } static vlong @@ -821,7 +857,64 @@ decodetype_arraylen(Sym *s) static Sym* decodetype_ptrelem(Sym *s) { - return decode_reloc(s, 5*PtrSize + 8); // 0x1c / 0x30 + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.MapType.key, elem +static Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} +static Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc(s, 6*PtrSize + 8)->sym; // 0x20 / 0x38 +} + +// Type.ChanType.elem +static Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +static int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[5*PtrSize + 8]; +} + +// Type.FuncType.in.len +static int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + 7*PtrSize + 8, 4); +} + +static int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + 8*PtrSize + 16, 4); +} + +static Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 6*PtrSize + 8); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +static Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 7*PtrSize + 16); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; } // Type.StructType.fields.Slice::len @@ -835,20 +928,21 @@ decodetype_structfieldcount(Sym *s) static char* decodetype_structfieldname(Sym *s, int i) { - Sym *p; - p = decode_reloc(s, 6*PtrSize + 0x10 + i*5*PtrSize); // go.string."foo" 0x28 / 0x40 - if (p == nil) // embedded structs have a nil name. + Reloc* r; + + r = decode_reloc(s, 6*PtrSize + 0x10 + i*5*PtrSize); // go.string."foo" 0x28 / 0x40 + if (r == nil) // embedded structs have a nil name. return nil; - p = decode_reloc(p, 0); // string."foo" - if (p == nil) // shouldn't happen. + r = decode_reloc(r->sym, 0); // string."foo" + if (r == nil) // shouldn't happen. return nil; - return (char*)p->p; // the c-string + return (char*)r->sym->p; // the c-string } static Sym* decodetype_structfieldtype(Sym *s, int i) { - return decode_reloc(s, 8*PtrSize + 0x10 + i*5*PtrSize); // 0x30 / 0x50 + return decode_reloc(s, 8*PtrSize + 0x10 + i*5*PtrSize)->sym; // 0x30 / 0x50 } static vlong @@ -865,13 +959,15 @@ enum { DW_AT_internal_location = 253, // params and locals }; +static DWDie* defptrto(DWDie *dwtype); // below + // Define gotype, for composite ones recurse into constituents. static DWDie* defgotype(Sym *gotype) { - DWDie *die, *fld, *elem, *ptrelem; + DWDie *die, *fld; Sym *s; - char *name, *ptrname, *f; + char *name, *f; uint8 kind; vlong bytesize; int i, nfields; @@ -891,18 +987,18 @@ defgotype(Sym *gotype) if (0 && debug['v'] > 2) { print("new type: %s @0x%08x [%d]", gotype->name, gotype->value, gotype->size); - for (i = 0; i < gotype->size; ++i) { + for (i = 0; i < gotype->size; i++) { if (!(i%8)) print("\n\t%04x ", i); print("%02x ", gotype->p[i]); } print("\n"); - for (i = 0; i < gotype->nr; ++i) { - print("\t%02x %d %d %lld %s\n", + for (i = 0; i < gotype->nr; i++) { + print("\t0x%02x[%x] %d %s[%llx]\n", gotype->r[i].off, gotype->r[i].siz, gotype->r[i].type, - gotype->r[i].add, - gotype->r[i].sym->name); + gotype->r[i].sym->name, + gotype->r[i].add); } } @@ -961,25 +1057,46 @@ defgotype(Sym *gotype) fld = newdie(die, DW_ABRV_ARRAYRANGE, "range"); newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0); newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); - break; case KindChan: die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name); - // TODO: describe ../../pkg/runtime/chan.c::struct Hchan + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_chanelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); break; case KindFunc: die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")); + nfields = decodetype_funcincount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcintype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defgotype(s)); + } + if (decodetype_funcdotdotdot(gotype)) + newdie(die, DW_ABRV_DOTDOTDOT, "..."); + nfields = decodetype_funcoutcount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcouttype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defptrto(defgotype(s))); + } + die = defptrto(die); break; case KindInterface: die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); break; case KindMap: die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name); - // TODO: describe ../../pkg/runtime/hashmap.c::struct hash + s = decodetype_mapkey(gotype); + newrefattr(die, DW_AT_internal_key_type, defgotype(s)); + s = decodetype_mapvalue(gotype); + newrefattr(die, DW_AT_internal_val_type, defgotype(s)); break; case KindPtr: @@ -991,48 +1108,20 @@ defgotype(Sym *gotype) case KindSlice: die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - fld = newdie(die, DW_ABRV_STRUCTFIELD, "data"); - // Synthesize *elemtype if not already exists. Maybe - // this should be named '<*T>' to not stand in the way - // of the real definition of *T. s = decodetype_arrayelem(gotype); - elem = defgotype(s); - ptrname = strdup(s->name + 4); // skip "type" but leave the '.' - ptrname[0] = '*'; // .. to stuff in the '*' - ptrelem = find(&dwtypes, ptrname); - if (ptrelem == nil) { - ptrelem = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname); - newrefattr(ptrelem, DW_AT_type, elem); - } else { - free(ptrname); - } - newrefattr(fld, DW_AT_type, ptrelem); - newmemberoffsetattr(fld, 0); - fld = newdie(die, DW_ABRV_STRUCTFIELD, "len"); - newrefattr(fld, DW_AT_type, find(&dwtypes, "")); - newmemberoffsetattr(fld, PtrSize); - fld = newdie(die, DW_ABRV_STRUCTFIELD, "cap"); - newrefattr(fld, DW_AT_type, find(&dwtypes, "")); - newmemberoffsetattr(fld, PtrSize + 4); - + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); break; case KindString: die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - fld = newdie(die, DW_ABRV_STRUCTFIELD, "str"); - newrefattr(fld, DW_AT_type, find(&dwtypes, "")); - newmemberoffsetattr(fld, 0); - fld = newdie(die, DW_ABRV_STRUCTFIELD, "len"); - newrefattr(fld, DW_AT_type, find(&dwtypes, "")); - newmemberoffsetattr(fld, PtrSize); break; case KindStruct: die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); nfields = decodetype_structfieldcount(gotype); - for (i = 0; i < nfields; ++i) { + for (i = 0; i < nfields; i++) { f = decodetype_structfieldname(gotype, i); s = decodetype_structfieldtype(gotype, i); if (f == nil) @@ -1051,11 +1140,274 @@ defgotype(Sym *gotype) default: diag("definition of unknown kind %d: %s", kind, gotype->name); die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name); - newrefattr(die, DW_AT_type, find(&dwtypes, "")); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "")); } return die; - } +} + +// Find or construct *T given T. +static DWDie* +defptrto(DWDie *dwtype) +{ + char ptrname[1024]; + DWDie *die; + + snprint(ptrname, sizeof ptrname, "*%s", getattr(dwtype, DW_AT_name)->data); + die = find(&dwtypes, ptrname); + if (die == nil) { + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, + strcpy(mal(strlen(ptrname)+1), ptrname)); + newrefattr(die, DW_AT_type, dwtype); + } + return die; +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. +static void +copychildren(DWDie *dst, DWDie *src) +{ + DWDie *c; + DWAttr *a; + + for (src = src->child; src != nil; src = src->link) { + c = newdie(dst, src->abbrev, getattr(src, DW_AT_name)->data); + for (a = src->attr; a != nil; a = a->link) + newattr(c, a->atr, a->cls, a->value, a->data); + copychildren(c, src); + } + reverselist(&dst->child); +} + +// Search children (assumed to have DW_TAG_member) for the one named +// field and set it's DW_AT_type to dwtype +static void +substitutetype(DWDie *structdie, char *field, DWDie* dwtype) +{ + DWDie *child; + DWAttr *a; + + child = find_or_diag(structdie, field); + if (child == nil) + return; + + a = getattr(child, DW_AT_type); + if (a != nil) + a->data = (char*) dwtype; + else + newrefattr(child, DW_AT_type, dwtype); +} + +static void +synthesizestringtypes(DWDie* die) +{ + DWDie *prototype; + + prototype = defgotype(lookup("type.runtime.string_", 0)); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_STRINGTYPE) + continue; + copychildren(die, prototype); + } +} + +static void +synthesizeslicetypes(DWDie *die) +{ + DWDie *prototype, *elem; + + prototype = defgotype(lookup("type.runtime.slice",0)); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_SLICETYPE) + continue; + copychildren(die, prototype); + elem = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + substitutetype(die, "array", defptrto(elem)); + } +} + +static char* +mkinternaltypename(char *base, char *arg1, char *arg2) +{ + char buf[1024]; + char *n; + + if (arg2 == nil) + snprint(buf, sizeof buf, "%s<%s>", base, arg1); + else + snprint(buf, sizeof buf, "%s<%s,%s>", base, arg1, arg2); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +enum { + MaxValsize = 256 - 64 +}; + +static void +synthesizemaptypes(DWDie *die) +{ + + DWDie *hash, *hash_subtable, *hash_entry, + *dwh, *dwhs, *dwhe, *keytype, *valtype, *fld; + int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; + DWAttr *a; + + hash = defgotype(lookup("type.runtime.hash",0)); + hash_subtable = defgotype(lookup("type.runtime.hash_subtable",0)); + hash_entry = defgotype(lookup("type.runtime.hash_entry",0)); + + if (hash == nil || hash_subtable == nil || hash_entry == nil) + return; + + dwh = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; + if (dwh == nil) + return; + + hashsize = getattr(dwh, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_MAPTYPE) + continue; + + keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; + valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; + + a = getattr(keytype, DW_AT_byte_size); + keysize = a ? a->value : PtrSize; // We don't store size with Pointers + + a = getattr(valtype, DW_AT_byte_size); + valsize = a ? a->value : PtrSize; + + // This is what happens in hash_init and makemap_c + valsize_in_hash = valsize; + if (valsize > MaxValsize) + valsize_in_hash = PtrSize; + datavo = keysize; + if (valsize_in_hash >= PtrSize) + datavo = rnd(keysize, PtrSize); + datsize = datavo + valsize_in_hash; + if (datsize < PtrSize) + datsize = PtrSize; + datsize = rnd(datsize, PtrSize); + + // Construct struct hash_entry + dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_entry", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhe, hash_entry); + substitutetype(dwhe, "key", keytype); + if (valsize > MaxValsize) + valtype = defptrto(valtype); + substitutetype(dwhe, "val", valtype); + fld = find_or_diag(dwhe, "val"); + delattr(fld, DW_AT_data_member_location); + newmemberoffsetattr(fld, hashsize + datavo); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); + + // Construct hash_subtable> + dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_subtable", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhs, hash_subtable); + substitutetype(dwhs, "end", defptrto(dwhe)); + substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size + newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash_subtable, DW_AT_byte_size)->value, NULL); + + // Construct hash + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwh, hash); + substitutetype(dwh, "st", defptrto(dwhs)); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash, DW_AT_byte_size)->value, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +static void +synthesizechantypes(DWDie *die) +{ + DWDie *sudog, *waitq, *link, *hchan, + *dws, *dww, *dwl, *dwh, *elemtype; + DWAttr *a; + int elemsize, linksize, sudogsize; + + sudog = defgotype(lookup("type.runtime.sudoG",0)); + waitq = defgotype(lookup("type.runtime.waitQ",0)); + link = defgotype(lookup("type.runtime.link",0)); + hchan = defgotype(lookup("type.runtime.hChan",0)); + if (sudog == nil || waitq == nil || link == nil || hchan == nil) + return; + + sudogsize = getattr(sudog, DW_AT_byte_size)->value; + linksize = getattr(link, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_CHANTYPE) + continue; + elemtype = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + a = getattr(elemtype, DW_AT_byte_size); + elemsize = a ? a->value : PtrSize; + + // sudog + dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("sudog", + getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dws, sudog); + substitutetype(dws, "elem", elemtype); + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // waitq + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dww, waitq); + substitutetype(dww, "first", defptrto(dws)); + substitutetype(dww, "last", defptrto(dws)); + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(waitq, DW_AT_byte_size)->value, NULL); + + // link + dwl = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("link", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwl, link); + substitutetype(dwl, "link", defptrto(dwl)); + substitutetype(dwl, "elem", elemtype); + newattr(dwl, DW_AT_byte_size, DW_CLS_CONSTANT, + linksize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // hchan + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwh, hchan); + substitutetype(dwh, "senddataq", defptrto(dwl)); + substitutetype(dwh, "recvdataq", defptrto(dwl)); + substitutetype(dwh, "recvq", dww); + substitutetype(dwh, "sendq", dww); + substitutetype(dwh, "free", dws); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hchan, DW_AT_byte_size)->value, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} // For use with pass.c::genasmsym static void @@ -1063,15 +1415,20 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) { DWDie *dv, *dt; - if (gotype == nil) { + if (strncmp(s, "go.string.", 10) == 0) + return; + if (strncmp(s, "string.", 7) == 0) + return; + if (strncmp(s, "type.", 5) == 0) return; - } dv = nil; switch (t) { default: return; + case 'd': + case 'b': case 'D': case 'B': dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); @@ -1100,7 +1457,6 @@ movetomodule(DWDie *parent) die->link = parent->child; } - /* * Filename fragments for the line history stack. */ @@ -1211,6 +1567,24 @@ addhistfile(char *zentry) return histfilesize - 1; } +// if the histfile stack contains ..../runtime/runtime_defs.go +// use that to set gdbscript +static void +finddebugruntimepath() +{ + int i, l; + char *c; + + for (i = 1; i < histfilesize; i++) { + if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) { + l = c - histfile[i]; + memmove(gdbscript, histfile[i], l); + memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1); + break; + } + } +} + // Go's runtime C sources are sane, and Go sources nest only 1 level, // so 16 should be plenty. static struct { @@ -1435,14 +1809,14 @@ writelines(void) Prog *q; Sym *s; Auto *a; - vlong unitstart; + vlong unitstart, offs; vlong pc, epc, lc, llc, lline; int currfile; int i, lang, da, dt; Linehist *lh; DWDie *dwinfo, *dwfunc, *dwvar, **dws; DWDie *varhash[HASHSIZE]; - char *n; + char *n, *nn; unitstart = -1; epc = pc = 0; @@ -1471,6 +1845,7 @@ writelines(void) } lang = guesslang(histfile[1]); + finddebugruntimepath(); dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); @@ -1561,14 +1936,15 @@ writelines(void) da = 0; dwfunc->hash = varhash; // enable indexing of children by name memset(varhash, 0, sizeof varhash); - for(a = s->autom; a; a = a->link) { switch (a->type) { case D_AUTO: dt = DW_ABRV_AUTO; + offs = a->aoffset - PtrSize; break; case D_PARAM: dt = DW_ABRV_PARAM; + offs = a->aoffset; break; default: continue; @@ -1579,15 +1955,20 @@ writelines(void) n = mkvarname(a->asym->name, da); else n = a->asym->name; - dwvar = newdie(dwfunc, dt, n); - newcfaoffsetattr(dwvar, a->aoffset); - newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); - // push dwvar down dwfunc->child to keep order + // Drop the package prefix from locals and arguments. + nn = strrchr(n, '.'); + if (nn) + n = nn + 1; - newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, a->aoffset, NULL); + dwvar = newdie(dwfunc, dt, n); + newcfaoffsetattr(dwvar, offs); + newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); + + // push dwvar down dwfunc->child to preserve order + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, NULL); dwfunc->child = dwvar->link; // take dwvar out from the top of the list for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) - if (a->aoffset > getattr(*dws, DW_AT_internal_location)->value) + if (offs > getattr(*dws, DW_AT_internal_location)->value) break; dwvar->link = *dws; *dws = dwvar; @@ -1818,7 +2199,7 @@ writepub(int (*ispub)(DWDie*)) * because we need die->offs of dw_globals. */ static vlong -writearanges() +writearanges(void) { DWDie *compunit; DWAttr *b, *e; @@ -1853,6 +2234,21 @@ writearanges() return sectionstart; } +static vlong +writegdbscript(void) +{ + vlong sectionstart; + + sectionstart = cpos(); + + if (gdbscript[0]) { + cput(1); // magic 1 byte? + strnput(gdbscript, strlen(gdbscript)+1); + cflush(); + } + return sectionstart; +} + /* * This is the main entry point for generating dwarf. After emitting * the mandatory debug_abbrev section, it calls writelines() to set up @@ -1866,7 +2262,10 @@ void dwarfemitdebugsections(void) { vlong infoe; - DWDie *die; + DWDie* die; + + // For diagnostic messages. + newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes"); mkindex(&dwroot); mkindex(&dwtypes); @@ -1875,39 +2274,33 @@ dwarfemitdebugsections(void) // Some types that must exist to define other ones. newdie(&dwtypes, DW_ABRV_NULLTYPE, ""); newdie(&dwtypes, DW_ABRV_NULLTYPE, "void"); - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"); - newrefattr(die, DW_AT_type, find(&dwtypes, "void")); + newrefattr(newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"), + DW_AT_type, find(&dwtypes, "void")); die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); - die = newdie(&dwtypes, DW_ABRV_BASETYPE, ""); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, 4, 0); - - die = newdie(&dwtypes, DW_ABRV_BASETYPE, ""); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, 1, 0); - - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, ""); - newrefattr(die, DW_AT_type, find(&dwtypes, "")); - genasmsym(defdwsymb); - reversetree(&dwtypes.child); - reversetree(&dwglobals.child); writeabbrev(); writelines(); writeframes(); - reversetree(&dwroot.child); - movetomodule(&dwtypes); // TODO: put before functions - movetomodule(&dwglobals); + synthesizestringtypes(dwtypes.child); + synthesizeslicetypes(dwtypes.child); + synthesizemaptypes(dwtypes.child); + synthesizechantypes(dwtypes.child); + reversetree(&dwroot.child); + reversetree(&dwtypes.child); + reversetree(&dwglobals.child); + + movetomodule(&dwtypes); + movetomodule(&dwglobals); infoo = cpos(); writeinfo(); - arangeso = pubtypeso = pubnameso = infoe = cpos(); + gdbscripto = arangeso = pubtypeso = pubnameso = infoe = cpos(); if (fwdcount > 0) { if (debug['v']) @@ -1925,13 +2318,15 @@ dwarfemitdebugsections(void) } infosize = infoe - infoo; - pubnameso = writepub(ispubname); - pubtypeso = writepub(ispubtype); - arangeso = writearanges(); + pubnameso = writepub(ispubname); + pubtypeso = writepub(ispubtype); + arangeso = writearanges(); + gdbscripto = writegdbscript(); - pubnamessize = pubtypeso - pubnameso; - pubtypessize = arangeso - pubtypeso; - arangessize = cpos() - arangeso; + pubnamessize = pubtypeso - pubnameso; + pubtypessize = arangeso - pubtypeso; + arangessize = gdbscripto - arangeso; + gdbscriptsize = cpos() - gdbscripto; } /* @@ -1950,6 +2345,7 @@ enum ElfStrDebugPubTypes, ElfStrDebugRanges, ElfStrDebugStr, + ElfStrGDBScripts, NElfStrDbg }; @@ -1969,6 +2365,7 @@ dwarfaddshstrings(Sym *shstrtab) elfstrdbg[ElfStrDebugPubTypes] = addstring(shstrtab, ".debug_pubtypes"); elfstrdbg[ElfStrDebugRanges] = addstring(shstrtab, ".debug_ranges"); elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); + elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); } void @@ -2023,6 +2420,14 @@ dwarfaddelfheaders(void) sh->size = arangessize; sh->addralign = 1; } + + if (gdbscriptsize) { + sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]); + sh->type = SHT_PROGBITS; + sh->off = gdbscripto; + sh->size = gdbscriptsize; + sh->addralign = 1; + } } /* @@ -2033,7 +2438,6 @@ dwarfaddmachoheaders(void) { MachoSect *msect; MachoSeg *ms; - vlong fakestart; int nsect; @@ -2042,9 +2446,14 @@ dwarfaddmachoheaders(void) fakestart = abbrevo & ~0xfff; nsect = 4; - if (pubnamessize > 0) nsect++; - if (pubtypessize > 0) nsect++; - if (arangessize > 0) nsect++; + if (pubnamessize > 0) + nsect++; + if (pubtypessize > 0) + nsect++; + if (arangessize > 0) + nsect++; + if (gdbscriptsize > 0) + nsect++; ms = newMachoSeg("__DWARF", nsect); ms->fileoffset = fakestart; @@ -2090,4 +2499,12 @@ dwarfaddmachoheaders(void) msect->size = arangessize; ms->filesize += msect->size; } + + // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) + if (gdbscriptsize > 0) { + msect = newMachoSect(ms, "__debug_gdb_scripts"); + msect->off = gdbscripto; + msect->size = gdbscriptsize; + ms->filesize += msect->size; + } } diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py new file mode 100644 index 00000000000..422809e4172 --- /dev/null +++ b/src/pkg/runtime/runtime-gdb.py @@ -0,0 +1,174 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +"""GDB Pretty printers and convencience functions for Go's runtime structures. + +This script is loaded by GDB when it finds a .debug_gdb_scripts +section in the compiled binary. The [68]l linkers emit this with a +path to this file based on the path to the runtime package. +""" + +import sys, re + +print >>sys.stderr, "Loading Go Runtime support." + +# +# Pretty Printers +# + +class StringTypePrinter: + "Pretty print Go strings." + + pattern = re.compile(r'^struct string$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'string' + + def to_string(self): + return self.val['str'] + + +class SliceTypePrinter: + "Pretty print slices." + + pattern = re.compile(r'^struct \[\]') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'array' + + def to_string(self): + return str(self.val.type)[6:] # skip 'struct ' + + def children(self): + ptr = self.val["array"] + for idx in range(self.val["len"]): + yield ('[%d]' % idx, (ptr + idx).dereference()) + + +class MapTypePrinter: + """Pretty print map[K]V types. + + Map-typed go variables are really pointers. dereference them in gdb + to inspect their contents with this pretty printer. + """ + + pattern = re.compile(r'^struct hash<.*>$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'map' + + def to_string(self): + return str(self.val.type) + + def children(self): + stab = self.val['st'] + i = 0 + for v in self.traverse_hash(stab): + yield ("[%d]" % i, v['key']) + yield ("[%d]" % (i + 1), v['val']) + i += 2 + + def traverse_hash(self, stab): + ptr = stab['entry'].address + end = stab['end'] + while ptr < end: + v = ptr.dereference() + ptr = ptr + 1 + if v['hash'] == 0: continue + if v['hash'] & 63 == 63: # subtable + for v in self.traverse_hash(v['key'].cast(self.val['st'].type)): + yield v + else: + yield v + + +class ChanTypePrinter: + """Pretty print chan[T] types. + + Map-typed go variables are really pointers. dereference them in gdb + to inspect their contents with this pretty printer. + """ + + pattern = re.compile(r'^struct hchan<.*>$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'array' + + def to_string(self): + return str(self.val.type) + + def children(self): + ptr = self.val['recvdataq'] + for idx in range(self.val["qcount"]): + yield ('[%d]' % idx, ptr['elem']) + ptr = ptr['link'] + +# +# Register all the *Printer classes +# + +def makematcher(klass): + def matcher(val): + try: + if klass.pattern.match(str(val.type)): return klass(val) + except: pass + return matcher + +gdb.current_objfile().pretty_printers.extend([makematcher(k) for k in vars().values() if hasattr(k, 'pattern')]) + + +# +# Convenience Functions +# + +class GoLenFunc(gdb.Function): + "Length of strings, slices, maps or channels" + + how = ((StringTypePrinter, 'len' ), + (SliceTypePrinter, 'len'), + (MapTypePrinter, 'count'), + (ChanTypePrinter, 'qcount')) + + def __init__(self): + super(GoLenFunc, self).__init__("len") + + def invoke(self, obj): + typename = str(obj.type) + for klass, fld in self.how: + if klass.pattern.match(typename): + return obj[fld] + +class GoCapFunc(gdb.Function): + "Capacity of slices or channels" + + how = ((SliceTypePrinter, 'cap'), + (ChanTypePrinter, 'dataqsiz')) + + def __init__(self): + super(GoCapFunc, self).__init__("cap") + + def invoke(self, obj): + typename = str(obj.type) + for klass, fld in self.how: + if klass.pattern.match(typename): + return obj[fld] + +# +# Register all convience functions and CLI commands +# +for k in vars().values(): + if hasattr(k, 'invoke'): + k()