mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +08:00
Implements set command for pointers and numerical values
set <variable name> <value>
This commit is contained in:
14
_fixtures/testvariables3.go
Normal file
14
_fixtures/testvariables3.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
i1 := 1
|
||||
i2 := 2
|
||||
p1 := &i1
|
||||
runtime.Breakpoint()
|
||||
fmt.Printf("%d %d %v\n", i1, i2, p1)
|
||||
}
|
||||
@ -6,6 +6,9 @@ import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
@ -337,6 +340,15 @@ func (scope *EvalScope) EvalVariable(name string) (*Variable, error) {
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Sets the value of the named variable
|
||||
func (scope *EvalScope) SetVariable(name, value string) error {
|
||||
addr, err := scope.ExtractVariableInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return addr.setValue(value)
|
||||
}
|
||||
|
||||
func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
|
||||
rdr := scope.DwarfReader()
|
||||
v, err := scope.extractVarInfoFromEntry(entry, rdr)
|
||||
@ -627,6 +639,27 @@ func (v *Variable) loadValueInternal(printStructName bool, recurseLevel int) (st
|
||||
return "", fmt.Errorf("could not find value for type %s", v.dwarfType)
|
||||
}
|
||||
|
||||
func (v *Variable) setValue(value string) error {
|
||||
v = v.resolveTypedefs()
|
||||
|
||||
switch t := v.dwarfType.(type) {
|
||||
case *dwarf.PtrType:
|
||||
return v.writeUint(false, value, int64(v.thread.dbp.arch.PtrSize()))
|
||||
case *dwarf.ComplexType:
|
||||
return v.writeComplex(value, t.ByteSize)
|
||||
case *dwarf.IntType:
|
||||
return v.writeUint(true, value, t.ByteSize)
|
||||
case *dwarf.UintType:
|
||||
return v.writeUint(false, value, t.ByteSize)
|
||||
case *dwarf.FloatType:
|
||||
return v.writeFloat(value, t.ByteSize)
|
||||
case *dwarf.BoolType:
|
||||
return v.writeBool(value)
|
||||
default:
|
||||
return fmt.Errorf("Can not set value of variables of type: %T\n", t)
|
||||
}
|
||||
}
|
||||
|
||||
func (thread *Thread) readString(addr uintptr) (string, error) {
|
||||
// string data structure is always two ptrs in size. Addr, followed by len
|
||||
// http://research.swtch.com/godata
|
||||
@ -775,6 +808,81 @@ func (v *Variable) readComplex(size int64) (string, error) {
|
||||
return fmt.Sprintf("(%s + %si)", r, i), nil
|
||||
}
|
||||
|
||||
func (v *Variable) writeComplex(value string, size int64) error {
|
||||
var real, imag float64
|
||||
|
||||
expr, err := parser.ParseExpr(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lits []*ast.BasicLit
|
||||
|
||||
if e, ok := expr.(*ast.ParenExpr); ok {
|
||||
expr = e.X
|
||||
}
|
||||
|
||||
switch e := expr.(type) {
|
||||
case *ast.BinaryExpr: // "<float> + <float>i" or "<float>i + <float>"
|
||||
x, xok := e.X.(*ast.BasicLit)
|
||||
y, yok := e.Y.(*ast.BasicLit)
|
||||
if e.Op != token.ADD || !xok || !yok {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
lits = []*ast.BasicLit{x, y}
|
||||
case *ast.CallExpr: // "complex(<float>, <float>)"
|
||||
tname, ok := e.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
if (tname.Name != "complex64") && (tname.Name != "complex128") {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
if len(e.Args) != 2 {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
for i := range e.Args {
|
||||
lit, ok := e.Args[i].(*ast.BasicLit)
|
||||
if !ok {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
lits = append(lits, lit)
|
||||
}
|
||||
lits[1].Kind = token.IMAG
|
||||
lits[1].Value = lits[1].Value + "i"
|
||||
case *ast.BasicLit: // "<float>" or "<float>i"
|
||||
lits = []*ast.BasicLit{e}
|
||||
default:
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
|
||||
for _, lit := range lits {
|
||||
var err error
|
||||
var v float64
|
||||
switch lit.Kind {
|
||||
case token.FLOAT, token.INT:
|
||||
v, err = strconv.ParseFloat(lit.Value, int(size/2))
|
||||
real += v
|
||||
case token.IMAG:
|
||||
v, err = strconv.ParseFloat(lit.Value[:len(lit.Value)-1], int(size/2))
|
||||
imag += v
|
||||
default:
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = v.writeFloatRaw(real, int64(size/2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
imagaddr := *v
|
||||
imagaddr.Addr += uintptr(size / 2)
|
||||
return imagaddr.writeFloatRaw(imag, int64(size/2))
|
||||
}
|
||||
|
||||
func (v *Variable) readInt(size int64) (string, error) {
|
||||
n, err := v.thread.readIntRaw(v.Addr, size)
|
||||
if err != nil {
|
||||
@ -813,6 +921,37 @@ func (v *Variable) readUint(size int64) (string, error) {
|
||||
return strconv.FormatUint(n, 10), nil
|
||||
}
|
||||
|
||||
func (v *Variable) writeUint(signed bool, value string, size int64) error {
|
||||
var n uint64
|
||||
var err error
|
||||
if signed {
|
||||
var m int64
|
||||
m, err = strconv.ParseInt(value, 0, int(size*8))
|
||||
n = uint64(m)
|
||||
} else {
|
||||
n, err = strconv.ParseUint(value, 0, int(size*8))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val := make([]byte, size)
|
||||
|
||||
switch size {
|
||||
case 1:
|
||||
val[0] = byte(n)
|
||||
case 2:
|
||||
binary.LittleEndian.PutUint16(val, uint16(n))
|
||||
case 4:
|
||||
binary.LittleEndian.PutUint32(val, uint32(n))
|
||||
case 8:
|
||||
binary.LittleEndian.PutUint64(val, uint64(n))
|
||||
}
|
||||
|
||||
_, err = v.thread.writeMemory(v.Addr, val)
|
||||
return err
|
||||
}
|
||||
|
||||
func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) {
|
||||
var n uint64
|
||||
|
||||
@ -856,6 +995,30 @@ func (v *Variable) readFloat(size int64) (string, error) {
|
||||
return "", fmt.Errorf("could not read float")
|
||||
}
|
||||
|
||||
func (v *Variable) writeFloat(value string, size int64) error {
|
||||
f, err := strconv.ParseFloat(value, int(size*8))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.writeFloatRaw(f, size)
|
||||
}
|
||||
|
||||
func (v *Variable) writeFloatRaw(f float64, size int64) error {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
switch size {
|
||||
case 4:
|
||||
n := float32(f)
|
||||
binary.Write(buf, binary.LittleEndian, n)
|
||||
case 8:
|
||||
n := float64(f)
|
||||
binary.Write(buf, binary.LittleEndian, n)
|
||||
}
|
||||
|
||||
_, err := v.thread.writeMemory(v.Addr, buf.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *Variable) readBool() (string, error) {
|
||||
val, err := v.thread.readMemory(v.Addr, 1)
|
||||
if err != nil {
|
||||
@ -869,6 +1032,19 @@ func (v *Variable) readBool() (string, error) {
|
||||
return "true", nil
|
||||
}
|
||||
|
||||
func (v *Variable) writeBool(value string) error {
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val := []byte{0}
|
||||
if b {
|
||||
val[0] = *(*byte)(unsafe.Pointer(&b))
|
||||
}
|
||||
_, err = v.thread.writeMemory(v.Addr, val)
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *Variable) readFunctionPtr() (string, error) {
|
||||
val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize())
|
||||
if err != nil {
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
type varTest struct {
|
||||
name string
|
||||
value string
|
||||
setTo string
|
||||
varType string
|
||||
err error
|
||||
}
|
||||
@ -38,48 +39,67 @@ func evalVariable(p *Process, symbol string) (*Variable, error) {
|
||||
return scope.EvalVariable(symbol)
|
||||
}
|
||||
|
||||
func (tc *varTest) settable() bool {
|
||||
return tc.setTo != ""
|
||||
}
|
||||
|
||||
func (tc *varTest) afterSet() varTest {
|
||||
r := *tc
|
||||
r.value = r.setTo
|
||||
return r
|
||||
}
|
||||
|
||||
func setVariable(p *Process, symbol, value string) error {
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return scope.SetVariable(symbol, value)
|
||||
}
|
||||
|
||||
const varTestBreakpointLineNumber = 59
|
||||
|
||||
func TestVariableEvaluation(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
{"a1", "foofoofoofoofoofoo", "struct string", nil},
|
||||
{"a10", "ofo", "struct string", nil},
|
||||
{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "[3]main.FooBar", nil},
|
||||
{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "struct []main.FooBar", nil},
|
||||
{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "int", nil},
|
||||
{"a3", "7.23", "float64", nil},
|
||||
{"a4", "[2]int [1,2]", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: word}", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "*main.FooBar", nil},
|
||||
{"baz", "bazburzum", "struct string", nil},
|
||||
{"neg", "-1", "int", nil},
|
||||
{"f32", "1.2", "float32", nil},
|
||||
{"c64", "(1 + 2i)", "complex64", nil},
|
||||
{"c128", "(2 + 3i)", "complex128", nil},
|
||||
{"a6.Baz", "8", "int", nil},
|
||||
{"a7.Baz", "5", "int", nil},
|
||||
{"a8.Baz", "feh", "struct string", nil},
|
||||
{"a9.Baz", "nil", "int", fmt.Errorf("a9 is nil")},
|
||||
{"a9.NonExistent", "nil", "int", fmt.Errorf("a9 has no member NonExistent")},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", "[2]int32 [1,2]", "[2]int32", nil},
|
||||
{"b1", "true", "bool", nil},
|
||||
{"b2", "false", "bool", nil}, {"i8", "1", "int8", nil},
|
||||
{"u16", "65535", "uint16", nil},
|
||||
{"u32", "4294967295", "uint32", nil},
|
||||
{"u64", "18446744073709551615", "uint64", nil},
|
||||
{"u8", "255", "uint8", nil},
|
||||
{"up", "5", "uintptr", nil},
|
||||
{"f", "main.barfoo", "func()", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "struct []int", nil},
|
||||
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil},
|
||||
{"main.p1", "10", "int", nil},
|
||||
{"p1", "10", "int", nil},
|
||||
{"NonExistent", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
|
||||
{"a1", "foofoofoofoofoofoo", "", "struct string", nil},
|
||||
{"a10", "ofo", "", "struct string", nil},
|
||||
{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "", "[3]main.FooBar", nil},
|
||||
{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "", "struct []main.FooBar", nil},
|
||||
{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "10", "int", nil},
|
||||
{"a3", "7.23", "3.1", "float64", nil},
|
||||
{"a4", "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: word}", "", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"baz", "bazburzum", "", "struct string", nil},
|
||||
{"neg", "-1", "-20", "int", nil},
|
||||
{"f32", "1.2", "1.1", "float32", nil},
|
||||
{"c64", "(1 + 2i)", "(4 + 5i)", "complex64", nil},
|
||||
{"c128", "(2 + 3i)", "(6.3 + 7i)", "complex128", nil},
|
||||
{"a6.Baz", "8", "20", "int", nil},
|
||||
{"a7.Baz", "5", "25", "int", nil},
|
||||
{"a8.Baz", "feh", "", "struct string", nil},
|
||||
{"a9.Baz", "nil", "", "int", fmt.Errorf("a9 is nil")},
|
||||
{"a9.NonExistent", "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"b1", "true", "false", "bool", nil},
|
||||
{"b2", "false", "true", "bool", nil},
|
||||
{"i8", "1", "2", "int8", nil},
|
||||
{"u16", "65535", "0", "uint16", nil},
|
||||
{"u32", "4294967295", "1", "uint32", nil},
|
||||
{"u64", "18446744073709551615", "2", "uint64", nil},
|
||||
{"u8", "255", "3", "uint8", nil},
|
||||
{"up", "5", "4", "uintptr", nil},
|
||||
{"f", "main.barfoo", "", "func()", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "", "main.Nest", nil},
|
||||
{"main.p1", "10", "12", "int", nil},
|
||||
{"p1", "10", "13", "int", nil},
|
||||
{"NonExistent", "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
|
||||
@ -101,6 +121,18 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if tc.settable() {
|
||||
assertNoError(setVariable(p, tc.name, tc.setTo), t, "SetVariable()")
|
||||
variable, err = evalVariable(p, tc.name)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
assertVariable(t, variable, tc.afterSet())
|
||||
|
||||
assertNoError(setVariable(p, tc.name, tc.value), t, "SetVariable()")
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
assertVariable(t, variable, tc)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -165,39 +197,39 @@ func TestLocalVariables(t *testing.T) {
|
||||
}{
|
||||
{(*EvalScope).LocalVariables,
|
||||
[]varTest{
|
||||
{"a1", "foofoofoofoofoofoo", "struct string", nil},
|
||||
{"a10", "ofo", "struct string", nil},
|
||||
{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "[3]main.FooBar", nil},
|
||||
{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "struct []main.FooBar", nil},
|
||||
{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "int", nil},
|
||||
{"a3", "7.23", "float64", nil},
|
||||
{"a4", "[2]int [1,2]", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: word}", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "*main.FooBar", nil},
|
||||
{"b1", "true", "bool", nil},
|
||||
{"b2", "false", "bool", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "struct []int", nil},
|
||||
{"c128", "(2 + 3i)", "complex128", nil},
|
||||
{"c64", "(1 + 2i)", "complex64", nil},
|
||||
{"f", "main.barfoo", "func()", nil},
|
||||
{"f32", "1.2", "float32", nil},
|
||||
{"i32", "[2]int32 [1,2]", "[2]int32", nil},
|
||||
{"i8", "1", "int8", nil},
|
||||
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil},
|
||||
{"neg", "-1", "int", nil},
|
||||
{"u16", "65535", "uint16", nil},
|
||||
{"u32", "4294967295", "uint32", nil},
|
||||
{"u64", "18446744073709551615", "uint64", nil},
|
||||
{"u8", "255", "uint8", nil},
|
||||
{"up", "5", "uintptr", nil}}},
|
||||
{"a1", "foofoofoofoofoofoo", "", "struct string", nil},
|
||||
{"a10", "ofo", "", "struct string", nil},
|
||||
{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "", "[3]main.FooBar", nil},
|
||||
{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "", "struct []main.FooBar", nil},
|
||||
{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "", "int", nil},
|
||||
{"a3", "7.23", "", "float64", nil},
|
||||
{"a4", "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: word}", "", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"b1", "true", "", "bool", nil},
|
||||
{"b2", "false", "", "bool", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"c128", "(2 + 3i)", "", "complex128", nil},
|
||||
{"c64", "(1 + 2i)", "", "complex64", nil},
|
||||
{"f", "main.barfoo", "", "func()", nil},
|
||||
{"f32", "1.2", "", "float32", nil},
|
||||
{"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"i8", "1", "", "int8", nil},
|
||||
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "", "main.Nest", nil},
|
||||
{"neg", "-1", "", "int", nil},
|
||||
{"u16", "65535", "", "uint16", nil},
|
||||
{"u32", "4294967295", "", "uint32", nil},
|
||||
{"u64", "18446744073709551615", "", "uint64", nil},
|
||||
{"u8", "255", "", "uint8", nil},
|
||||
{"up", "5", "", "uintptr", nil}}},
|
||||
{(*EvalScope).FunctionArguments,
|
||||
[]varTest{
|
||||
{"bar", "main.FooBar {Baz: 10, Bur: lorem}", "main.FooBar", nil},
|
||||
{"baz", "bazburzum", "struct string", nil}}},
|
||||
{"bar", "main.FooBar {Baz: 10, Bur: lorem}", "", "main.FooBar", nil},
|
||||
{"baz", "bazburzum", "", "struct string", nil}}},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
|
||||
@ -302,3 +334,57 @@ func TestFrameEvaluation(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestComplexSetting(t *testing.T) {
|
||||
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
|
||||
pc, _, _ := p.goSymTable.LineToPC(fixture.Source, varTestBreakpointLineNumber)
|
||||
|
||||
_, err := p.SetBreakpoint(pc)
|
||||
assertNoError(err, t, "SetBreakpoint() returned an error")
|
||||
|
||||
err = p.Continue()
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
h := func(setExpr, value string) {
|
||||
assertNoError(setVariable(p, "c128", setExpr), t, "SetVariable()")
|
||||
variable, err := evalVariable(p, "c128")
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
if variable.Value != value {
|
||||
t.Fatalf("Wrong value of c128: \"%s\", expected \"%s\" after setting it to \"%s\"", variable.Value, value, setExpr)
|
||||
}
|
||||
}
|
||||
|
||||
h("3.2i", "(0 + 3.2i)")
|
||||
h("1.1", "(1.1 + 0i)")
|
||||
h("1 + 3.3i", "(1 + 3.3i)")
|
||||
h("complex128(1.2, 3.4)", "(1.2 + 3.4i)")
|
||||
})
|
||||
}
|
||||
|
||||
func TestPointerSetting(t *testing.T) {
|
||||
withTestProcess("testvariables3", t, func(p *Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
|
||||
pval := func(value string) {
|
||||
variable, err := evalVariable(p, "p1")
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
if variable.Value != value {
|
||||
t.Fatalf("Wrong value of p1, \"%s\" expected \"%s\"", variable.Value, value)
|
||||
}
|
||||
}
|
||||
|
||||
pval("*1")
|
||||
|
||||
// change p1 to point to i2
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
assertNoError(err, t, "Scope()")
|
||||
i2addr, err := scope.ExtractVariableInfo("i2")
|
||||
assertNoError(err, t, "EvalVariableAddr()")
|
||||
assertNoError(setVariable(p, "p1", strconv.Itoa(int(i2addr.Addr))), t, "SetVariable()")
|
||||
pval("*2")
|
||||
|
||||
// change the value of i2 check that p1 also changes
|
||||
assertNoError(setVariable(p, "i2", "5"), t, "SetVariable()")
|
||||
pval("*5")
|
||||
})
|
||||
}
|
||||
|
||||
@ -53,6 +53,9 @@ type Client interface {
|
||||
// ListPackageVariablesFor lists all package variables in the context of a thread.
|
||||
ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error)
|
||||
|
||||
// SetVariable sets the value of a variable
|
||||
SetVariable(scope api.EvalScope, symbol, value string) error
|
||||
|
||||
// ListSources lists all source files in the process matching filter.
|
||||
ListSources(filter string) ([]string, error)
|
||||
// ListFunctions lists all functions in the process matching filter.
|
||||
|
||||
@ -442,6 +442,14 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api
|
||||
return &converted, err
|
||||
}
|
||||
|
||||
func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error {
|
||||
s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.SetVariable(symbol, value)
|
||||
}
|
||||
|
||||
func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
|
||||
goroutines := []*api.Goroutine{}
|
||||
gs, err := d.process.GoroutinesInfo()
|
||||
|
||||
@ -155,6 +155,11 @@ func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Varia
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
|
||||
var unused int
|
||||
return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused)
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListSources(filter string) ([]string, error) {
|
||||
var sources []string
|
||||
err := c.call("ListSources", filter, &sources)
|
||||
|
||||
@ -249,6 +249,17 @@ func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetSymbolArgs struct {
|
||||
Scope api.EvalScope
|
||||
Symbol string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error {
|
||||
*unused = 0
|
||||
return s.debugger.SetVariableInScope(args.Scope, args.Symbol, args.Value)
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListSources(filter string, sources *[]string) error {
|
||||
ss, err := s.debugger.Sources(filter)
|
||||
if err != nil {
|
||||
|
||||
@ -629,7 +629,31 @@ func TestClientServer_EvalVariable(t *testing.T) {
|
||||
t.Logf("var1: <%s>", var1.Value)
|
||||
|
||||
if var1.Value != "foofoofoofoofoofoo" {
|
||||
t.Fatalf("Wrong variable value (EvalVariable)", var1.Value)
|
||||
t.Fatalf("Wrong variable value: %v", var1.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestClientServer_SetVariable(t *testing.T) {
|
||||
withTestClient("testvariables", t, func(c service.Client) {
|
||||
fp := testProgPath(t, "testvariables")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 59})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
|
||||
state := <-c.Continue()
|
||||
|
||||
if state.Err != nil {
|
||||
t.Fatalf("Continue(): %v\n", state.Err)
|
||||
}
|
||||
|
||||
assertNoError(c.SetVariable(api.EvalScope{ -1, 0 }, "a2", "8"), t, "SetVariable()")
|
||||
|
||||
a2, err := c.EvalVariable(api.EvalScope{ -1, 0 }, "a2")
|
||||
|
||||
t.Logf("a2: <%s>", a2.Value)
|
||||
|
||||
if a2.Value != "8" {
|
||||
t.Fatalf("Wrong variable value: %v", a2.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -67,6 +67,7 @@ func DebugCommands(client service.Client) *Commands {
|
||||
{aliases: []string{"goroutine"}, cmdFn: goroutine, helpMsg: "Sets current goroutine."},
|
||||
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
|
||||
{aliases: []string{"print", "p"}, cmdFn: g0f0(printVar), helpMsg: "Evaluate a variable."},
|
||||
{aliases: []string{"set"}, cmdFn: g0f0(setVar), helpMsg: "Changes the value of a variable."},
|
||||
{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."},
|
||||
{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."},
|
||||
{aliases: []string{"args"}, cmdFn: filterSortAndOutput(g0f0filter(args)), helpMsg: "Print function arguments, optionally filtered by a regexp."},
|
||||
@ -557,6 +558,14 @@ func printVar(client service.Client, scope api.EvalScope, args ...string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func setVar(client service.Client, scope api.EvalScope, args ...string) error {
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("wrong number of arguments")
|
||||
}
|
||||
|
||||
return client.SetVariable(scope, args[0], args[1])
|
||||
}
|
||||
|
||||
func filterVariables(vars []api.Variable, filter string) []string {
|
||||
reg, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user