Implements set command for pointers and numerical values

set <variable name> <value>
This commit is contained in:
aarzilli
2015-09-28 12:01:18 +02:00
committed by Derek Parker
parent 8f2ccbad0a
commit c9b517067b
9 changed files with 406 additions and 70 deletions

View 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)
}

View File

@ -6,6 +6,9 @@ import (
"debug/gosym" "debug/gosym"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"go/ast"
"go/parser"
"go/token"
"strconv" "strconv"
"strings" "strings"
"unsafe" "unsafe"
@ -337,6 +340,15 @@ func (scope *EvalScope) EvalVariable(name string) (*Variable, error) {
return v, err 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) { func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
rdr := scope.DwarfReader() rdr := scope.DwarfReader()
v, err := scope.extractVarInfoFromEntry(entry, rdr) 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) 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) { func (thread *Thread) readString(addr uintptr) (string, error) {
// string data structure is always two ptrs in size. Addr, followed by len // string data structure is always two ptrs in size. Addr, followed by len
// http://research.swtch.com/godata // 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 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) { func (v *Variable) readInt(size int64) (string, error) {
n, err := v.thread.readIntRaw(v.Addr, size) n, err := v.thread.readIntRaw(v.Addr, size)
if err != nil { if err != nil {
@ -813,6 +921,37 @@ func (v *Variable) readUint(size int64) (string, error) {
return strconv.FormatUint(n, 10), nil 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) { func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) {
var n uint64 var n uint64
@ -856,6 +995,30 @@ func (v *Variable) readFloat(size int64) (string, error) {
return "", fmt.Errorf("could not read float") 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) { func (v *Variable) readBool() (string, error) {
val, err := v.thread.readMemory(v.Addr, 1) val, err := v.thread.readMemory(v.Addr, 1)
if err != nil { if err != nil {
@ -869,6 +1032,19 @@ func (v *Variable) readBool() (string, error) {
return "true", nil 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) { func (v *Variable) readFunctionPtr() (string, error) {
val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize()) val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize())
if err != nil { if err != nil {

View File

@ -12,6 +12,7 @@ import (
type varTest struct { type varTest struct {
name string name string
value string value string
setTo string
varType string varType string
err error err error
} }
@ -38,48 +39,67 @@ func evalVariable(p *Process, symbol string) (*Variable, error) {
return scope.EvalVariable(symbol) 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 const varTestBreakpointLineNumber = 59
func TestVariableEvaluation(t *testing.T) { func TestVariableEvaluation(t *testing.T) {
testcases := []varTest{ testcases := []varTest{
{"a1", "foofoofoofoofoofoo", "struct string", nil}, {"a1", "foofoofoofoofoofoo", "", "struct string", nil},
{"a10", "ofo", "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}, {"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}, {"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}, {"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}, {"a2", "6", "10", "int", nil},
{"a3", "7.23", "float64", nil}, {"a3", "7.23", "3.1", "float64", nil},
{"a4", "[2]int [1,2]", "[2]int", nil}, {"a4", "[2]int [1,2]", "", "[2]int", nil},
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []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}, {"a6", "main.FooBar {Baz: 8, Bur: word}", "", "main.FooBar", nil},
{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil}, {"a7", "*main.FooBar {Baz: 5, Bur: strum}", "", "*main.FooBar", nil},
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, {"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil},
{"a9", "*main.FooBar nil", "*main.FooBar", nil}, {"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
{"baz", "bazburzum", "struct string", nil}, {"baz", "bazburzum", "", "struct string", nil},
{"neg", "-1", "int", nil}, {"neg", "-1", "-20", "int", nil},
{"f32", "1.2", "float32", nil}, {"f32", "1.2", "1.1", "float32", nil},
{"c64", "(1 + 2i)", "complex64", nil}, {"c64", "(1 + 2i)", "(4 + 5i)", "complex64", nil},
{"c128", "(2 + 3i)", "complex128", nil}, {"c128", "(2 + 3i)", "(6.3 + 7i)", "complex128", nil},
{"a6.Baz", "8", "int", nil}, {"a6.Baz", "8", "20", "int", nil},
{"a7.Baz", "5", "int", nil}, {"a7.Baz", "5", "25", "int", nil},
{"a8.Baz", "feh", "struct string", nil}, {"a8.Baz", "feh", "", "struct string", nil},
{"a9.Baz", "nil", "int", fmt.Errorf("a9 is nil")}, {"a9.Baz", "nil", "", "int", fmt.Errorf("a9 is nil")},
{"a9.NonExistent", "nil", "int", fmt.Errorf("a9 has no member NonExistent")}, {"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 {"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil}, // reread variable after member
{"i32", "[2]int32 [1,2]", "[2]int32", nil}, {"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
{"b1", "true", "bool", nil}, {"b1", "true", "false", "bool", nil},
{"b2", "false", "bool", nil}, {"i8", "1", "int8", nil}, {"b2", "false", "true", "bool", nil},
{"u16", "65535", "uint16", nil}, {"i8", "1", "2", "int8", nil},
{"u32", "4294967295", "uint32", nil}, {"u16", "65535", "0", "uint16", nil},
{"u64", "18446744073709551615", "uint64", nil}, {"u32", "4294967295", "1", "uint32", nil},
{"u8", "255", "uint8", nil}, {"u64", "18446744073709551615", "2", "uint64", nil},
{"up", "5", "uintptr", nil}, {"u8", "255", "3", "uint8", nil},
{"f", "main.barfoo", "func()", nil}, {"up", "5", "4", "uintptr", 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}, {"f", "main.barfoo", "", "func()", nil},
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", 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},
{"main.p1", "10", "int", nil}, {"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "", "main.Nest", nil},
{"p1", "10", "int", nil}, {"main.p1", "10", "12", "int", nil},
{"NonExistent", "", "", fmt.Errorf("could not find symbol value for NonExistent")}, {"p1", "10", "13", "int", nil},
{"NonExistent", "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
} }
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { 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()) 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, {(*EvalScope).LocalVariables,
[]varTest{ []varTest{
{"a1", "foofoofoofoofoofoo", "struct string", nil}, {"a1", "foofoofoofoofoofoo", "", "struct string", nil},
{"a10", "ofo", "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}, {"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}, {"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}, {"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}, {"a2", "6", "", "int", nil},
{"a3", "7.23", "float64", nil}, {"a3", "7.23", "", "float64", nil},
{"a4", "[2]int [1,2]", "[2]int", nil}, {"a4", "[2]int [1,2]", "", "[2]int", nil},
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []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}, {"a6", "main.FooBar {Baz: 8, Bur: word}", "", "main.FooBar", nil},
{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil}, {"a7", "*main.FooBar {Baz: 5, Bur: strum}", "", "*main.FooBar", nil},
{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, {"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil},
{"a9", "*main.FooBar nil", "*main.FooBar", nil}, {"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
{"b1", "true", "bool", nil}, {"b1", "true", "", "bool", nil},
{"b2", "false", "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}, {"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}, {"c128", "(2 + 3i)", "", "complex128", nil},
{"c64", "(1 + 2i)", "complex64", nil}, {"c64", "(1 + 2i)", "", "complex64", nil},
{"f", "main.barfoo", "func()", nil}, {"f", "main.barfoo", "", "func()", nil},
{"f32", "1.2", "float32", nil}, {"f32", "1.2", "", "float32", nil},
{"i32", "[2]int32 [1,2]", "[2]int32", nil}, {"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
{"i8", "1", "int8", nil}, {"i8", "1", "", "int8", nil},
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil}, {"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "", "main.Nest", nil},
{"neg", "-1", "int", nil}, {"neg", "-1", "", "int", nil},
{"u16", "65535", "uint16", nil}, {"u16", "65535", "", "uint16", nil},
{"u32", "4294967295", "uint32", nil}, {"u32", "4294967295", "", "uint32", nil},
{"u64", "18446744073709551615", "uint64", nil}, {"u64", "18446744073709551615", "", "uint64", nil},
{"u8", "255", "uint8", nil}, {"u8", "255", "", "uint8", nil},
{"up", "5", "uintptr", nil}}}, {"up", "5", "", "uintptr", nil}}},
{(*EvalScope).FunctionArguments, {(*EvalScope).FunctionArguments,
[]varTest{ []varTest{
{"bar", "main.FooBar {Baz: 10, Bur: lorem}", "main.FooBar", nil}, {"bar", "main.FooBar {Baz: 10, Bur: lorem}", "", "main.FooBar", nil},
{"baz", "bazburzum", "struct string", nil}}}, {"baz", "bazburzum", "", "struct string", nil}}},
} }
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { 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")
})
}

View File

@ -53,6 +53,9 @@ type Client interface {
// ListPackageVariablesFor lists all package variables in the context of a thread. // ListPackageVariablesFor lists all package variables in the context of a thread.
ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) 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 lists all source files in the process matching filter.
ListSources(filter string) ([]string, error) ListSources(filter string) ([]string, error)
// ListFunctions lists all functions in the process matching filter. // ListFunctions lists all functions in the process matching filter.

View File

@ -442,6 +442,14 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api
return &converted, err 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) { func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
goroutines := []*api.Goroutine{} goroutines := []*api.Goroutine{}
gs, err := d.process.GoroutinesInfo() gs, err := d.process.GoroutinesInfo()

View File

@ -155,6 +155,11 @@ func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Varia
return v, err 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) { func (c *RPCClient) ListSources(filter string) ([]string, error) {
var sources []string var sources []string
err := c.call("ListSources", filter, &sources) err := c.call("ListSources", filter, &sources)

View File

@ -249,6 +249,17 @@ func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) erro
return nil 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 { func (s *RPCServer) ListSources(filter string, sources *[]string) error {
ss, err := s.debugger.Sources(filter) ss, err := s.debugger.Sources(filter)
if err != nil { if err != nil {

View File

@ -629,7 +629,31 @@ func TestClientServer_EvalVariable(t *testing.T) {
t.Logf("var1: <%s>", var1.Value) t.Logf("var1: <%s>", var1.Value)
if var1.Value != "foofoofoofoofoofoo" { 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)
} }
}) })
} }

View File

@ -67,6 +67,7 @@ func DebugCommands(client service.Client) *Commands {
{aliases: []string{"goroutine"}, cmdFn: goroutine, helpMsg: "Sets current goroutine."}, {aliases: []string{"goroutine"}, cmdFn: goroutine, helpMsg: "Sets current goroutine."},
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."}, {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{"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{"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{"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."}, {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 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 { func filterVariables(vars []api.Variable, filter string) []string {
reg, err := regexp.Compile(filter) reg, err := regexp.Compile(filter)
if err != nil { if err != nil {