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