// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build linux package server import ( "bytes" "fmt" "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch" "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf" ) // typeAndAddress associates an address in the target with a DWARF type. type typeAndAddress struct { Type dwarf.Type Address uint64 } // Routines to print a value using DWARF type descriptions. // TODO: Does this deserve its own package? It has no dependencies on Server. // A Printer pretty-prints values in the target address space. // It can be reused after each printing operation to avoid unnecessary // allocations. However, it is not safe for concurrent access. type Printer struct { err error // Sticky error value. server *Server dwarf *dwarf.Data arch *arch.Architecture printBuf bytes.Buffer // Accumulates the output. visited map[typeAndAddress]bool // Prevents looping on cyclic data. } // printf prints to printBuf. func (p *Printer) printf(format string, args ...interface{}) { fmt.Fprintf(&p.printBuf, format, args...) } // errorf prints the error to printBuf, then sets the sticky error for the // printer, if not already set. func (p *Printer) errorf(format string, args ...interface{}) { fmt.Fprintf(&p.printBuf, "<"+format+">", args...) if p.err != nil { return } p.err = fmt.Errorf(format, args...) } // NewPrinter returns a printer that can use the Server to access and print // values of the specified architecture described by the provided DWARF data. func NewPrinter(arch *arch.Architecture, dwarf *dwarf.Data, server *Server) *Printer { return &Printer{ server: server, arch: arch, dwarf: dwarf, visited: make(map[typeAndAddress]bool), } } // reset resets the Printer. It must be called before starting a new // printing operation. func (p *Printer) reset() { p.err = nil p.printBuf.Reset() // Just wipe the map rather than reallocating. It's almost always tiny. for k := range p.visited { delete(p.visited, k) } } // Sprint returns the pretty-printed value of the item with the given name, such as "main.global". func (p *Printer) Sprint(name string) (string, error) { entry, err := p.dwarf.LookupEntry(name) if err != nil { return "", err } p.reset() switch entry.Tag { case dwarf.TagVariable: // TODO: What other entries have global location attributes? var a uint64 iface := entry.Val(dwarf.AttrLocation) if iface != nil { a = p.decodeLocation(iface.([]byte)) } p.printEntryValueAt(entry, a) default: p.errorf("unrecognized entry type %s", entry.Tag) } return p.printBuf.String(), p.err } // Figure 24 of DWARF v4. const ( locationAddr = 0x03 ) // decodeLocation decodes the dwarf data describing an address. func (p *Printer) decodeLocation(data []byte) uint64 { switch data[0] { case locationAddr: return p.arch.Uintptr(data[1:]) default: p.errorf("unimplemented location type %#x", data[0]) } return 0 } // SprintEntry returns the pretty-printed value of the item with the specified DWARF Entry and address. func (p *Printer) SprintEntry(entry *dwarf.Entry, a uint64) (string, error) { p.reset() p.printEntryValueAt(entry, a) return p.printBuf.String(), p.err } // printEntryValueAt pretty-prints the data at the specified address. // using the type information in the Entry. func (p *Printer) printEntryValueAt(entry *dwarf.Entry, a uint64) { if a == 0 { p.printf("") return } switch entry.Tag { case dwarf.TagVariable, dwarf.TagFormalParameter: // OK default: p.errorf("unrecognized entry type %s", entry.Tag) return } iface := entry.Val(dwarf.AttrType) if iface == nil { p.errorf("no type") return } typ, err := p.dwarf.Type(iface.(dwarf.Offset)) if err != nil { p.errorf("type lookup: %v", err) return } p.printValueAt(typ, a) } // printValueAt pretty-prints the data at the specified address. // using the provided type information. func (p *Printer) printValueAt(typ dwarf.Type, a uint64) { if a != 0 { // Check if we are repeating the same type and address. ta := typeAndAddress{typ, a} if p.visited[ta] { p.printf("(%v %#x)", typ, a) return } p.visited[ta] = true } switch typ := typ.(type) { case *dwarf.BoolType: if typ.ByteSize != 1 { p.errorf("unrecognized bool size %d", typ.ByteSize) return } if b, err := p.server.peekUint8(a); err != nil { p.errorf("reading bool: %s", err) } else { p.printf("%t", b != 0) } case *dwarf.PtrType: if ptr, err := p.server.peekPtr(a); err != nil { p.errorf("reading pointer: %s", err) } else { p.printf("%#x", ptr) } case *dwarf.IntType: // Sad we can't tell a rune from an int32. if i, err := p.server.peekInt(a, typ.ByteSize); err != nil { p.errorf("reading integer: %s", err) } else { p.printf("%d", i) } case *dwarf.UintType: if u, err := p.server.peekUint(a, typ.ByteSize); err != nil { p.errorf("reading unsigned integer: %s", err) } else { p.printf("%d", u) } case *dwarf.FloatType: buf := make([]byte, typ.ByteSize) if err := p.server.peekBytes(a, buf); err != nil { p.errorf("reading float: %s", err) return } switch typ.ByteSize { case 4: p.printf("%g", p.arch.Float32(buf)) case 8: p.printf("%g", p.arch.Float64(buf)) default: p.errorf("unrecognized float size %d", typ.ByteSize) } case *dwarf.ComplexType: buf := make([]byte, typ.ByteSize) if err := p.server.peekBytes(a, buf); err != nil { p.errorf("reading complex: %s", err) return } switch typ.ByteSize { case 8: p.printf("%g", p.arch.Complex64(buf)) case 16: p.printf("%g", p.arch.Complex128(buf)) default: p.errorf("unrecognized complex size %d", typ.ByteSize) } case *dwarf.StructType: if typ.Kind != "struct" { // Could be "class" or "union". p.errorf("can't handle struct type %s", typ.Kind) return } p.printf("%s {", typ.String()) for i, field := range typ.Field { if i != 0 { p.printf(", ") } p.printValueAt(field.Type, a+uint64(field.ByteOffset)) } p.printf("}") case *dwarf.ArrayType: p.printArrayAt(typ, a) case *dwarf.InterfaceType: p.printInterfaceAt(typ, a) case *dwarf.MapType: p.printMapAt(typ, a) case *dwarf.ChanType: p.printChannelAt(typ, a) case *dwarf.SliceType: p.printSliceAt(typ, a) case *dwarf.StringType: p.printStringAt(typ, a) case *dwarf.TypedefType: p.printValueAt(typ.Type, a) case *dwarf.FuncType: p.printf("%v @%#x ", typ, a) case *dwarf.VoidType: p.printf("void") default: p.errorf("unimplemented type %v", typ) } } func (p *Printer) printArrayAt(typ *dwarf.ArrayType, a uint64) { elemType := typ.Type length := typ.Count stride, ok := p.arrayStride(typ) if !ok { p.errorf("can't determine element size") } p.printf("%s{", typ) n := length if n > 100 { n = 100 // TODO: Have a way to control this? } for i := int64(0); i < n; i++ { if i != 0 { p.printf(", ") } p.printValueAt(elemType, a) a += stride // TODO: Alignment and padding - not given by Type } if n < length { p.printf(", ...") } p.printf("}") } func (p *Printer) printInterfaceAt(t *dwarf.InterfaceType, a uint64) { // t embeds a TypedefType, which may point to another typedef. // The underlying type should be a struct. st, ok := followTypedefs(&t.TypedefType).(*dwarf.StructType) if !ok { p.errorf("bad interface type: not a struct") return } p.printf("(") tab, err := p.server.peekPtrStructField(st, a, "tab") if err != nil { p.errorf("reading interface type: %s", err) } else { f, err := getField(st, "tab") if err != nil { p.errorf("%s", err) } else { p.printTypeOfInterface(f.Type, tab) } } p.printf(", ") data, err := p.server.peekPtrStructField(st, a, "data") if err != nil { p.errorf("reading interface value: %s", err) } else if data == 0 { p.printf("") } else { p.printf("%#x", data) } p.printf(")") } // printTypeOfInterface prints the type of the given tab pointer. func (p *Printer) printTypeOfInterface(t dwarf.Type, a uint64) { if a == 0 { p.printf("") return } // t should be a pointer to a struct containing _type, which is a pointer to a // struct containing _string, which is the name of the type. // Depending on the compiler version, some of these types can be typedefs, and // _string may be a string or a *string. t1, ok := followTypedefs(t).(*dwarf.PtrType) if !ok { p.errorf("interface's tab is not a pointer") return } t2, ok := followTypedefs(t1.Type).(*dwarf.StructType) if !ok { p.errorf("interface's tab is not a pointer to struct") return } typeField, err := getField(t2, "_type") if err != nil { p.errorf("%s", err) return } t3, ok := followTypedefs(typeField.Type).(*dwarf.PtrType) if !ok { p.errorf("interface's _type is not a pointer") return } t4, ok := followTypedefs(t3.Type).(*dwarf.StructType) if !ok { p.errorf("interface's _type is not a pointer to struct") return } stringField, err := getField(t4, "_string") if err != nil { p.errorf("%s", err) return } if t5, ok := stringField.Type.(*dwarf.PtrType); ok { stringType, ok := t5.Type.(*dwarf.StringType) if !ok { p.errorf("interface _string is a pointer to %T, want string or *string", t5.Type) return } typeAddr, err := p.server.peekPtrStructField(t2, a, "_type") if err != nil { p.errorf("reading interface type: %s", err) return } stringAddr, err := p.server.peekPtrStructField(t4, typeAddr, "_string") if err != nil { p.errorf("reading interface type: %s", err) return } p.printStringAt(stringType, stringAddr) } else { stringType, ok := stringField.Type.(*dwarf.StringType) if !ok { p.errorf("interface _string is a %T, want string or *string", stringField.Type) return } typeAddr, err := p.server.peekPtrStructField(t2, a, "_type") if err != nil { p.errorf("reading interface type: %s", err) return } stringAddr := typeAddr + uint64(stringField.ByteOffset) p.printStringAt(stringType, stringAddr) } } // maxMapValuesToPrint values are printed for each map; any remaining values are // truncated to "...". const maxMapValuesToPrint = 8 func (p *Printer) printMapAt(typ *dwarf.MapType, a uint64) { count := 0 fn := func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) (stop bool) { count++ if count > maxMapValuesToPrint { return false } if count > 1 { p.printf(" ") } p.printValueAt(keyType, keyAddr) p.printf(":") p.printValueAt(valType, valAddr) return true } p.printf("map[") if err := p.server.peekMapValues(typ, a, fn); err != nil { p.errorf("reading map values: %s", err) } if count > maxMapValuesToPrint { p.printf(" ...") } p.printf("]") } func (p *Printer) printChannelAt(ct *dwarf.ChanType, a uint64) { p.printf("(chan %s ", ct.ElemType) defer p.printf(")") a, err := p.server.peekPtr(a) if err != nil { p.errorf("reading channel: %s", err) return } if a == 0 { p.printf("") return } p.printf("%#x", a) // ct is a typedef for a pointer to a struct. pt, ok := ct.TypedefType.Type.(*dwarf.PtrType) if !ok { p.errorf("bad channel type: not a pointer") return } st, ok := pt.Type.(*dwarf.StructType) if !ok { p.errorf("bad channel type: not a pointer to a struct") return } // Print the channel buffer's length (qcount) and capacity (dataqsiz), // if not 0/0. qcount, err := p.server.peekUintOrIntStructField(st, a, "qcount") if err != nil { p.errorf("reading channel: %s", err) return } dataqsiz, err := p.server.peekUintOrIntStructField(st, a, "dataqsiz") if err != nil { p.errorf("reading channel: %s", err) return } if qcount != 0 || dataqsiz != 0 { p.printf(" [%d/%d]", qcount, dataqsiz) } } func (p *Printer) printSliceAt(typ *dwarf.SliceType, a uint64) { // Slices look like a struct with fields array *elemtype, len uint32/64, cap uint32/64. // BUG: Slice header appears to have fields with ByteSize == 0 ptr, err := p.server.peekPtrStructField(&typ.StructType, a, "array") if err != nil { p.errorf("reading slice: %s", err) return } length, err := p.server.peekUintOrIntStructField(&typ.StructType, a, "len") if err != nil { p.errorf("reading slice: %s", err) return } // Capacity is not used yet. _, err = p.server.peekUintOrIntStructField(&typ.StructType, a, "cap") if err != nil { p.errorf("reading slice: %s", err) return } elemType := typ.ElemType size, ok := p.sizeof(typ.ElemType) if !ok { p.errorf("can't determine element size") } p.printf("%s{", typ) for i := uint64(0); i < length; i++ { if i != 0 { p.printf(", ") } p.printValueAt(elemType, ptr) ptr += size // TODO: Alignment and padding - not given by Type } p.printf("}") } func (p *Printer) printStringAt(typ *dwarf.StringType, a uint64) { const maxStringSize = 100 if s, err := p.server.peekString(typ, a, maxStringSize); err != nil { p.errorf("reading string: %s", err) } else { p.printf("%q", s) } } // sizeof returns the byte size of the type. func (p *Printer) sizeof(typ dwarf.Type) (uint64, bool) { size := typ.Size() // Will be -1 if ByteSize is not set. if size >= 0 { return uint64(size), true } switch typ.(type) { case *dwarf.PtrType: // This is the only one we know of, but more may arise. return uint64(p.arch.PointerSize), true } return 0, false } // arrayStride returns the stride of a dwarf.ArrayType in bytes. func (p *Printer) arrayStride(t *dwarf.ArrayType) (uint64, bool) { stride := t.StrideBitSize if stride > 0 { return uint64(stride / 8), true } return p.sizeof(t.Type) } // getField finds the *dwarf.StructField in a dwarf.StructType with name fieldName. func getField(t *dwarf.StructType, fieldName string) (*dwarf.StructField, error) { var r *dwarf.StructField for _, f := range t.Field { if f.Name == fieldName { if r != nil { return nil, fmt.Errorf("struct definition repeats field %s", fieldName) } r = f } } if r == nil { return nil, fmt.Errorf("struct field %s missing", fieldName) } return r, nil }