提交 50b5fc92 编写于 作者: A aarzilli 提交者: Derek Parker

Changed api.Variable to have a machine readable value

The new contents of api.Variable are documented in
proc/variables.go.

Implements #243
上级 91939bc9
package main
import "fmt"
import "runtime"
type FooBar struct {
Baz int
......@@ -19,6 +20,7 @@ type Nest struct {
}
func barfoo() {
runtime.Breakpoint()
a1 := "bur"
fmt.Println(a1)
}
......@@ -56,6 +58,7 @@ func foobar(baz string, bar FooBar) {
ba = make([]int, 200, 200) // Test array size capping
)
runtime.Breakpoint()
barfoo()
fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, b1, b2, baz, neg, i8, u8, u16, u32, u64, up, f32, c64, c128, i32, bar, f, ms, ba, p1)
}
......
......@@ -10,5 +10,5 @@ func main() {
i2 := 2
p1 := &i1
runtime.Breakpoint()
fmt.Printf("%d %d %v\n", i1, i2, p1)
fmt.Println(i1, i2, p1)
}
package main
import (
"fmt"
"runtime"
"fmt"
"runtime"
)
type A struct {
val int
val int
}
type C struct {
......@@ -14,20 +14,20 @@ type C struct {
}
type B struct {
A
A
*C
a A
ptr *A
a A
ptr *A
}
func main() {
b := B{A: A{-314}, C: &C{"hello"}, a: A{42}, ptr: &A{1337}}
runtime.Breakpoint()
fmt.Println(b)
fmt.Println(b.val)
fmt.Println(b.A.val)
fmt.Println(b.a.val)
fmt.Println(b.ptr.val)
fmt.Println(b.C.s)
fmt.Println(b.s)
}
\ No newline at end of file
b := B{A: A{-314}, C: &C{"hello"}, a: A{42}, ptr: &A{1337}}
runtime.Breakpoint()
fmt.Println(b)
fmt.Println(b.val)
fmt.Println(b.A.val)
fmt.Println(b.a.val)
fmt.Println(b.ptr.val)
fmt.Println(b.C.s)
fmt.Println(b.s)
}
......@@ -195,7 +195,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
}
if state.BreakpointInfo != nil {
for _, arg := range state.BreakpointInfo.Arguments {
args = append(args, arg.Value)
args = append(args, arg.SinglelineString())
}
}
fmt.Printf("%s(%s) %s:%d\n", fname, strings.Join(args, ", "), state.CurrentThread.File, state.CurrentThread.Line)
......
......@@ -707,9 +707,9 @@ func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error)
return
}
ver, ok := parseVersionString(vv.Value)
ver, ok := parseVersionString(vv.Value.(string))
if !ok {
err = fmt.Errorf("Could not parse version number: %s\n", vv.Value)
err = fmt.Errorf("Could not parse version number: %v\n", vv.Value)
return
}
......
......@@ -7,7 +7,9 @@ import (
"net/http"
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"testing"
"time"
......@@ -332,7 +334,7 @@ func TestNextConcurrent(t *testing.T) {
}
v, err := evalVariable(p, "n")
assertNoError(err, t, "EvalVariable")
if v.Value != initV.Value {
if v.Value.(int64) != initV.Value.(int64) {
t.Fatal("Did not end up on same goroutine")
}
}
......@@ -809,3 +811,221 @@ func TestIssue239(t *testing.T) {
assertNoError(p.Continue(), t, fmt.Sprintf("Continue()"))
})
}
func evalVariable(p *Process, symbol string) (*Variable, error) {
scope, err := p.CurrentThread.Scope()
if err != nil {
return nil, err
}
return scope.EvalVariable(symbol)
}
func setVariable(p *Process, symbol, value string) error {
scope, err := p.CurrentThread.Scope()
if err != nil {
return err
}
return scope.SetVariable(symbol, value)
}
func TestVariableEvaluation(t *testing.T) {
testcases := []struct {
name string
st reflect.Kind
value interface{}
length, cap int64
childrenlen int
}{
{"a1", reflect.String, "foofoofoofoofoofoo", 18, 0, 0},
{"a11", reflect.Array, nil, 3, -1, 3},
{"a12", reflect.Slice, nil, 2, 2, 2},
{"a13", reflect.Slice, nil, 3, 3, 3},
{"a2", reflect.Int, int64(6), 0, 0, 0},
{"a3", reflect.Float64, float64(7.23), 0, 0, 0},
{"a4", reflect.Array, nil, 2, -1, 2},
{"a5", reflect.Slice, nil, 5, 5, 5},
{"a6", reflect.Struct, nil, 2, 0, 2},
{"a7", reflect.Ptr, nil, 1, 0, 1},
{"a8", reflect.Struct, nil, 2, 0, 2},
{"a9", reflect.Ptr, nil, 1, 0, 1},
{"baz", reflect.String, "bazburzum", 9, 0, 0},
{"neg", reflect.Int, int64(-1), 0, 0, 0},
{"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0},
{"c64", reflect.Complex64, nil, 2, 0, 2},
{"c128", reflect.Complex128, nil, 2, 0, 2},
{"a6.Baz", reflect.Int, int64(8), 0, 0, 0},
{"a7.Baz", reflect.Int, int64(5), 0, 0, 0},
{"a8.Baz", reflect.String, "feh", 3, 0, 0},
{"a8", reflect.Struct, nil, 2, 0, 2},
{"i32", reflect.Array, nil, 2, -1, 2},
{"b1", reflect.Bool, true, 0, 0, 0},
{"b2", reflect.Bool, false, 0, 0, 0},
{"f", reflect.Func, "main.barfoo", 0, 0, 0},
{"ba", reflect.Slice, nil, 200, 200, 64},
}
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue() returned an error")
for _, tc := range testcases {
v, err := evalVariable(p, tc.name)
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", tc.name))
if v.Kind != tc.st {
t.Fatalf("%s simple type: expected: %s got: %s", tc.name, tc.st, v.Kind.String())
}
if v.Value == nil && tc.value != nil {
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
} else {
switch x := v.Value.(type) {
case int64:
if y, ok := tc.value.(int64); !ok || x != y {
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
}
case float64:
if y, ok := tc.value.(float64); !ok || x != y {
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
}
case string:
if y, ok := tc.value.(string); !ok || x != y {
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
}
}
}
if v.Len != tc.length {
t.Fatalf("%s len: expected: %d got: %d", tc.name, tc.length, v.Len)
}
if v.Cap != tc.cap {
t.Fatalf("%s cap: expected: %d got: %d", tc.name, tc.cap, v.Cap)
}
if len(v.Children) != tc.childrenlen {
t.Fatalf("%s children len: expected %d got: %d", tc.name, tc.childrenlen, len(v.Children))
}
}
})
}
func TestFrameEvaluation(t *testing.T) {
withTestProcess("goroutinestackprog", t, func(p *Process, fixture protest.Fixture) {
_, err := setFunctionBreakpoint(p, "main.stacktraceme")
assertNoError(err, t, "setFunctionBreakpoint")
assertNoError(p.Continue(), t, "Continue()")
/**** Testing evaluation on goroutines ****/
gs, err := p.GoroutinesInfo()
assertNoError(err, t, "GoroutinesInfo")
found := make([]bool, 10)
for _, g := range gs {
frame := -1
frames, err := p.GoroutineStacktrace(g, 10)
assertNoError(err, t, "GoroutineStacktrace()")
for i := range frames {
if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" {
frame = i
break
}
}
if frame < 0 {
t.Logf("Goroutine %d: could not find correct frame", g.Id)
continue
}
scope, err := p.ConvertEvalScope(g.Id, frame)
assertNoError(err, t, "ConvertEvalScope()")
t.Logf("scope = %v", scope)
v, err := scope.EvalVariable("i")
t.Logf("v = %v", v)
if err != nil {
t.Logf("Goroutine %d: %v\n", g.Id, err)
continue
}
found[v.Value.(int64)] = true
}
for i := range found {
if !found[i] {
t.Fatalf("Goroutine %d not found\n", i)
}
}
/**** Testing evaluation on frames ****/
assertNoError(p.Continue(), t, "Continue() 2")
g, err := p.CurrentThread.GetG()
assertNoError(err, t, "GetG()")
for i := 0; i <= 3; i++ {
scope, err := p.ConvertEvalScope(g.Id, i+1)
assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1))
v, err := scope.EvalVariable("n")
assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1))
n := v.Value.(int64)
t.Logf("frame %d n %d\n", i+1, n)
if n != int64(3-i) {
t.Fatalf("On frame %d value of n is %d (not %d)", i+1, n, 3-i)
}
}
})
}
func TestPointerSetting(t *testing.T) {
withTestProcess("testvariables3", t, func(p *Process, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue() returned an error")
pval := func(n int64) {
variable, err := evalVariable(p, "p1")
assertNoError(err, t, "EvalVariable()")
if variable.Children[0].Value.(int64) != n {
t.Fatalf("Wrong value of p1, *%d expected *%d", variable.Children[0].Value.(int64), n)
}
}
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)
})
}
func TestVariableFunctionScoping(t *testing.T) {
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
err := p.Continue()
assertNoError(err, t, "Continue() returned an error")
_, err = evalVariable(p, "a1")
assertNoError(err, t, "Unable to find variable a1")
_, err = evalVariable(p, "a2")
assertNoError(err, t, "Unable to find variable a1")
// Move scopes, a1 exists here by a2 does not
err = p.Continue()
assertNoError(err, t, "Continue() returned an error")
_, err = evalVariable(p, "a1")
assertNoError(err, t, "Unable to find variable a1")
_, err = evalVariable(p, "a2")
if err == nil {
t.Fatalf("Can eval out of scope variable a2")
}
})
}
func TestRecursiveStructure(t *testing.T) {
withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue()")
v, err := evalVariable(p, "aas")
assertNoError(err, t, "EvalVariable()")
t.Logf("v: %v\n", v)
})
}
此差异已折叠。
package api
import (
"debug/dwarf"
"debug/gosym"
"fmt"
"strconv"
"github.com/derekparker/delve/proc"
......@@ -57,12 +59,43 @@ func ConvertThread(th *proc.Thread) *Thread {
}
// convertVar converts an internal variable to an API Variable.
func ConvertVar(v *proc.Variable) Variable {
return Variable{
Name: v.Name,
Value: v.Value,
Type: v.Type,
func ConvertVar(v *proc.Variable) *Variable {
r := Variable{
Addr: v.Addr,
Name: v.Name,
Kind: v.Kind,
Len: v.Len,
Cap: v.Cap,
}
if v.DwarfType != nil {
r.Type = v.DwarfType.String()
}
if v.RealType != nil {
r.RealType = v.RealType.String()
}
if v.Unreadable != nil {
r.Unreadable = v.Unreadable.Error()
}
switch typ := v.RealType.(type) {
case *dwarf.FloatType:
r.Value = strconv.FormatFloat(v.Value.(float64), 'f', -1, int(typ.Size()*8))
default:
if v.Value != nil {
r.Value = fmt.Sprintf("%v", v.Value)
}
}
r.Children = make([]Variable, len(v.Children))
for i := range v.Children {
r.Children[i] = *ConvertVar(&v.Children[i])
}
return &r
}
func ConvertFunction(fn *gosym.Func) *Function {
......
package api
import (
"bytes"
"fmt"
"reflect"
)
const (
// strings longer than this will cause slices, arrays and structs to be printed on multiple lines when newlines is enabled
maxShortStringLen = 7
// string used for one indentation level (when printing on multiple lines)
indentString = "\t"
)
// Returns a representation of v on a single line
func (v *Variable) SinglelineString() string {
var buf bytes.Buffer
v.writeTo(&buf, false, true, "")
return buf.String()
}
// Returns a representation of v on multiple lines
func (v *Variable) MultilineString(indent string) string {
var buf bytes.Buffer
buf.WriteString(indent)
v.writeTo(&buf, true, true, indent)
return buf.String()
}
func (v *Variable) writeTo(buf *bytes.Buffer, newlines, includeType bool, indent string) {
if v.Unreadable != "" {
fmt.Fprintf(buf, "(unreadable %s)", v.Unreadable)
return
}
if v.Addr == 0 {
fmt.Fprintf(buf, "%s nil", v.Type)
return
}
switch v.Kind {
case reflect.Slice:
v.writeSliceTo(buf, newlines, includeType, indent)
case reflect.Array:
v.writeArrayTo(buf, newlines, includeType, indent)
case reflect.Ptr:
fmt.Fprintf(buf, "*")
v.Children[0].writeTo(buf, newlines, includeType, indent)
case reflect.String:
v.writeStringTo(buf)
case reflect.Struct:
v.writeStructTo(buf, newlines, includeType, indent)
case reflect.Map:
v.writeMapTo(buf, newlines, includeType, indent)
case reflect.Func:
fmt.Fprintf(buf, "%s", v.Value)
case reflect.Complex64, reflect.Complex128:
switch v.RealType {
case "complex64":
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
case "complex128":
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
}
default:
if v.Value != "" {
buf.Write([]byte(v.Value))
} else {
fmt.Fprintf(buf, "(unknown %s)", v.Kind)
}
}
}
func (v *Variable) writeStringTo(buf *bytes.Buffer) {
s := v.Value
if len(s) != int(v.Len) {
s = fmt.Sprintf("%s...+%d more", s, int(v.Len)-len(s))
}
fmt.Fprintf(buf, "%q", s)
}
func (v *Variable) writeSliceTo(buf *bytes.Buffer, newlines, includeType bool, indent string) {
if includeType {
fmt.Fprintf(buf, "%s len: %d, cap: %d, ", v.Type[len("struct "):], v.Len, v.Cap)
}
v.writeSliceOrArrayTo(buf, newlines, indent)
}
func (v *Variable) writeArrayTo(buf *bytes.Buffer, newlines, includeType bool, indent string) {
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
}
v.writeSliceOrArrayTo(buf, newlines, indent)
}
func (v *Variable) writeStructTo(buf *bytes.Buffer, newlines, includeType bool, indent string) {
if int(v.Len) != len(v.Children) {
fmt.Fprintf(buf, "(*%s)(0x%x)", v.Type, v.Addr)
return
}
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
}
nl := v.shouldNewlineStruct(newlines)
fmt.Fprintf(buf, "{")
for i := range v.Children {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
fmt.Fprintf(buf, "%s: ", v.Children[i].Name)
v.Children[i].writeTo(buf, nl, true, indent+indentString)
if i != len(v.Children)-1 || nl {
fmt.Fprintf(buf, ",")
if !nl {
fmt.Fprintf(buf, " ")
}
}
}
if nl {
fmt.Fprintf(buf, "\n%s", indent)
}
fmt.Fprintf(buf, "}")
}
func (v *Variable) writeMapTo(buf *bytes.Buffer, newlines, includeType bool, indent string) {
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
}
nl := newlines && (len(v.Children) > 0)
fmt.Fprintf(buf, "[")
for i := 0; i < len(v.Children); i += 2 {
key := &v.Children[i]
value := &v.Children[i+1]
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
key.writeTo(buf, false, false, indent+indentString)
fmt.Fprintf(buf, ": ")
value.writeTo(buf, nl, false, indent+indentString)
if i != len(v.Children)-1 || nl {
fmt.Fprintf(buf, ", ")
}
}
if len(v.Children) != int(v.Len) {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
} else {
fmt.Fprintf(buf, ",")
}
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
}
if nl {
fmt.Fprintf(buf, "\n%s", indent)
}
fmt.Fprintf(buf, "]")
}
func (v *Variable) shouldNewlineArray(newlines bool) bool {
if !newlines || len(v.Children) == 0 {
return false
}
kind, hasptr := (&v.Children[0]).recursiveKind()
switch kind {
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map:
return true
case reflect.String:
if hasptr {
return true
}
for i := range v.Children {
if len(v.Children[i].Value) > maxShortStringLen {
return true
}
}
return false
default:
return false
}
}
func (v *Variable) recursiveKind() (reflect.Kind, bool) {
hasptr := false
var kind reflect.Kind
for {
kind = v.Kind
if kind == reflect.Ptr {
hasptr = true
v = &(v.Children[0])
} else {
break
}
}
return kind, hasptr
}
func (v *Variable) shouldNewlineStruct(newlines bool) bool {
if !newlines || len(v.Children) == 0 {
return false
}
for i := range v.Children {
kind, hasptr := (&v.Children[i]).recursiveKind()
switch kind {
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map:
return true
case reflect.String:
if hasptr {
return true
}
if len(v.Children[i].Value) > maxShortStringLen {
return true
}
}
}
return false
}
func (v *Variable) writeSliceOrArrayTo(buf *bytes.Buffer, newlines bool, indent string) {
nl := v.shouldNewlineArray(newlines)
fmt.Fprintf(buf, "[")
for i := range v.Children {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
v.Children[i].writeTo(buf, nl, false, indent+indentString)
if i != len(v.Children)-1 || nl {
fmt.Fprintf(buf, ",")
}
}
if len(v.Children) != int(v.Len) {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
} else {
fmt.Fprintf(buf, ",")
}
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
}
if nl {
fmt.Fprintf(buf, "\n%s", indent)
}
fmt.Fprintf(buf, "]")
}
package api
import "reflect"
// DebuggerState represents the current context of the debugger.
type DebuggerState struct {
// Breakpoint is the current breakpoint at which the debugged process is
......@@ -104,9 +106,35 @@ type Function struct {
// Variable describes a variable.
type Variable struct {
Name string `json:"name"`
// Name of the variable or struct member
Name string `json:"name"`
// Address of the variable or struct member
Addr uintptr `json:"addr"`
// Go type of the variable
Type string `json:"type"`
// Type of the variable after resolving any typedefs
RealType string `json:"realType"`
Kind reflect.Kind `json:"kind"`
//Strings have their length capped at proc.maxArrayValues, use Len for the real length of a string
//Function variables will store the name of the function in this field
Value string `json:"value"`
Type string `json:"type"`
// Number of elements in an array or a slice, number of keys for a map, number of struct members for a struct, length of strings
Len int64 `json:"len"`
// Cap value for slices
Cap int64 `json:"cap"`
// Array and slice elements, member fields of structs, key/value pairs of maps, value of complex numbers
// The Name field in this slice will always be the empty string except for structs (when it will be the field name) and for complex numbers (when it will be "real" and "imaginary")
// For maps each map entry will have to items in this slice, even numbered items will represent map keys and odd numbered items will represent their values
// This field's length is capped at proc.maxArrayValues for slices and arrays and 2*proc.maxArrayValues for maps, in the circumnstances where the cap takes effect len(Children) != Len
// The other length cap applied to this field is related to maximum recursion depth, when the maximum recursion depth is reached this field is left empty, contrary to the previous one this cap also applies to structs (otherwise structs will always have all thier member fields returned)
Children []Variable `json:"children"`
// Unreadable addresses will have this field set
Unreadable string `json:"unreadable"`
}
// Goroutine represents the information relevant to Delve from the runtime's
......
......@@ -312,11 +312,11 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
if err != nil {
return err
}
bpi.Variables[i] = api.ConvertVar(v)
bpi.Variables[i] = *api.ConvertVar(v)
}
vars, err := functionArguments(s)
args, err := s.FunctionArguments()
if err == nil {
bpi.Arguments = vars
bpi.Arguments = convertVars(args)
}
return nil
}
......@@ -376,7 +376,7 @@ func (d *Debugger) PackageVariables(threadID int, filter string) ([]api.Variable
}
for _, v := range pv {
if regex.Match([]byte(v.Name)) {
vars = append(vars, api.ConvertVar(v))
vars = append(vars, *api.ConvertVar(v))
}
}
return vars, err
......@@ -397,7 +397,7 @@ func (d *Debugger) Registers(threadID int) (string, error) {
func convertVars(pv []*proc.Variable) []api.Variable {
vars := make([]api.Variable, 0, len(pv))
for _, v := range pv {
vars = append(vars, api.ConvertVar(v))
vars = append(vars, *api.ConvertVar(v))
}
return vars
}
......@@ -419,19 +419,11 @@ func (d *Debugger) FunctionArguments(scope api.EvalScope) ([]api.Variable, error
if err != nil {
return nil, err
}
return functionArguments(s)
}
func functionArguments(s *proc.EvalScope) ([]api.Variable, error) {
pv, err := s.FunctionArguments()
if err != nil {
return nil, err
}
vars := make([]api.Variable, 0, len(pv))
for _, v := range pv {
vars = append(vars, api.ConvertVar(v))
}
return vars, nil
return convertVars(pv), nil
}
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api.Variable, error) {
......@@ -443,8 +435,7 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api
if err != nil {
return nil, err
}
converted := api.ConvertVar(v)
return &converted, err
return api.ConvertVar(v), err
}
func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error {
......@@ -492,17 +483,19 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, full bool) ([]ap
for i := range rawlocs {
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
if full {
var err error
scope := rawlocs[i].Scope(d.process.CurrentThread)
lv, err := scope.LocalVariables()
locals, err := scope.LocalVariables()
if err != nil {
return nil, err
}
av, err := scope.FunctionArguments()
arguments, err := scope.FunctionArguments()
if err != nil {
return nil, err
}
frame.Locals = convertVars(lv)
frame.Arguments = convertVars(av)
frame.Locals = convertVars(locals)
frame.Arguments = convertVars(arguments)
}
locations = append(locations, frame)
}
......
......@@ -456,8 +456,12 @@ func TestClientServer_traceContinue(t *testing.T) {
t.Fatalf("Wrong variable returned %s", bpi.Variables[0].Name)
}
if bpi.Variables[0].Value != strconv.Itoa(count-1) {
t.Fatalf("Wrong variable value %s (%d)", bpi.Variables[0].Value, count)
t.Logf("Variable i is %v", bpi.Variables[0])
n, err := strconv.Atoi(bpi.Variables[0].Value)
if err != nil || n != count-1 {
t.Fatalf("Wrong variable value %q (%v %d)", bpi.Variables[0].Value, err, count)
}
}
if state.Exited {
......@@ -604,10 +608,6 @@ func TestClientServer_FindLocations(t *testing.T) {
func TestClientServer_EvalVariable(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 {
......@@ -617,20 +617,16 @@ func TestClientServer_EvalVariable(t *testing.T) {
var1, err := c.EvalVariable(api.EvalScope{-1, 0}, "a1")
assertNoError(err, t, "EvalVariable")
t.Logf("var1: <%s>", var1.Value)
t.Logf("var1: %s", var1.SinglelineString())
if var1.Value != "foofoofoofoofoofoo" {
t.Fatalf("Wrong variable value: %v", var1.Value)
t.Fatalf("Wrong variable value: %s", 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 {
......@@ -641,10 +637,12 @@ func TestClientServer_SetVariable(t *testing.T) {
a2, err := c.EvalVariable(api.EvalScope{-1, 0}, "a2")
t.Logf("a2: <%s>", a2.Value)
t.Logf("a2: %v", a2)
if a2.Value != "8" {
t.Fatalf("Wrong variable value: %v", a2.Value)
n, err := strconv.Atoi(a2.Value)
if err != nil && n != 8 {
t.Fatalf("Wrong variable value: %v", a2)
}
})
}
......@@ -676,9 +674,11 @@ func TestClientServer_FullStacktrace(t *testing.T) {
if arg.Name != "i" {
continue
}
n, err := strconv.Atoi(arg.Value)
assertNoError(err, t, fmt.Sprintf("Wrong value for i in goroutine %d (%s)", g.ID, arg.Value))
found[n] = true
t.Logf("frame %d, variable i is %v\n", arg)
argn, err := strconv.Atoi(arg.Value)
if err == nil {
found[argn] = true
}
}
}
}
......@@ -707,10 +707,9 @@ func TestClientServer_FullStacktrace(t *testing.T) {
if v == nil {
t.Fatalf("Could not find value of variable n in frame %d", i)
}
n, err := strconv.Atoi(v.Value)
assertNoError(err, t, fmt.Sprintf("Wrong value for n: %s", v.Value))
if n != cur {
t.Fatalf("Expected value %d got %d", cur, n)
vn, err := strconv.Atoi(v.Value)
if err != nil || vn != cur {
t.Fatalf("Expected value %d got %d (error: %v)", cur, vn, err)
}
cur--
if cur < 0 {
......
package proc
package servicetest
import (
"fmt"
"sort"
"strconv"
"strings"
"testing"
"github.com/derekparker/delve/proc"
"github.com/derekparker/delve/service/api"
protest "github.com/derekparker/delve/proc/test"
)
......@@ -17,21 +20,34 @@ type varTest struct {
err error
}
func assertVariable(t *testing.T, variable *Variable, expected varTest) {
func matchStringOrPrefix(output, target string) bool {
if strings.HasSuffix(target, "…") {
prefix := target[:len(target)-len("…")]
b := strings.HasPrefix(output, prefix)
return b
} else {
return output == target
}
}
func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
if variable.Name != expected.name {
t.Fatalf("Expected %s got %s\n", expected.name, variable.Name)
}
if variable.Type != expected.varType {
t.Fatalf("Expected %s got %s (for variable %s)\n", expected.varType, variable.Type, expected.name)
cv := api.ConvertVar(variable)
if cv.Type != expected.varType {
t.Fatalf("Expected %s got %s (for variable %s)\n", expected.varType, cv.Type, expected.name)
}
if variable.Value != expected.value {
t.Fatalf("Expected %#v got %#v (for variable %s)\n", expected.value, variable.Value, expected.name)
if ss := cv.SinglelineString(); !matchStringOrPrefix(ss, expected.value) {
t.Fatalf("Expected %#v got %#v (for variable %s)\n", expected.value, ss, expected.name)
}
}
func evalVariable(p *Process, symbol string) (*Variable, error) {
func evalVariable(p *proc.Process, symbol string) (*proc.Variable, error) {
scope, err := p.CurrentThread.Scope()
if err != nil {
return nil, err
......@@ -49,7 +65,7 @@ func (tc *varTest) afterSet() varTest {
return r
}
func setVariable(p *Process, symbol, value string) error {
func setVariable(p *proc.Process, symbol, value string) error {
scope, err := p.CurrentThread.Scope()
if err != nil {
return err
......@@ -59,32 +75,46 @@ func setVariable(p *Process, symbol, value string) error {
const varTestBreakpointLineNumber = 59
func withTestProcess(name string, t *testing.T, fn func(p *proc.Process, fixture protest.Fixture)) {
fixture := protest.BuildFixture(name)
p, err := proc.Launch([]string{fixture.Path})
if err != nil {
t.Fatal("Launch():", err)
}
defer func() {
p.Halt()
p.Kill()
}()
fn(p, fixture)
}
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},
{"a1", "\"foofoofoofoofoofoo\"", "", "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},
{"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},
{"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},
{"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
{"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},
......@@ -96,8 +126,8 @@ func TestVariableEvaluation(t *testing.T) {
{"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},
{"ms.Nest.Nest", "*main.Nest {Level: 2, Nest: *main.Nest {Level: 3, Nest: *main.Nest {...}}}", "", "*main.Nest", nil},
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)(…", "", "main.Nest", nil},
{"ms.Nest.Nest", "*main.Nest {Level: 2, Nest: *main.Nest {Level: 3, Nest: *(*main.Nest)(…", "", "*main.Nest", nil},
{"ms.Nest.Nest.Nest.Nest.Nest", "*main.Nest nil", "", "*main.Nest", nil},
{"ms.Nest.Nest.Nest.Nest.Nest.Nest", "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
{"main.p1", "10", "12", "int", nil},
......@@ -105,13 +135,8 @@ func TestVariableEvaluation(t *testing.T) {
{"NonExistent", "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
}
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()
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
err := p.Continue()
assertNoError(err, t, "Continue() returned an error")
for _, tc := range testcases {
......@@ -120,6 +145,9 @@ func TestVariableEvaluation(t *testing.T) {
assertNoError(err, t, "EvalVariable() returned an error")
assertVariable(t, variable, tc)
} else {
if err == nil {
t.Fatalf("Expected error %s, got no error: %s\n", tc.err.Error(), api.ConvertVar(variable).SinglelineString())
}
if tc.err.Error() != err.Error() {
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
}
......@@ -140,43 +168,55 @@ func TestVariableEvaluation(t *testing.T) {
})
}
func TestVariableFunctionScoping(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")
p.ClearBreakpoint(pc)
_, err = evalVariable(p, "a1")
assertNoError(err, t, "Unable to find variable a1")
_, err = evalVariable(p, "a2")
assertNoError(err, t, "Unable to find variable a1")
// Move scopes, a1 exists here by a2 does not
pc, _, _ = p.goSymTable.LineToPC(fixture.Source, 23)
_, err = p.SetBreakpoint(pc)
assertNoError(err, t, "SetBreakpoint() returned an error")
func TestMultilineVariableEvaluation(t *testing.T) {
testcases := []varTest{
{"a1", "\"foofoofoofoofoofoo\"", "", "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},
{"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},
{"a8", "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
{"i32", "[2]int32 [1,2]", "", "[2]int32", 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},
}
err = p.Continue()
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
err := p.Continue()
assertNoError(err, t, "Continue() returned an error")
_, err = evalVariable(p, "a1")
assertNoError(err, t, "Unable to find variable a1")
_, err = evalVariable(p, "a2")
if err == nil {
t.Fatalf("Can eval out of scope variable a2")
for _, tc := range testcases {
variable, err := evalVariable(p, tc.name)
assertNoError(err, t, "EvalVariable() returned an error")
if ms := api.ConvertVar(variable).MultilineString(""); !matchStringOrPrefix(ms, tc.value) {
t.Fatalf("Expected %s got %s (variable %s)\n", tc.value, ms, variable.Name)
}
}
})
}
type varArray []*Variable
type varArray []*proc.Variable
// Len is part of sort.Interface.
func (s varArray) Len() int {
......@@ -195,23 +235,23 @@ func (s varArray) Less(i, j int) bool {
func TestLocalVariables(t *testing.T) {
testcases := []struct {
fn func(*EvalScope) ([]*Variable, error)
fn func(*proc.EvalScope) ([]*proc.Variable, error)
output []varTest
}{
{(*EvalScope).LocalVariables,
{(*proc.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},
{"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},
{"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},
......@@ -222,26 +262,21 @@ func TestLocalVariables(t *testing.T) {
{"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},
{"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,
{(*proc.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) {
pc, _, _ := p.goSymTable.LineToPC(fixture.Source, varTestBreakpointLineNumber)
_, err := p.SetBreakpoint(pc)
assertNoError(err, t, "SetBreakpoint() returned an error")
err = p.Continue()
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
err := p.Continue()
assertNoError(err, t, "Continue() returned an error")
for _, tc := range testcases {
......@@ -263,97 +298,43 @@ func TestLocalVariables(t *testing.T) {
})
}
func TestRecursiveStructure(t *testing.T) {
withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue()")
v, err := evalVariable(p, "aas")
assertNoError(err, t, "EvalVariable()")
t.Logf("v: %v\n", v)
})
}
func TestFrameEvaluation(t *testing.T) {
withTestProcess("goroutinestackprog", t, func(p *Process, fixture protest.Fixture) {
_, err := setFunctionBreakpoint(p, "main.stacktraceme")
assertNoError(err, t, "setFunctionBreakpoint")
func TestEmbeddedStruct(t *testing.T) {
withTestProcess("testvariables4", t, func(p *proc.Process, fixture protest.Fixture) {
testcases := []varTest{
{"b.val", "-314", "", "int", nil},
{"b.A.val", "-314", "", "int", nil},
{"b.a.val", "42", "", "int", nil},
{"b.ptr.val", "1337", "", "int", nil},
{"b.C.s", "\"hello\"", "", "struct string", nil},
{"b.s", "\"hello\"", "", "struct string", nil},
}
assertNoError(p.Continue(), t, "Continue()")
/**** Testing evaluation on goroutines ****/
gs, err := p.GoroutinesInfo()
assertNoError(err, t, "GoroutinesInfo")
found := make([]bool, 10)
for _, g := range gs {
frame := -1
frames, err := p.GoroutineStacktrace(g, 10)
assertNoError(err, t, "GoroutineStacktrace()")
for i := range frames {
if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" {
frame = i
break
for _, tc := range testcases {
variable, err := evalVariable(p, tc.name)
if tc.err == nil {
assertNoError(err, t, "EvalVariable() returned an error")
assertVariable(t, variable, tc)
} else {
if tc.err.Error() != err.Error() {
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
}
}
if frame < 0 {
t.Logf("Goroutine %d: could not find correct frame", g.Id)
continue
}
scope, err := p.ConvertEvalScope(g.Id, frame)
assertNoError(err, t, "ConvertEvalScope()")
t.Logf("scope = %v", scope)
v, err := scope.EvalVariable("i")
t.Logf("v = %v", v)
if err != nil {
t.Logf("Goroutine %d: %v\n", g.Id, err)
continue
}
i, err := strconv.Atoi(v.Value)
assertNoError(err, t, fmt.Sprintf("strconv.Atoi(%s)", v.Value))
found[i] = true
}
for i := range found {
if !found[i] {
t.Fatalf("Goroutine %d not found\n", i)
}
}
/**** Testing evaluation on frames ****/
assertNoError(p.Continue(), t, "Continue() 2")
g, err := p.CurrentThread.GetG()
assertNoError(err, t, "GetG()")
for i := 0; i <= 3; i++ {
scope, err := p.ConvertEvalScope(g.Id, i+1)
assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1))
v, err := scope.EvalVariable("n")
assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1))
n, err := strconv.Atoi(v.Value)
assertNoError(err, t, fmt.Sprintf("strconv.Atoi(%s) on frame %d", v.Value, i+1))
t.Logf("frame %d n %d\n", i+1, n)
if n != 3-i {
t.Fatalf("On frame %d value of n is %d (not %d)", i+1, n, 3-i)
}
}
})
}
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()
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
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)
if s := api.ConvertVar(variable).SinglelineString(); s != value {
t.Fatalf("Wrong value of c128: \"%s\", expected \"%s\" after setting it to \"%s\"", s, value, setExpr)
}
}
......@@ -363,57 +344,3 @@ func TestComplexSetting(t *testing.T) {
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")
})
}
func TestEmbeddedStruct(t *testing.T) {
withTestProcess("testvariables4", t, func(p *Process, fixture protest.Fixture) {
testcases := []varTest{
{"b.val", "-314", "", "int", nil},
{"b.A.val", "-314", "", "int", nil},
{"b.a.val", "42", "", "int", nil},
{"b.ptr.val", "1337", "", "int", nil},
{"b.C.s", "hello", "", "struct string", nil},
{"b.s", "hello", "", "struct string", nil},
}
assertNoError(p.Continue(), t, "Continue()")
for _, tc := range testcases {
variable, err := evalVariable(p, tc.name)
if tc.err == nil {
assertNoError(err, t, "EvalVariable() returned an error")
assertVariable(t, variable, tc)
} else {
if tc.err.Error() != err.Error() {
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
}
}
}
})
}
......@@ -629,7 +629,8 @@ func printVar(t *Term, scope api.EvalScope, args ...string) error {
if err != nil {
return err
}
fmt.Println(val.Value)
fmt.Println(val.MultilineString(""))
return nil
}
......@@ -650,7 +651,7 @@ func filterVariables(vars []api.Variable, filter string) []string {
data := make([]string, 0, len(vars))
for _, v := range vars {
if reg == nil || reg.Match([]byte(v.Name)) {
data = append(data, fmt.Sprintf("%s = %s", v.Name, v.Value))
data = append(data, fmt.Sprintf("%s = %s", v.Name, v.SinglelineString()))
}
}
return data
......@@ -801,10 +802,10 @@ func printStack(stack []api.Stackframe, ind string) {
fmt.Printf("%sat %s:%d\n", s, shortenFilePath(stack[i].File), stack[i].Line)
for j := range stack[i].Arguments {
fmt.Printf("%s %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].Value)
fmt.Printf("%s %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].SinglelineString())
}
for j := range stack[i].Locals {
fmt.Printf("%s %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].Value)
fmt.Printf("%s %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].SinglelineString())
}
}
}
......@@ -829,7 +830,7 @@ func printcontext(t *Term, state *api.DebuggerState) error {
if state.Breakpoint.Tracepoint {
var arg []string
for _, ar := range state.CurrentThread.Function.Args {
arg = append(arg, ar.Value)
arg = append(arg, ar.SinglelineString())
}
args = strings.Join(arg, ", ")
}
......@@ -864,7 +865,7 @@ func printcontext(t *Term, state *api.DebuggerState) error {
ss := make([]string, len(bpi.Variables))
for i, v := range bpi.Variables {
ss[i] = fmt.Sprintf("%s: <%v>", v.Name, v.Value)
ss[i] = fmt.Sprintf("%s: %v", v.Name, v.MultilineString(""))
}
fmt.Printf("\t%s\n", strings.Join(ss, ", "))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册