aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuuk van Dijk <lvd@golang.org>2010-12-03 19:19:33 +0100
committerLuuk van Dijk <lvd@golang.org>2010-12-03 19:19:33 +0100
commit9a71bb00bb39ca4cc200df36b2d5bfd591169bef (patch)
tree74d5b035ca811499156158b5e24ebfb4ef13c8ae
parent09e4d860bca257a1c83b7821f73389055f8c42c8 (diff)
downloadgo-9a71bb00bb39ca4cc200df36b2d5bfd591169bef.tar.gz
go-9a71bb00bb39ca4cc200df36b2d5bfd591169bef.zip
[68]l: generate debug info for builtin structured types. prettyprinting in gdb.
R=rsc CC=golang-dev https://golang.org/cl/3309041
-rw-r--r--src/cmd/ld/dwarf.c629
-rw-r--r--src/pkg/runtime/runtime-gdb.py174
2 files changed, 697 insertions, 106 deletions
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 95ae0b5925..9342e1e284 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, "<int32>"));
- newmemberoffsetattr(fld, PtrSize);
- fld = newdie(die, DW_ABRV_STRUCTFIELD, "cap");
- newrefattr(fld, DW_AT_type, find(&dwtypes, "<int32>"));
- 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, "<byte*>"));
- newmemberoffsetattr(fld, 0);
- fld = newdie(die, DW_ABRV_STRUCTFIELD, "len");
- newrefattr(fld, DW_AT_type, find(&dwtypes, "<int32>"));
- 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, "<unspecified>"));
+ newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "<unspecified>"));
}
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<K,V>
+ 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<hash_entry<K,V>>
+ 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<K,V>
+ 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<T>
+ 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<T>
+ 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<T>
+ 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<T>
+ 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;
+ // Drop the package prefix from locals and arguments.
+ nn = strrchr(n, '.');
+ if (nn)
+ n = nn + 1;
+
dwvar = newdie(dwfunc, dt, n);
- newcfaoffsetattr(dwvar, a->aoffset);
+ newcfaoffsetattr(dwvar, offs);
newrefattr(dwvar, DW_AT_type, defgotype(a->gotype));
- // push dwvar down dwfunc->child to keep order
- newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, a->aoffset, NULL);
+ // 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, "<unspecified>");
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, "<int32>");
- 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, "<byte>");
- 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, "<byte*>");
- newrefattr(die, DW_AT_type, find(&dwtypes, "<byte>"));
-
genasmsym(defdwsymb);
- reversetree(&dwtypes.child);
- reversetree(&dwglobals.child);
writeabbrev();
writelines();
writeframes();
+ synthesizestringtypes(dwtypes.child);
+ synthesizeslicetypes(dwtypes.child);
+ synthesizemaptypes(dwtypes.child);
+ synthesizechantypes(dwtypes.child);
+
reversetree(&dwroot.child);
- movetomodule(&dwtypes); // TODO: put before functions
- movetomodule(&dwglobals);
+ 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 0000000000..422809e417
--- /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()