|
- // 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.
-
- // Functions for reading values of various types from a program's memory.
-
- // +build linux
-
- package server
-
- import (
- "errors"
- "fmt"
-
- "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
- "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/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
- }
-
- // peekSlice reads the header of a slice with the given type and address.
- func (s *Server) peekSlice(t *dwarf.SliceType, addr uint64) (debug.Slice, error) {
- ptr, err := s.peekPtrStructField(&t.StructType, addr, "array")
- if err != nil {
- return debug.Slice{}, fmt.Errorf("reading slice location: %s", err)
- }
- length, err := s.peekUintOrIntStructField(&t.StructType, addr, "len")
- if err != nil {
- return debug.Slice{}, fmt.Errorf("reading slice length: %s", err)
- }
- capacity, err := s.peekUintOrIntStructField(&t.StructType, addr, "cap")
- if err != nil {
- return debug.Slice{}, fmt.Errorf("reading slice capacity: %s", err)
- }
- if capacity < length {
- return debug.Slice{}, fmt.Errorf("slice's capacity %d is less than its length %d", capacity, length)
- }
-
- return debug.Slice{
- debug.Array{
- ElementTypeID: uint64(t.ElemType.Common().Offset),
- Address: uint64(ptr),
- Length: length,
- StrideBits: uint64(t.ElemType.Common().ByteSize) * 8,
- },
- capacity,
- }, nil
- }
-
- // peekString reads a string of the given type at the given address.
- // At most byteLimit bytes will be read. If the string is longer, "..." is appended.
- func (s *Server) peekString(typ *dwarf.StringType, a uint64, byteLimit uint64) (string, error) {
- ptr, err := s.peekPtrStructField(&typ.StructType, a, "str")
- if err != nil {
- return "", err
- }
- length, err := s.peekUintOrIntStructField(&typ.StructType, a, "len")
- if err != nil {
- return "", err
- }
- if length > byteLimit {
- buf := make([]byte, byteLimit, byteLimit+3)
- if err := s.peekBytes(ptr, buf); err != nil {
- return "", err
- } else {
- buf = append(buf, '.', '.', '.')
- return string(buf), nil
- }
- } else {
- buf := make([]byte, length)
- if err := s.peekBytes(ptr, buf); err != nil {
- return "", err
- } else {
- return string(buf), nil
- }
- }
- }
-
- // peekCString reads a NUL-terminated string at the given address.
- // At most byteLimit bytes will be read. If the string is longer, "..." is appended.
- // peekCString never returns errors; if an error occurs, the string will be truncated in some way.
- func (s *Server) peekCString(a uint64, byteLimit uint64) string {
- buf := make([]byte, byteLimit, byteLimit+3)
- s.peekBytes(a, buf)
- for i, c := range buf {
- if c == 0 {
- return string(buf[0:i])
- }
- }
- buf = append(buf, '.', '.', '.')
- return string(buf)
- }
-
- // 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)
- }
-
- // peekStringStructField reads a string field from the struct of the given type
- // at the given address.
- // At most byteLimit bytes will be read. If the string is longer, "..." is appended.
- func (s *Server) peekStringStructField(t *dwarf.StructType, addr uint64, fieldName string, byteLimit uint64) (string, error) {
- f, err := getField(t, fieldName)
- if err != nil {
- return "", fmt.Errorf("reading field %s: %s", fieldName, err)
- }
- st, ok := followTypedefs(f.Type).(*dwarf.StringType)
- if !ok {
- return "", fmt.Errorf("field %s is not a string", fieldName)
- }
- return s.peekString(st, addr+uint64(f.ByteOffset), byteLimit)
- }
-
- // 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
- }
|