blob: e5bb0d221e11f66dea42cac6de9a95fb4de28b09 [file] [log] [blame]
// Copyright 2015 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.
package server
import (
"fmt"
"golang.org/x/debug"
"golang.org/x/debug/dwarf"
)
// value peeks the program's memory at the given address, parsing it as a value of type t.
func (s *Server) value(t dwarf.Type, addr uint64) (debug.Value, error) {
// readBasic reads the memory for a basic type of size n bytes.
readBasic := func(n int64) ([]byte, error) {
switch n {
case 1, 2, 4, 8, 16:
default:
return nil, fmt.Errorf("invalid size: %d", n)
}
buf := make([]byte, n)
if err := s.peek(uintptr(addr), buf); err != nil {
return nil, err
}
return buf, nil
}
switch t := t.(type) {
case *dwarf.CharType, *dwarf.IntType:
bs := t.Common().ByteSize
buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading integer: %s", err)
}
x := s.arch.IntN(buf)
switch bs {
case 1:
return int8(x), nil
case 2:
return int16(x), nil
case 4:
return int32(x), nil
case 8:
return int64(x), nil
default:
return nil, fmt.Errorf("invalid integer size: %d", bs)
}
case *dwarf.UcharType, *dwarf.UintType, *dwarf.AddrType:
bs := t.Common().ByteSize
buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading unsigned integer: %s", err)
}
x := s.arch.UintN(buf)
switch bs {
case 1:
return uint8(x), nil
case 2:
return uint16(x), nil
case 4:
return uint32(x), nil
case 8:
return uint64(x), nil
default:
return nil, fmt.Errorf("invalid unsigned integer size: %d", bs)
}
case *dwarf.BoolType:
bs := t.Common().ByteSize
buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading boolean: %s", err)
}
for _, b := range buf {
if b != 0 {
return true, nil
}
}
return false, nil
case *dwarf.FloatType:
bs := t.Common().ByteSize
buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading float: %s", err)
}
switch bs {
case 4:
return s.arch.Float32(buf), nil
case 8:
return s.arch.Float64(buf), nil
default:
return nil, fmt.Errorf("invalid float size: %d", bs)
}
case *dwarf.ComplexType:
bs := t.Common().ByteSize
buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading complex: %s", err)
}
switch bs {
case 8:
return s.arch.Complex64(buf), nil
case 16:
return s.arch.Complex128(buf), nil
default:
return nil, fmt.Errorf("invalid complex size: %d", bs)
}
case *dwarf.PtrType:
bs := t.Common().ByteSize
if bs != int64(s.arch.PointerSize) {
return nil, fmt.Errorf("invalid pointer size: %d", bs)
}
buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading pointer: %s", err)
}
return debug.Pointer{
TypeID: uint64(t.Type.Common().Offset),
Address: uint64(s.arch.Uintptr(buf)),
}, nil
case *dwarf.SliceType:
if s, err := s.peekSlice(t, addr); err != nil {
return nil, err
} else {
return s, nil
}
case *dwarf.ArrayType:
length := t.Count
stride := t.StrideBitSize
if stride%8 != 0 {
return nil, fmt.Errorf("array is not byte-aligned")
}
return debug.Array{
ElementTypeID: uint64(t.Type.Common().Offset),
Address: uint64(addr),
Length: uint64(length),
StrideBits: uint64(stride),
}, nil
case *dwarf.StructType:
fields := make([]debug.StructField, len(t.Field))
for i, field := range t.Field {
fields[i] = debug.StructField{
Name: field.Name,
Var: debug.Var{
TypeID: uint64(field.Type.Common().Offset),
Address: uint64(addr) + uint64(field.ByteOffset),
},
}
}
return debug.Struct{fields}, nil
case *dwarf.TypedefType:
return s.value(t.Type, addr)
case *dwarf.MapType:
length, err := s.peekMapLength(t, addr)
if err != nil {
return nil, err
}
return debug.Map{
TypeID: uint64(t.Common().Offset),
Address: addr,
Length: length,
}, nil
case *dwarf.StringType:
ptr, err := s.peekPtrStructField(&t.StructType, addr, "str")
if err != nil {
return nil, fmt.Errorf("reading string location: %s", err)
}
length, err := s.peekUintOrIntStructField(&t.StructType, addr, "len")
if err != nil {
return nil, fmt.Errorf("reading string length: %s", err)
}
const maxStringSize = 256
n := length
if n > maxStringSize {
n = maxStringSize
}
tmp := make([]byte, n)
if err := s.peekBytes(ptr, tmp); err != nil {
return nil, fmt.Errorf("reading string contents: %s", err)
}
return debug.String{Length: length, String: string(tmp)}, nil
case *dwarf.ChanType:
pt, ok := t.TypedefType.Type.(*dwarf.PtrType)
if !ok {
return nil, fmt.Errorf("reading channel: type is not a pointer")
}
st, ok := pt.Type.(*dwarf.StructType)
if !ok {
return nil, fmt.Errorf("reading channel: type is not a pointer to struct")
}
a, err := s.peekPtr(addr)
if err != nil {
return nil, fmt.Errorf("reading channel pointer: %s", err)
}
if a == 0 {
// This channel is nil.
return debug.Channel{
ElementTypeID: uint64(t.ElemType.Common().Offset),
Address: 0,
Buffer: 0,
Length: 0,
Capacity: 0,
Stride: uint64(t.ElemType.Common().ByteSize),
BufferStart: 0,
}, nil
}
buf, err := s.peekPtrStructField(st, a, "buf")
if err != nil {
return nil, fmt.Errorf("reading channel buffer location: %s", err)
}
qcount, err := s.peekUintOrIntStructField(st, a, "qcount")
if err != nil {
return nil, fmt.Errorf("reading channel length: %s", err)
}
capacity, err := s.peekUintOrIntStructField(st, a, "dataqsiz")
if err != nil {
return nil, fmt.Errorf("reading channel capacity: %s", err)
}
recvx, err := s.peekUintOrIntStructField(st, a, "recvx")
if err != nil {
return nil, fmt.Errorf("reading channel buffer index: %s", err)
}
return debug.Channel{
ElementTypeID: uint64(t.ElemType.Common().Offset),
Address: a,
Buffer: buf,
Length: qcount,
Capacity: capacity,
Stride: uint64(t.ElemType.Common().ByteSize),
BufferStart: recvx,
}, nil
case *dwarf.FuncType:
a, err := s.peekPtr(addr)
if err != nil {
return nil, fmt.Errorf("reading func: %s", err)
}
return debug.Func{Address: a}, nil
case *dwarf.InterfaceType:
return debug.Interface{}, nil
// TODO: more types
}
return nil, fmt.Errorf("Unsupported type %T", t)
}