| // 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 |
| } |