|
- // Copyright 2018 Google Inc. All Rights Reserved.
- //
- // 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("<nil>")
- 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("<nil>")
- } 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("<nil>")
- 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("<nil>")
- 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
- }
|