blob: e61a974b4b6b723fd5c81c6f4abda05473fe9a3f [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.
// Functions for reading values of various types from a program's memory.
package server
import (
"errors"
"fmt"
"golang.org/x/debug/dwarf"
)
// peekBytes reads len(buf) bytes at addr.
func (s *Server) peekBytes(addr uint64, buf []byte) error {
return s.ptracePeek(s.stoppedPid, uintptr(addr), buf)
}
// peekPtr reads a pointer at addr.
func (s *Server) peekPtr(addr uint64) (uint64, error) {
buf := make([]byte, s.arch.PointerSize)
if err := s.peekBytes(addr, buf); err != nil {
return 0, err
}
return s.arch.Uintptr(buf), nil
}
// peekUint8 reads a single byte at addr.
func (s *Server) peekUint8(addr uint64) (byte, error) {
buf := make([]byte, 1)
if err := s.peekBytes(addr, buf); err != nil {
return 0, err
}
return uint8(s.arch.UintN(buf)), nil
}
// peekInt reads an int of size n bytes at addr.
func (s *Server) peekInt(addr uint64, n int64) (int64, error) {
buf := make([]byte, n)
if err := s.peekBytes(addr, buf); err != nil {
return 0, err
}
return s.arch.IntN(buf), nil
}
// peekUint reads a uint of size n bytes at addr.
func (s *Server) peekUint(addr uint64, n int64) (uint64, error) {
buf := make([]byte, n)
if err := s.peekBytes(addr, buf); err != nil {
return 0, err
}
return s.arch.UintN(buf), nil
}
// peekPtrStructField reads a pointer in the field fieldName of the struct
// of type t at addr.
func (s *Server) peekPtrStructField(t *dwarf.StructType, addr uint64, fieldName string) (uint64, error) {
f, err := getField(t, fieldName)
if err != nil {
return 0, fmt.Errorf("reading field %s: %s", fieldName, err)
}
if _, ok := f.Type.(*dwarf.PtrType); !ok {
return 0, fmt.Errorf("field %s is not a pointer", fieldName)
}
return s.peekPtr(addr + uint64(f.ByteOffset))
}
// peekUintOrIntStructField reads a signed or unsigned integer in the field fieldName
// of the struct of type t at addr. If the value is negative, it returns an error.
// This function is used when the value should be non-negative, but the DWARF
// type of the field may be signed or unsigned.
func (s *Server) peekUintOrIntStructField(t *dwarf.StructType, addr uint64, fieldName string) (uint64, error) {
f, err := getField(t, fieldName)
if err != nil {
return 0, fmt.Errorf("reading field %s: %s", fieldName, err)
}
ut, ok := f.Type.(*dwarf.UintType)
if ok {
return s.peekUint(addr+uint64(f.ByteOffset), ut.ByteSize)
}
it, ok := f.Type.(*dwarf.IntType)
if !ok {
return 0, fmt.Errorf("field %s is not an integer", fieldName)
}
i, err := s.peekInt(addr+uint64(f.ByteOffset), it.ByteSize)
if err != nil {
return 0, err
}
if i < 0 {
return 0, fmt.Errorf("field %s is negative", fieldName)
}
return uint64(i), nil
}
// peekUintStructField reads a uint in the field fieldName of the struct
// of type t at addr. The size of the uint is determined by the field.
func (s *Server) peekUintStructField(t *dwarf.StructType, addr uint64, fieldName string) (uint64, error) {
f, err := getField(t, fieldName)
if err != nil {
return 0, fmt.Errorf("reading field %s: %s", fieldName, err)
}
ut, ok := f.Type.(*dwarf.UintType)
if !ok {
return 0, fmt.Errorf("field %s is not an unsigned integer", fieldName)
}
return s.peekUint(addr+uint64(f.ByteOffset), ut.ByteSize)
}
// peekIntStructField reads an int in the field fieldName of the struct
// of type t at addr. The size of the int is determined by the field.
func (s *Server) peekIntStructField(t *dwarf.StructType, addr uint64, fieldName string) (int64, error) {
f, err := getField(t, fieldName)
if err != nil {
return 0, fmt.Errorf("reading field %s: %s", fieldName, err)
}
it, ok := f.Type.(*dwarf.IntType)
if !ok {
return 0, fmt.Errorf("field %s is not a signed integer", fieldName)
}
return s.peekInt(addr+uint64(f.ByteOffset), it.ByteSize)
}
// peekMapLocationAndType returns the address and DWARF type of the underlying
// struct of a map variable.
func (s *Server) peekMapLocationAndType(t *dwarf.MapType, a uint64) (uint64, *dwarf.StructType, error) {
// Maps are pointers to structs.
pt, ok := t.Type.(*dwarf.PtrType)
if !ok {
return 0, nil, errors.New("bad map type: not a pointer")
}
st, ok := pt.Type.(*dwarf.StructType)
if !ok {
return 0, nil, errors.New("bad map type: not a pointer to a struct")
}
// a is the address of a pointer to a struct. Get the pointer's value.
a, err := s.peekPtr(a)
if err != nil {
return 0, nil, fmt.Errorf("reading map pointer: %s", err)
}
return a, st, nil
}
// peekMapValues reads a map at the given address and calls fn with the addresses for each (key, value) pair.
// If fn returns false, peekMapValues stops.
func (s *Server) peekMapValues(t *dwarf.MapType, a uint64, fn func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool) error {
a, st, err := s.peekMapLocationAndType(t, a)
if err != nil {
return err
}
if a == 0 {
// The pointer was nil, so the map is empty.
return nil
}
// Gather information about the struct type and the map bucket type.
b, err := s.peekUintStructField(st, a, "B")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
buckets, err := s.peekPtrStructField(st, a, "buckets")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
oldbuckets, err := s.peekPtrStructField(st, a, "oldbuckets")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
bf, err := getField(st, "buckets")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
bucketPtrType, ok := bf.Type.(*dwarf.PtrType)
if !ok {
return errors.New("bad map bucket type: not a pointer")
}
bt, ok := bucketPtrType.Type.(*dwarf.StructType)
if !ok {
return errors.New("bad map bucket type: not a pointer to a struct")
}
bucketSize := uint64(bucketPtrType.Type.Size())
tophashField, err := getField(bt, "tophash")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
bucketCnt := uint64(tophashField.Type.Size())
tophashFieldOffset := uint64(tophashField.ByteOffset)
keysField, err := getField(bt, "keys")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
keysType, ok := keysField.Type.(*dwarf.ArrayType)
if !ok {
return errors.New(`bad map bucket type: "keys" is not an array`)
}
keyType := keysType.Type
keysStride := uint64(keysType.StrideBitSize / 8)
keysFieldOffset := uint64(keysField.ByteOffset)
valuesField, err := getField(bt, "values")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
valuesType, ok := valuesField.Type.(*dwarf.ArrayType)
if !ok {
return errors.New(`bad map bucket type: "values" is not an array`)
}
valueType := valuesType.Type
valuesStride := uint64(valuesType.StrideBitSize / 8)
valuesFieldOffset := uint64(valuesField.ByteOffset)
overflowField, err := getField(bt, "overflow")
if err != nil {
return fmt.Errorf("reading map: %s", err)
}
overflowFieldOffset := uint64(overflowField.ByteOffset)
// Iterate through the two arrays of buckets.
bucketArrays := [2]struct {
addr uint64
size uint64
}{
{buckets, 1 << b},
{oldbuckets, 1 << (b - 1)},
}
for _, bucketArray := range bucketArrays {
if bucketArray.addr == 0 {
continue
}
for i := uint64(0); i < bucketArray.size; i++ {
bucketAddr := bucketArray.addr + i*bucketSize
// Iterate through the linked list of buckets.
// TODO: check for repeated bucket pointers.
for bucketAddr != 0 {
// Iterate through each entry in the bucket.
for j := uint64(0); j < bucketCnt; j++ {
tophash, err := s.peekUint8(bucketAddr + tophashFieldOffset + j)
if err != nil {
return errors.New("reading map: " + err.Error())
}
// From runtime/hashmap.go
const minTopHash = 4
if tophash < minTopHash {
continue
}
keyAddr := bucketAddr + keysFieldOffset + j*keysStride
valAddr := bucketAddr + valuesFieldOffset + j*valuesStride
if !fn(keyAddr, valAddr, keyType, valueType) {
return nil
}
}
var err error
bucketAddr, err = s.peekPtr(bucketAddr + overflowFieldOffset)
if err != nil {
return errors.New("reading map: " + err.Error())
}
}
}
}
return nil
}
// peekMapLength returns the number of elements in a map at the given address.
func (s *Server) peekMapLength(t *dwarf.MapType, a uint64) (uint64, error) {
a, st, err := s.peekMapLocationAndType(t, a)
if err != nil {
return 0, err
}
if a == 0 {
// The pointer was nil, so the map is empty.
return 0, nil
}
length, err := s.peekUintOrIntStructField(st, a, "count")
if err != nil {
return 0, fmt.Errorf("reading map: %s", err)
}
return uint64(length), nil
}