variables_test.go 63.8 KB
Newer Older
1
package service_test
2 3

import (
4
	"errors"
5
	"fmt"
6
	"go/constant"
7
	"path/filepath"
8
	"runtime"
9 10 11 12
	"sort"
	"strings"
	"testing"

13 14 15 16 17
	"github.com/go-delve/delve/pkg/goversion"
	"github.com/go-delve/delve/pkg/proc"
	"github.com/go-delve/delve/pkg/proc/gdbserial"
	"github.com/go-delve/delve/pkg/proc/native"
	"github.com/go-delve/delve/service/api"
18

19
	protest "github.com/go-delve/delve/pkg/proc/test"
20 21
)

22 23
var pnormalLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1, 0}
var pshortLoadConfig = proc.LoadConfig{false, 0, 64, 0, 3, 0}
24

25
type varTest struct {
A
aarzilli 已提交
26 27 28
	name         string
	preserveName bool
	value        string
29
	alternate    string
A
aarzilli 已提交
30 31
	varType      string
	err          error
32 33 34 35 36 37 38 39 40 41 42 43 44
}

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) {
A
aarzilli 已提交
45 46 47 48
	if expected.preserveName {
		if variable.Name != expected.name {
			t.Fatalf("Expected %s got %s\n", expected.name, variable.Name)
		}
49 50 51 52 53 54 55 56 57 58 59 60 61
	}

	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 ss := cv.SinglelineString(); !matchStringOrPrefix(ss, expected.value) {
		t.Fatalf("Expected %#v got %#v (for variable %s)\n", expected.value, ss, expected.name)
	}
}

62 63
func findFirstNonRuntimeFrame(p proc.Process) (proc.Stackframe, error) {
	frames, err := proc.ThreadStacktrace(p.CurrentThread(), 10)
64
	if err != nil {
65 66 67 68 69 70 71
		return proc.Stackframe{}, err
	}

	for _, frame := range frames {
		if frame.Current.Fn != nil && !strings.HasPrefix(frame.Current.Fn.Name, "runtime.") {
			return frame, nil
		}
72
	}
73 74 75
	return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found")
}

76 77 78
func evalScope(p proc.Process) (*proc.EvalScope, error) {
	if testBackend != "rr" {
		return proc.GoroutineScope(p.CurrentThread())
79
	}
80 81 82 83 84 85
	frame, err := findFirstNonRuntimeFrame(p)
	if err != nil {
		return nil, err
	}
	return proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame), nil
}
86

87 88
func evalVariable(p proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
	scope, err := evalScope(p)
89 90 91 92
	if err != nil {
		return nil, err
	}

93
	return scope.EvalVariable(symbol, cfg)
94 95
}

96
func (tc *varTest) alternateVarTest() varTest {
97
	r := *tc
98
	r.value = r.alternate
99 100 101
	return r
}

102
func setVariable(p proc.Process, symbol, value string) error {
103
	scope, err := proc.GoroutineScope(p.CurrentThread())
104 105 106 107 108 109
	if err != nil {
		return err
	}
	return scope.SetVariable(symbol, value)
}

110
func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture protest.Fixture)) {
111 112 113 114
	withTestProcessArgs(name, t, ".", []string{}, 0, fn)
}

func withTestProcessArgs(name string, t *testing.T, wd string, args []string, buildFlags protest.BuildFlags, fn func(p proc.Process, fixture protest.Fixture)) {
115
	if buildMode == "pie" {
116
		buildFlags |= protest.BuildModePIE
117 118
	}
	fixture := protest.BuildFixture(name, buildFlags)
119
	var p proc.Process
120
	var err error
121
	var tracedir string
122 123
	switch testBackend {
	case "native":
124
		p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{})
125
	case "lldb":
126
		p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{})
127 128 129
	case "rr":
		protest.MustHaveRecordingAllowed(t)
		t.Log("recording")
130
		p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{})
131
		t.Logf("replaying %q", tracedir)
132 133 134
	default:
		t.Fatalf("unknown backend %q", testBackend)
	}
135 136 137 138 139
	if err != nil {
		t.Fatal("Launch():", err)
	}

	defer func() {
140
		p.Detach(true)
141 142 143
		if tracedir != "" {
			protest.SafeRemoveAll(tracedir)
		}
144 145 146 147 148 149 150
	}()

	fn(p, fixture)
}

func TestVariableEvaluation(t *testing.T) {
	testcases := []varTest{
151
		{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
A
aarzilli 已提交
152
		{"a11", true, "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
153 154
		{"a12", true, "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "[]main.FooBar", nil},
		{"a13", true, "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "[]*main.FooBar", nil},
A
aarzilli 已提交
155 156 157
		{"a2", true, "6", "10", "int", nil},
		{"a3", true, "7.23", "3.1", "float64", nil},
		{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
158
		{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "[]int", nil},
A
aarzilli 已提交
159 160 161 162
		{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
		{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
		{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
163
		{"baz", true, "\"bazburzum\"", "", "string", nil},
A
aarzilli 已提交
164 165 166 167 168 169
		{"neg", true, "-1", "-20", "int", nil},
		{"f32", true, "1.2", "1.1", "float32", nil},
		{"c64", true, "(1 + 2i)", "(4 + 5i)", "complex64", nil},
		{"c128", true, "(2 + 3i)", "(6.3 + 7i)", "complex128", nil},
		{"a6.Baz", true, "8", "20", "int", nil},
		{"a7.Baz", true, "5", "25", "int", nil},
170
		{"a8.Baz", true, "\"feh\"", "", "string", nil},
A
aarzilli 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183
		{"a9.Baz", true, "nil", "", "int", fmt.Errorf("a9 is nil")},
		{"a9.NonExistent", true, "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
		{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
		{"b1", true, "true", "false", "bool", nil},
		{"b2", true, "false", "true", "bool", nil},
		{"i8", true, "1", "2", "int8", nil},
		{"u16", true, "65535", "0", "uint16", nil},
		{"u32", true, "4294967295", "1", "uint32", nil},
		{"u64", true, "18446744073709551615", "2", "uint64", nil},
		{"u8", true, "255", "3", "uint8", nil},
		{"up", true, "5", "4", "uintptr", nil},
		{"f", true, "main.barfoo", "", "func()", nil},
184
		{"ba", true, "[]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]", "", "[]int", nil},
A
aarzilli 已提交
185 186 187 188 189 190 191
		{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)(…", "", "main.Nest", nil},
		{"ms.Nest.Nest", true, "*main.Nest {Level: 2, Nest: *main.Nest {Level: 3, Nest: *(*main.Nest)(…", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest", true, "*main.Nest nil", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest.Nest", true, "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
		{"main.p1", true, "10", "12", "int", nil},
		{"p1", true, "10", "13", "int", nil},
		{"NonExistent", true, "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
192 193
	}

194
	protest.AllowRecording(t)
195
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
196
		err := proc.Continue(p)
197 198 199
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
200
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
201 202 203 204 205 206 207 208 209 210 211 212
			if tc.err == nil {
				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())
				}
			}

213
			if tc.alternate != "" && testBackend != "rr" {
214 215
				assertNoError(setVariable(p, tc.name, tc.alternate), t, "SetVariable()")
				variable, err = evalVariable(p, tc.name, pnormalLoadConfig)
216
				assertNoError(err, t, "EvalVariable()")
217
				assertVariable(t, variable, tc.alternateVarTest())
218 219

				assertNoError(setVariable(p, tc.name, tc.value), t, "SetVariable()")
220
				variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
221 222 223 224 225 226 227
				assertNoError(err, t, "EvalVariable()")
				assertVariable(t, variable, tc)
			}
		}
	})
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
func TestSetVariable(t *testing.T) {
	var testcases = []struct {
		name     string
		typ      string // type of <name>
		startVal string // original value of <name>
		expr     string
		finalVal string // new value of <name> after executing <name> = <expr>
	}{
		{"b.ptr", "*main.A", "*main.A {val: 1337}", "nil", "*main.A nil"},
		{"m2", "map[int]*main.astruct", "map[int]*main.astruct [1: *{A: 10, B: 11}, ]", "nil", "map[int]*main.astruct nil"},
		{"fn1", "main.functype", "main.afunc", "nil", "nil"},
		{"ch1", "chan int", "chan int 4/10", "nil", "chan int nil"},
		{"s2", "[]main.astruct", "[]main.astruct len: 8, cap: 8, [{A: 1, B: 2},{A: 3, B: 4},{A: 5, B: 6},{A: 7, B: 8},{A: 9, B: 10},{A: 11, B: 12},{A: 13, B: 14},{A: 15, B: 16}]", "nil", "[]main.astruct len: 0, cap: 0, nil"},
		{"err1", "error", "error(*main.astruct) *{A: 1, B: 2}", "nil", "error nil"},
		{"s1[0]", "string", `"one"`, `""`, `""`},
		{"as1", "main.astruct", "main.astruct {A: 1, B: 1}", `m1["Malone"]`, "main.astruct {A: 2, B: 3}"},

		{"iface1", "interface {}", "interface {}(*main.astruct) *{A: 1, B: 2}", "nil", "interface {} nil"},
		{"iface1", "interface {}", "interface {} nil", "iface2", "interface {}(string) \"test\""},
		{"iface1", "interface {}", "interface {}(string) \"test\"", "parr", "interface {}(*[4]int) *[0,1,2,3]"},

		{"s3", "[]int", `[]int len: 0, cap: 6, []`, "s4[2:5]", "[]int len: 3, cap: 3, [3,4,5]"},
		{"s3", "[]int", "[]int len: 3, cap: 3, [3,4,5]", "arr1[:]", "[]int len: 4, cap: 4, [0,1,2,3]"},
	}

	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
		assertNoError(proc.Continue(p), t, "Continue()")

		for _, tc := range testcases {
			if tc.name == "iface1" && tc.expr == "parr" {
				if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
					// conversion pointer -> eface not supported prior to Go 1.11
					continue
				}
			}
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
			assertNoError(err, t, "EvalVariable()")
			assertVariable(t, variable, varTest{tc.name, true, tc.startVal, "", tc.typ, nil})

			assertNoError(setVariable(p, tc.name, tc.expr), t, "SetVariable()")

			variable, err = evalVariable(p, tc.name, pnormalLoadConfig)
			assertNoError(err, t, "EvalVariable()")
			assertVariable(t, variable, varTest{tc.name, true, tc.finalVal, "", tc.typ, nil})
		}
	})
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
func TestVariableEvaluationShort(t *testing.T) {
	testcases := []varTest{
		{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
		{"a11", true, "[3]main.FooBar [...]", "", "[3]main.FooBar", nil},
		{"a12", true, "[]main.FooBar len: 2, cap: 2, [...]", "", "[]main.FooBar", nil},
		{"a13", true, "[]*main.FooBar len: 3, cap: 3, [...]", "", "[]*main.FooBar", nil},
		{"a2", true, "6", "", "int", nil},
		{"a3", true, "7.23", "", "float64", nil},
		{"a4", true, "[2]int [...]", "", "[2]int", nil},
		{"a5", true, "[]int len: 5, cap: 5, [...]", "", "[]int", nil},
		{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
		{"a7", true, "(*main.FooBar)(0x…", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
		{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
		{"baz", true, "\"bazburzum\"", "", "string", nil},
		{"neg", true, "-1", "", "int", nil},
		{"f32", true, "1.2", "", "float32", nil},
		{"c64", true, "(1 + 2i)", "", "complex64", nil},
		{"c128", true, "(2 + 3i)", "", "complex128", nil},
		{"a6.Baz", true, "8", "", "int", nil},
		{"a7.Baz", true, "5", "", "int", nil},
		{"a8.Baz", true, "\"feh\"", "", "string", nil},
		{"a9.Baz", true, "nil", "", "int", fmt.Errorf("a9 is nil")},
		{"a9.NonExistent", true, "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
		{"i32", true, "[2]int32 [...]", "", "[2]int32", nil},
		{"b1", true, "true", "false", "bool", nil},
		{"b2", true, "false", "true", "bool", nil},
		{"i8", true, "1", "2", "int8", nil},
		{"u16", true, "65535", "0", "uint16", nil},
		{"u32", true, "4294967295", "1", "uint32", nil},
		{"u64", true, "18446744073709551615", "2", "uint64", nil},
		{"u8", true, "255", "3", "uint8", nil},
		{"up", true, "5", "4", "uintptr", nil},
		{"f", true, "main.barfoo", "", "func()", nil},
		{"ba", true, "[]int len: 200, cap: 200, [...]", "", "[]int", nil},
		{"ms", true, "main.Nest {Level: 0, Nest: (*main.Nest)(0x…", "", "main.Nest", nil},
		{"ms.Nest.Nest", true, "(*main.Nest)(0x…", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest", true, "*main.Nest nil", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest.Nest", true, "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
		{"main.p1", true, "10", "", "int", nil},
		{"p1", true, "10", "", "int", nil},
		{"NonExistent", true, "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
	}

321
	protest.AllowRecording(t)
322
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
323
		err := proc.Continue(p)
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
			variable, err := evalVariable(p, tc.name, pshortLoadConfig)
			if tc.err == nil {
				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())
				}
			}
		}
	})
}

343 344
func TestMultilineVariableEvaluation(t *testing.T) {
	testcases := []varTest{
345
		{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
A
aarzilli 已提交
346
		{"a11", true, `[3]main.FooBar [
347 348 349 350
	{Baz: 1, Bur: "a"},
	{Baz: 2, Bur: "b"},
	{Baz: 3, Bur: "c"},
]`, "", "[3]main.FooBar", nil},
A
aarzilli 已提交
351
		{"a12", true, `[]main.FooBar len: 2, cap: 2, [
352 353
	{Baz: 4, Bur: "d"},
	{Baz: 5, Bur: "e"},
354
]`, "", "[]main.FooBar", nil},
A
aarzilli 已提交
355
		{"a13", true, `[]*main.FooBar len: 3, cap: 3, [
356 357 358
	*{Baz: 6, Bur: "f"},
	*{Baz: 7, Bur: "g"},
	*{Baz: 8, Bur: "h"},
359
]`, "", "[]*main.FooBar", nil},
A
aarzilli 已提交
360 361
		{"a2", true, "6", "10", "int", nil},
		{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
362
		{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "[]int", nil},
A
aarzilli 已提交
363 364 365 366 367 368
		{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
		{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
		{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
		{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
369
		{"ba", true, "[]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]", "", "[]int", nil},
A
aarzilli 已提交
370
		{"ms", true, `main.Nest {
371 372 373 374 375 376
	Level: 0,
	Nest: *main.Nest {
		Level: 1,
		Nest: *(*main.Nest)(…`, "", "main.Nest", nil},
	}

377
	protest.AllowRecording(t)
378
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
379
		err := proc.Continue(p)
380 381 382
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
383
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
			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 []*proc.Variable

// Len is part of sort.Interface.
func (s varArray) Len() int {
	return len(s)
}

// Swap is part of sort.Interface.
func (s varArray) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s varArray) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

func TestLocalVariables(t *testing.T) {
	testcases := []struct {
411
		fn     func(*proc.EvalScope, proc.LoadConfig) ([]*proc.Variable, error)
412 413 414 415
		output []varTest
	}{
		{(*proc.EvalScope).LocalVariables,
			[]varTest{
416 417
				{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
				{"a10", true, "\"ofo\"", "", "string", nil},
A
aarzilli 已提交
418
				{"a11", true, "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
419 420
				{"a12", true, "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "[]main.FooBar", nil},
				{"a13", true, "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "[]*main.FooBar", nil},
A
aarzilli 已提交
421 422 423
				{"a2", true, "6", "", "int", nil},
				{"a3", true, "7.23", "", "float64", nil},
				{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
424
				{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "[]int", nil},
A
aarzilli 已提交
425 426 427 428 429 430
				{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
				{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
				{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
				{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
				{"b1", true, "true", "", "bool", nil},
				{"b2", true, "false", "", "bool", nil},
431
				{"ba", true, "[]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]", "", "[]int", nil},
A
aarzilli 已提交
432 433 434 435 436 437 438 439 440 441 442 443 444
				{"c128", true, "(2 + 3i)", "", "complex128", nil},
				{"c64", true, "(1 + 2i)", "", "complex64", nil},
				{"f", true, "main.barfoo", "", "func()", nil},
				{"f32", true, "1.2", "", "float32", nil},
				{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
				{"i8", true, "1", "", "int8", nil},
				{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)…", "", "main.Nest", nil},
				{"neg", true, "-1", "", "int", nil},
				{"u16", true, "65535", "", "uint16", nil},
				{"u32", true, "4294967295", "", "uint32", nil},
				{"u64", true, "18446744073709551615", "", "uint64", nil},
				{"u8", true, "255", "", "uint8", nil},
				{"up", true, "5", "", "uintptr", nil}}},
445 446
		{(*proc.EvalScope).FunctionArguments,
			[]varTest{
A
aarzilli 已提交
447
				{"bar", true, "main.FooBar {Baz: 10, Bur: \"lorem\"}", "", "main.FooBar", nil},
448
				{"baz", true, "\"bazburzum\"", "", "string", nil}}},
449 450
	}

451
	protest.AllowRecording(t)
452
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
453
		err := proc.Continue(p)
454 455 456
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
457 458 459 460 461 462 463
			var scope *proc.EvalScope
			var err error

			if testBackend == "rr" {
				var frame proc.Stackframe
				frame, err = findFirstNonRuntimeFrame(p)
				if err == nil {
464
					scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame)
465 466 467 468 469 470
				}
			} else {
				scope, err = proc.GoroutineScope(p.CurrentThread())
			}

			assertNoError(err, t, "scope")
471
			vars, err := tc.fn(scope, pnormalLoadConfig)
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
			assertNoError(err, t, "LocalVariables() returned an error")

			sort.Sort(varArray(vars))

			if len(tc.output) != len(vars) {
				t.Fatalf("Invalid variable count. Expected %d got %d.", len(tc.output), len(vars))
			}

			for i, variable := range vars {
				assertVariable(t, variable, tc.output[i])
			}
		}
	})
}

func TestEmbeddedStruct(t *testing.T) {
488
	protest.AllowRecording(t)
489
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
490
		testcases := []varTest{
491 492 493 494 495 496
			{"b.val", true, "-314", "-314", "int", nil},
			{"b.A.val", true, "-314", "-314", "int", nil},
			{"b.a.val", true, "42", "42", "int", nil},
			{"b.ptr.val", true, "1337", "1337", "int", nil},
			{"b.C.s", true, "\"hello\"", "\"hello\"", "string", nil},
			{"b.s", true, "\"hello\"", "\"hello\"", "string", nil},
497
			{"b2", true, "main.B {A: main.A {val: 42}, C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {A: (*main.A)(0x…", "main.B", nil},
498
		}
499
		assertNoError(proc.Continue(p), t, "Continue()")
500

501 502
		ver, _ := goversion.Parse(runtime.Version())
		if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
503 504 505 506 507 508 509 510 511
			// on go < 1.9 embedded fields had different names
			for i := range testcases {
				if testcases[i].name == "b2" {
					testcases[i].value = "main.B {main.A: main.A {val: 42}, *main.C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}"
					testcases[i].alternate = "main.B {main.A: (*main.A)(0x…"
				}
			}
		}

512
		for _, tc := range testcases {
513
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
514
			if tc.err == nil {
515
				assertNoError(err, t, fmt.Sprintf("EvalVariable(%s) returned an error", tc.name))
516
				assertVariable(t, variable, tc)
517 518 519
				variable, err = evalVariable(p, tc.name, pshortLoadConfig)
				assertNoError(err, t, fmt.Sprintf("EvalVariable(%s, pshortLoadConfig) returned an error", tc.name))
				assertVariable(t, variable, tc.alternateVarTest())
520 521 522 523 524 525 526 527 528 529
			} else {
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
		}
	})
}

func TestComplexSetting(t *testing.T) {
530
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
531
		err := proc.Continue(p)
532 533 534 535
		assertNoError(err, t, "Continue() returned an error")

		h := func(setExpr, value string) {
			assertNoError(setVariable(p, "c128", setExpr), t, "SetVariable()")
536
			variable, err := evalVariable(p, "c128", pnormalLoadConfig)
537 538 539 540 541 542 543 544 545
			assertNoError(err, t, "EvalVariable()")
			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)
			}
		}

		h("3.2i", "(0 + 3.2i)")
		h("1.1", "(1.1 + 0i)")
		h("1 + 3.3i", "(1 + 3.3i)")
546
		h("complex(1.2, 3.4)", "(1.2 + 3.4i)")
547 548
	})
}
A
aarzilli 已提交
549 550 551 552

func TestEvalExpression(t *testing.T) {
	testcases := []varTest{
		// slice/array/string subscript
553 554 555 556 557
		{"s1[0]", false, "\"one\"", "\"one\"", "string", nil},
		{"s1[1]", false, "\"two\"", "\"two\"", "string", nil},
		{"s1[2]", false, "\"three\"", "\"three\"", "string", nil},
		{"s1[3]", false, "\"four\"", "\"four\"", "string", nil},
		{"s1[4]", false, "\"five\"", "\"five\"", "string", nil},
558
		{"s1[5]", false, "", "", "string", fmt.Errorf("index out of bounds")},
559 560 561 562 563
		{"a1[0]", false, "\"one\"", "\"one\"", "string", nil},
		{"a1[1]", false, "\"two\"", "\"two\"", "string", nil},
		{"a1[2]", false, "\"three\"", "\"three\"", "string", nil},
		{"a1[3]", false, "\"four\"", "\"four\"", "string", nil},
		{"a1[4]", false, "\"five\"", "\"five\"", "string", nil},
564
		{"a1[5]", false, "", "", "string", fmt.Errorf("index out of bounds")},
565 566 567 568
		{"str1[0]", false, "48", "48", "byte", nil},
		{"str1[1]", false, "49", "49", "byte", nil},
		{"str1[2]", false, "50", "50", "byte", nil},
		{"str1[10]", false, "48", "48", "byte", nil},
A
aarzilli 已提交
569 570 571
		{"str1[11]", false, "", "", "byte", fmt.Errorf("index out of bounds")},

		// slice/array/string reslicing
572 573 574 575 576 577
		{"a1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "[]string len: 2, cap: 2, [...]", "[]string", nil},
		{"s1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "[]string len: 2, cap: 2, [...]", "[]string", nil},
		{"str1[2:4]", false, "\"23\"", "\"23\"", "string", nil},
		{"str1[0:11]", false, "\"01234567890\"", "\"01234567890\"", "string", nil},
		{"str1[:3]", false, "\"012\"", "\"012\"", "string", nil},
		{"str1[3:]", false, "\"34567890\"", "\"34567890\"", "string", nil},
578 579
		{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
		{"str1[5:3]", false, "", "", "string", fmt.Errorf("index out of bounds")},
A
aarzilli 已提交
580

581 582 583 584 585
		// NaN and Inf floats
		{"pinf", false, "+Inf", "+Inf", "float64", nil},
		{"ninf", false, "-Inf", "-Inf", "float64", nil},
		{"nan", false, "NaN", "NaN", "float64", nil},

A
aarzilli 已提交
586
		// pointers
587 588 589
		{"*p2", false, "5", "5", "int", nil},
		{"p2", true, "*5", "(*int)(0x…", "*int", nil},
		{"p3", true, "*int nil", "*int nil", "*int", nil},
A
aarzilli 已提交
590 591
		{"*p3", false, "", "", "int", fmt.Errorf("nil pointer dereference")},

A
aarzilli 已提交
592
		// channels
A
aarzilli 已提交
593
		{"ch1", true, "chan int 4/10", "chan int 4/10", "chan int", nil},
594
		{"chnil", true, "chan int nil", "chan int nil", "chan int", nil},
A
aarzilli 已提交
595 596
		{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},

A
aarzilli 已提交
597
		// maps
A
Alessandro Arzilli 已提交
598
		{"m1[\"Malone\"]", false, "main.astruct {A: 2, B: 3}", "main.astruct {A: 2, B: 3}", "main.astruct", nil},
599 600 601 602
		{"m2[1].B", false, "11", "11", "int", nil},
		{"m2[c1.sa[2].B-4].A", false, "10", "10", "int", nil},
		{"m2[*p1].B", false, "11", "11", "int", nil},
		{"m3[as1]", false, "42", "42", "int", nil},
A
aarzilli 已提交
603
		{"mnil[\"Malone\"]", false, "", "", "", fmt.Errorf("key not found")},
604
		{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
A
aarzilli 已提交
605

606
		// interfaces
A
Alessandro Arzilli 已提交
607 608
		{"err1", true, "error(*main.astruct) *{A: 1, B: 2}", "error(*main.astruct) 0x…", "error", nil},
		{"err2", true, "error(*main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*main.bstruct) 0x…", "error", nil},
609
		{"errnil", true, "error nil", "error nil", "error", nil},
A
Alessandro Arzilli 已提交
610
		{"iface1", true, "interface {}(*main.astruct) *{A: 1, B: 2}", "interface {}(*main.astruct) 0x…", "interface {}", nil},
611 612
		{"iface1.A", false, "1", "1", "int", nil},
		{"iface1.B", false, "2", "2", "int", nil},
613
		{"iface2", true, "interface {}(string) \"test\"", "interface {}(string) \"test\"", "interface {}", nil},
614
		{"iface3", true, "interface {}(map[string]go/constant.Value) []", "interface {}(map[string]go/constant.Value) []", "interface {}", nil},
615
		{"iface4", true, "interface {}([]go/constant.Value) [4]", "interface {}([]go/constant.Value) [...]", "interface {}", nil},
616 617
		{"ifacenil", true, "interface {} nil", "interface {} nil", "interface {}", nil},
		{"err1 == err2", false, "false", "false", "", nil},
618
		{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
619 620 621
		{"errnil == nil", false, "true", "true", "", nil},
		{"errtypednil == nil", false, "false", "false", "", nil},
		{"nil == errnil", false, "true", "true", "", nil},
A
Alessandro Arzilli 已提交
622 623
		{"err1.(*main.astruct)", false, "*main.astruct {A: 1, B: 2}", "(*main.astruct)(0x…", "*main.astruct", nil},
		{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *main.astruct, not *main.bstruct")},
624
		{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
625
		{"const1", true, "go/constant.Value(go/constant.int64Val) 3", "go/constant.Value(go/constant.int64Val) 3", "go/constant.Value", nil},
626

A
aarzilli 已提交
627
		// combined expressions
628 629 630 631 632
		{"c1.pb.a.A", true, "1", "1", "int", nil},
		{"c1.sa[1].B", false, "3", "3", "int", nil},
		{"s2[5].B", false, "12", "12", "int", nil},
		{"s2[c1.sa[2].B].A", false, "11", "11", "int", nil},
		{"s2[*p2].B", false, "12", "12", "int", nil},
A
aarzilli 已提交
633 634

		// constants
635 636 637 638 639
		{"1.1", false, "1.1", "1.1", "", nil},
		{"10", false, "10", "10", "", nil},
		{"1 + 2i", false, "(1 + 2i)", "(1 + 2i)", "", nil},
		{"true", false, "true", "true", "", nil},
		{"\"test\"", false, "\"test\"", "\"test\"", "", nil},
A
aarzilli 已提交
640 641

		// binary operators
642 643 644 645 646 647 648
		{"i2 + i3", false, "5", "5", "int", nil},
		{"i2 - i3", false, "-1", "-1", "int", nil},
		{"i3 - i2", false, "1", "1", "int", nil},
		{"i2 * i3", false, "6", "6", "int", nil},
		{"i2/i3", false, "0", "0", "int", nil},
		{"f1/2.0", false, "1.5", "1.5", "float64", nil},
		{"i2 << 2", false, "8", "8", "int", nil},
A
aarzilli 已提交
649 650

		// unary operators
651 652 653
		{"-i2", false, "-2", "-2", "int", nil},
		{"+i2", false, "2", "2", "int", nil},
		{"^i2", false, "-3", "-3", "int", nil},
A
aarzilli 已提交
654 655

		// comparison operators
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
		{"i2 == i3", false, "false", "false", "", nil},
		{"i2 == 2", false, "true", "true", "", nil},
		{"i2 == 2", false, "true", "true", "", nil},
		{"i2 == 3", false, "false", "false", "", nil},
		{"i2 != i3", false, "true", "true", "", nil},
		{"i2 < i3", false, "true", "true", "", nil},
		{"i2 <= i3", false, "true", "true", "", nil},
		{"i2 > i3", false, "false", "false", "", nil},
		{"i2 >= i3", false, "false", "false", "", nil},
		{"i2 >= 2", false, "true", "true", "", nil},
		{"str1 == \"01234567890\"", false, "true", "true", "", nil},
		{"str1 < \"01234567890\"", false, "false", "false", "", nil},
		{"str1 < \"11234567890\"", false, "true", "true", "", nil},
		{"str1 > \"00234567890\"", false, "true", "true", "", nil},
		{"str1 == str1", false, "true", "true", "", nil},
		{"c1.pb.a == *(c1.sa[0])", false, "true", "true", "", nil},
		{"c1.pb.a != *(c1.sa[0])", false, "false", "false", "", nil},
		{"c1.pb.a == *(c1.sa[1])", false, "false", "false", "", nil},
		{"c1.pb.a != *(c1.sa[1])", false, "true", "true", "", nil},
675
		{`longstr == "not this"`, false, "false", "false", "", nil},
A
aarzilli 已提交
676

677
		// builtins
678 679
		{"cap(parr)", false, "4", "4", "", nil},
		{"len(parr)", false, "4", "4", "", nil},
680 681
		{"cap(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for cap")},
		{"len(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for len")},
682 683 684 685 686 687
		{"cap(a1)", false, "5", "5", "", nil},
		{"len(a1)", false, "5", "5", "", nil},
		{"cap(s3)", false, "6", "6", "", nil},
		{"len(s3)", false, "0", "0", "", nil},
		{"cap(nilslice)", false, "0", "0", "", nil},
		{"len(nilslice)", false, "0", "0", "", nil},
A
aarzilli 已提交
688 689
		{"cap(ch1)", false, "10", "10", "", nil},
		{"len(ch1)", false, "4", "4", "", nil},
690 691 692 693 694 695 696 697
		{"cap(chnil)", false, "0", "0", "", nil},
		{"len(chnil)", false, "0", "0", "", nil},
		{"len(m1)", false, "41", "41", "", nil},
		{"len(mnil)", false, "0", "0", "", nil},
		{"imag(cpx1)", false, "2", "2", "", nil},
		{"real(cpx1)", false, "1", "1", "", nil},
		{"imag(3i)", false, "3", "3", "", nil},
		{"real(4)", false, "4", "4", "", nil},
698

A
aarzilli 已提交
699
		// nil
700
		{"nil", false, "nil", "nil", "", nil},
A
aarzilli 已提交
701
		{"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")},
702 703
		{"fn1", false, "main.afunc", "main.afunc", "main.functype", nil},
		{"fn2", false, "nil", "nil", "main.functype", nil},
704
		{"nilslice", false, "[]int len: 0, cap: 0, nil", "[]int len: 0, cap: 0, nil", "[]int", nil},
A
aarzilli 已提交
705
		{"fn1 == fn2", false, "", "", "", fmt.Errorf("can not compare func variables")},
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
		{"fn1 == nil", false, "false", "false", "", nil},
		{"fn1 != nil", false, "true", "true", "", nil},
		{"fn2 == nil", false, "true", "true", "", nil},
		{"fn2 != nil", false, "false", "false", "", nil},
		{"c1.sa == nil", false, "false", "false", "", nil},
		{"c1.sa != nil", false, "true", "true", "", nil},
		{"c1.sa[0] == nil", false, "false", "false", "", nil},
		{"c1.sa[0] != nil", false, "true", "true", "", nil},
		{"nilslice == nil", false, "true", "true", "", nil},
		{"nil == nilslice", false, "true", "true", "", nil},
		{"nilslice != nil", false, "false", "false", "", nil},
		{"nilptr == nil", false, "true", "true", "", nil},
		{"nilptr != nil", false, "false", "false", "", nil},
		{"p1 == nil", false, "false", "false", "", nil},
		{"p1 != nil", false, "true", "true", "", nil},
		{"ch1 == nil", false, "false", "false", "", nil},
		{"chnil == nil", false, "true", "true", "", nil},
A
aarzilli 已提交
723
		{"ch1 == chnil", false, "", "", "", fmt.Errorf("can not compare chan variables")},
724
		{"m1 == nil", false, "false", "false", "", nil},
A
aarzilli 已提交
725
		{"mnil == m1", false, "", "", "", fmt.Errorf("can not compare map variables")},
726
		{"mnil == nil", false, "true", "true", "", nil},
727 728
		{"nil == 2", false, "", "", "", fmt.Errorf("can not compare int to nil")},
		{"2 == nil", false, "", "", "", fmt.Errorf("can not compare int to nil")},
A
aarzilli 已提交
729 730 731

		// errors
		{"&3", false, "", "", "", fmt.Errorf("can not take address of \"3\"")},
732
		{"*3", false, "", "", "", fmt.Errorf("expression \"3\" (int) can not be dereferenced")},
A
aarzilli 已提交
733 734 735 736
		{"&(i2 + i3)", false, "", "", "", fmt.Errorf("can not take address of \"(i2 + i3)\"")},
		{"i2 + p1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"*int\"")},
		{"i2 + f1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"float64\"")},
		{"i2 << f1", false, "", "", "", fmt.Errorf("shift count type float64, must be unsigned integer")},
A
Alessandro Arzilli 已提交
737
		{"i2 << -1", false, "", "", "", fmt.Errorf("shift count must not be negative")},
A
aarzilli 已提交
738 739
		{"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")},
		{"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")},
740
		{"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("function calls not allowed without using 'call'")},
741 742 743 744 745
		{"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")},
		{"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")},
		{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
		{"nil[0]", false, "", "", "", fmt.Errorf("expression \"nil\" (nil) does not support indexing")},
		{"nil[2:10]", false, "", "", "", fmt.Errorf("can not slice \"nil\" (type nil)")},
746
		{"nil.member", false, "", "", "", fmt.Errorf("nil (type nil) is not a struct")},
747
		{"(map[string]main.astruct)(0x4000)", false, "", "", "", fmt.Errorf("can not convert \"0x4000\" to map[string]main.astruct")},
748 749

		// typecasts
750 751 752 753 754 755 756 757
		{"uint(i2)", false, "2", "2", "uint", nil},
		{"int8(i2)", false, "2", "2", "int8", nil},
		{"int(f1)", false, "3", "3", "int", nil},
		{"complex128(f1)", false, "(3 + 0i)", "(3 + 0i)", "complex128", nil},
		{"uint8(i4)", false, "32", "32", "uint8", nil},
		{"uint8(i5)", false, "253", "253", "uint8", nil},
		{"int8(i5)", false, "-3", "-3", "int8", nil},
		{"int8(i6)", false, "12", "12", "int8", nil},
758 759
		{"string(byteslice[0])", false, `"t"`, `"t"`, "string", nil},
		{"string(runeslice[0])", false, `"t"`, `"t"`, "string", nil},
760 761

		// misc
762 763 764 765
		{"i1", true, "1", "1", "int", nil},
		{"mainMenu", true, `main.Menu len: 3, cap: 3, [{Name: "home", Route: "/", Active: 1},{Name: "About", Route: "/about", Active: 1},{Name: "Login", Route: "/login", Active: 1}]`, `main.Menu len: 3, cap: 3, [...]`, "main.Menu", nil},
		{"mainMenu[0]", false, `main.Item {Name: "home", Route: "/", Active: 1}`, `main.Item {Name: "home", Route: "/", Active: 1}`, "main.Item", nil},
		{"sd", false, "main.D {u1: 0, u2: 0, u3: 0, u4: 0, u5: 0, u6: 0}", "main.D {u1: 0, u2: 0, u3: 0,...+3 more}", "main.D", nil},
766 767

		{"ifacearr", false, "[]error len: 2, cap: 2, [*main.astruct {A: 0, B: 0},nil]", "[]error len: 2, cap: 2, [...]", "[]error", nil},
768
		{"efacearr", false, `[]interface {} len: 3, cap: 3, [*main.astruct {A: 0, B: 0},"test",nil]`, "[]interface {} len: 3, cap: 3, [...]", "[]interface {}", nil},
769 770 771

		{"zsslice", false, `[]struct {} len: 3, cap: 3, [{},{},{}]`, `[]struct {} len: 3, cap: 3, [...]`, "[]struct {}", nil},
		{"zsvmap", false, `map[string]struct {} ["testkey": {}, ]`, `map[string]struct {} [...]`, "map[string]struct {}", nil},
772
		{"tm", false, "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [...]}", "main.truncatedMap", nil},
773 774 775 776

		{"emptyslice", false, `[]string len: 0, cap: 0, []`, `[]string len: 0, cap: 0, []`, "[]string", nil},
		{"emptymap", false, `map[string]string []`, `map[string]string []`, "map[string]string", nil},
		{"mnil", false, `map[string]main.astruct nil`, `map[string]main.astruct nil`, "map[string]main.astruct", nil},
A
aarzilli 已提交
777 778 779 780 781 782 783 784 785 786 787 788 789

		// conversions between string/[]byte/[]rune (issue #548)
		{"runeslice", true, `[]int32 len: 4, cap: 4, [116,232,115,116]`, `[]int32 len: 4, cap: 4, [...]`, "[]int32", nil},
		{"byteslice", true, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 5, cap: 5, [...]`, "[]uint8", nil},
		{"[]byte(str1)", false, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]uint8", nil},
		{"[]uint8(str1)", false, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]uint8", nil},
		{"[]rune(str1)", false, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]int32", nil},
		{"[]int32(str1)", false, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]int32", nil},
		{"string(byteslice)", false, `"tèst"`, `""`, "string", nil},
		{"[]int32(string(byteslice))", false, `[]int32 len: 4, cap: 4, [116,232,115,116]`, `[]int32 len: 0, cap: 0, nil`, "[]int32", nil},
		{"string(runeslice)", false, `"tèst"`, `""`, "string", nil},
		{"[]byte(string(runeslice))", false, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 0, cap: 0, nil`, "[]uint8", nil},
		{"*(*[5]byte)(uintptr(&byteslice[0]))", false, `[5]uint8 [116,195,168,115,116]`, `[5]uint8 [...]`, "[5]uint8", nil},
790 791 792
		{"string(bytearray)", false, `"tèst"`, `""`, "string", nil},
		{"string(runearray)", false, `"tèst"`, `""`, "string", nil},
		{"string(str1)", false, `"01234567890"`, `"01234567890"`, "string", nil},
A
aarzilli 已提交
793 794 795 796 797 798

		// access to channel field members
		{"ch1.qcount", false, "4", "4", "uint", nil},
		{"ch1.dataqsiz", false, "10", "10", "uint", nil},
		{"ch1.buf", false, `*[10]int [1,4,3,2,0,0,0,0,0,0]`, `(*[10]int)(…`, "*[10]int", nil},
		{"ch1.buf[0]", false, "1", "1", "int", nil},
799 800 801 802

		// shortcircuited logical operators
		{"nilstruct != nil && nilstruct.A == 1", false, "false", "false", "", nil},
		{"nilstruct == nil || nilstruct.A == 1", false, "true", "true", "", nil},
803 804 805

		{"afunc", true, `main.afunc`, `main.afunc`, `func()`, nil},
		{"main.afunc2", true, `main.afunc2`, `main.afunc2`, `func()`, nil},
A
aarzilli 已提交
806 807 808 809 810

		{"s2[0].Error", false, "main.(*astruct).Error", "main.(*astruct).Error", "func() string", nil},
		{"s2[0].NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", nil},
		{"as2.Error", false, "main.(*astruct).Error", "main.(*astruct).Error", "func() string", nil},
		{"as2.NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", nil},
811 812

		{`iface2map.(data)`, false, "…", "…", "map[string]interface {}", nil},
813 814

		{"issue1578", false, "main.Block {cache: *main.Cache nil}", "main.Block {cache: *main.Cache nil}", "main.Block", nil},
A
aarzilli 已提交
815 816
	}

817 818
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) {
819 820 821 822 823 824 825 826
		for i := range testcases {
			if testcases[i].name == "iface3" {
				testcases[i].value = "interface {}(*map[string]go/constant.Value) *[]"
				testcases[i].alternate = "interface {}(*map[string]go/constant.Value) 0x…"
			}
		}
	}

827
	protest.AllowRecording(t)
828
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
829
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
A
aarzilli 已提交
830
		for _, tc := range testcases {
831
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
A
aarzilli 已提交
832 833 834 835
			if err != nil && err.Error() == "evaluating methods not supported on this version of Go" {
				// this type of eval is unsupported with the current version of Go.
				continue
			}
A
aarzilli 已提交
836 837 838
			if tc.err == nil {
				assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
				assertVariable(t, variable, tc)
839 840 841
				variable, err := evalVariable(p, tc.name, pshortLoadConfig)
				assertNoError(err, t, fmt.Sprintf("EvalExpression(%s, pshortLoadConfig) returned an error", tc.name))
				assertVariable(t, variable, tc.alternateVarTest())
A
aarzilli 已提交
842 843
			} else {
				if err == nil {
844
					t.Fatalf("Expected error %s, got no error (%s)", tc.err.Error(), tc.name)
A
aarzilli 已提交
845 846 847 848 849
				}
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
850

A
aarzilli 已提交
851 852 853 854 855
		}
	})
}

func TestEvalAddrAndCast(t *testing.T) {
856
	protest.AllowRecording(t)
857
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
858
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
859
		c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig)
A
aarzilli 已提交
860 861 862 863 864 865 866
		assertNoError(err, t, "EvalExpression(&c1)")
		c1addrstr := api.ConvertVar(c1addr).SinglelineString()
		t.Logf("&c1 → %s", c1addrstr)
		if !strings.HasPrefix(c1addrstr, "(*main.cstruct)(0x") {
			t.Fatalf("Invalid value of EvalExpression(&c1) \"%s\"", c1addrstr)
		}

867
		aaddr, err := evalVariable(p, "&(c1.pb.a)", pnormalLoadConfig)
A
aarzilli 已提交
868 869 870 871 872 873 874
		assertNoError(err, t, "EvalExpression(&(c1.pb.a))")
		aaddrstr := api.ConvertVar(aaddr).SinglelineString()
		t.Logf("&(c1.pb.a) → %s", aaddrstr)
		if !strings.HasPrefix(aaddrstr, "(*main.astruct)(0x") {
			t.Fatalf("invalid value of EvalExpression(&(c1.pb.a)) \"%s\"", aaddrstr)
		}

875
		a, err := evalVariable(p, "*"+aaddrstr, pnormalLoadConfig)
A
aarzilli 已提交
876 877
		assertNoError(err, t, fmt.Sprintf("EvalExpression(*%s)", aaddrstr))
		t.Logf("*%s → %s", aaddrstr, api.ConvertVar(a).SinglelineString())
A
Alessandro Arzilli 已提交
878
		assertVariable(t, a, varTest{aaddrstr, false, "main.astruct {A: 1, B: 2}", "", "main.astruct", nil})
A
aarzilli 已提交
879 880
	})
}
A
aarzilli 已提交
881 882

func TestMapEvaluation(t *testing.T) {
883
	protest.AllowRecording(t)
884
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
885
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
886
		m1v, err := evalVariable(p, "m1", pnormalLoadConfig)
A
aarzilli 已提交
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
		assertNoError(err, t, "EvalVariable()")
		m1 := api.ConvertVar(m1v)
		t.Logf("m1 = %v", m1.MultilineString(""))

		if m1.Type != "map[string]main.astruct" {
			t.Fatalf("Wrong type: %s", m1.Type)
		}

		if len(m1.Children)/2 != 41 {
			t.Fatalf("Wrong number of children: %d", len(m1.Children)/2)
		}

		found := false
		for i := range m1.Children {
			if i%2 == 0 && m1.Children[i].Value == "Malone" {
				found = true
			}
		}
		if !found {
			t.Fatalf("Could not find Malone")
		}

909
		m1sliced, err := evalVariable(p, "m1[10:]", pnormalLoadConfig)
A
aarzilli 已提交
910 911 912 913 914
		assertNoError(err, t, "EvalVariable(m1[10:])")
		if len(m1sliced.Children)/2 != int(m1.Len-10) {
			t.Fatalf("Wrong number of children (after slicing): %d", len(m1sliced.Children)/2)
		}
	})
A
aarzilli 已提交
915
}
A
aarzilli 已提交
916

A
aarzilli 已提交
917
func TestUnsafePointer(t *testing.T) {
918
	protest.AllowRecording(t)
919
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
920
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
921
		up1v, err := evalVariable(p, "up1", pnormalLoadConfig)
A
aarzilli 已提交
922 923 924 925 926 927
		assertNoError(err, t, "EvalVariable(up1)")
		up1 := api.ConvertVar(up1v)
		if ss := up1.SinglelineString(); !strings.HasPrefix(ss, "unsafe.Pointer(") {
			t.Fatalf("wrong value for up1: %s", ss)
		}
	})
A
aarzilli 已提交
928
}
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945

type issue426TestCase struct {
	name string
	typ  string
}

func TestIssue426(t *testing.T) {
	// type casts using quoted type names
	testcases := []issue426TestCase{
		{"iface1", `interface {}`},
		{"mapanonstruct1", `map[string]struct {}`},
		{"anonstruct1", `struct { val go/constant.Value }`},
		{"anonfunc", `func(struct { i int }, interface {}, struct { val go/constant.Value })`},
		{"anonstruct2", `struct { i int; j int }`},
		{"anoniface1", `interface { OtherFunction(int, int); SomeFunction(struct { val go/constant.Value }) }`},
	}

946 947
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 8, -1, 0, 0, ""}) {
948 949 950 951 952 953
		testcases[2].typ = `struct { main.val go/constant.Value }`
		testcases[3].typ = `func(struct { main.i int }, interface {}, struct { main.val go/constant.Value })`
		testcases[4].typ = `struct { main.i int; main.j int }`
		testcases[5].typ = `interface { OtherFunction(int, int); SomeFunction(struct { main.val go/constant.Value }) }`
	}

954 955
	// Serialization of type expressions (go/ast.Expr) containing anonymous structs or interfaces
	// differs from the serialization used by the linker to produce DWARF type information
956
	protest.AllowRecording(t)
957
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
958
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
959 960 961 962 963 964 965 966 967 968
		for _, testcase := range testcases {
			v, err := evalVariable(p, testcase.name, pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", testcase.name))
			t.Logf("%s → %s", testcase.name, v.RealType.String())
			expr := fmt.Sprintf("(*%q)(%d)", testcase.typ, v.Addr)
			_, err = evalVariable(p, expr, pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", expr))
		}
	})
}
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985

func TestPackageRenames(t *testing.T) {
	// Tests that the concrete type of an interface variable is resolved
	// correctly in a few edge cases, in particular:
	// - in the presence of renamed imports
	// - when two packages with the same name are imported
	// - when a package has a canonical name that's different from its
	// path (for example the last element of the path contains a '.' or a
	// '-' or because the package name is different)
	// all of those edge cases are tested within composite types
	testcases := []varTest{
		// Renamed imports
		{"badexpr", true, `interface {}(*go/ast.BadExpr) *{From: 1, To: 2}`, "", "interface {}", nil},
		{"req", true, `interface {}(*net/http.Request) *{Method: "amethod", …`, "", "interface {}", nil},
		{"amap", true, "interface {}(map[go/ast.BadExpr]net/http.Request) [{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},

		// Package name that doesn't match import path
986
		{"iface3", true, `interface {}(*github.com/go-delve/delve/_fixtures/internal/dir0/renamedpackage.SomeType) *{A: true}`, "", "interface {}", nil},
987 988 989

		// Interfaces to anonymous types
		{"amap2", true, "interface {}(*map[go/ast.BadExpr]net/http.Request) *[{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},
990 991 992 993 994 995 996 997 998 999
		{"dir0someType", true, "interface {}(*github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType) *{X: 3}", "", "interface {}", nil},
		{"dir1someType", true, "interface {}(github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType) {X: 1, Y: 2}", "", "interface {}", nil},
		{"amap3", true, "interface {}(map[github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType]github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType) [{X: 4}: {X: 5, Y: 6}, ]", "", "interface {}", nil},
		{"anarray", true, `interface {}([2]github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType) [{X: 1},{X: 2}]`, "", "interface {}", nil},
		{"achan", true, `interface {}(chan github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType) chan github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType 0/0`, "", "interface {}", nil},
		{"aslice", true, `interface {}([]github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType) [{X: 3},{X: 4}]`, "", "interface {}", nil},
		{"afunc", true, `interface {}(func(github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType, github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType)) main.main.func1`, "", "interface {}", nil},
		{"astruct", true, `interface {}(*struct { A github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType; B github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType }) *{A: github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType {X: 1, Y: 2}, B: github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType {X: 3}}`, "", "interface {}", nil},
		{"astruct2", true, `interface {}(*struct { github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType; X int }) *{SomeType: github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType {X: 1, Y: 2}, X: 10}`, "", "interface {}", nil},
		{"iface2iface", true, `interface {}(*interface { AMethod(int) int; AnotherMethod(int) int }) **github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType {X: 4}`, "", "interface {}", nil},
1000 1001 1002

		{`"dir0/pkg".A`, false, "0", "", "int", nil},
		{`"dir1/pkg".A`, false, "1", "", "int", nil},
1003 1004
	}

1005 1006
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) {
1007 1008 1009 1010
		// Not supported on 1.6 or earlier
		return
	}

1011
	protest.AllowRecording(t)
1012
	withTestProcess("pkgrenames", t, func(p proc.Process, fixture protest.Fixture) {
1013
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
1014
		for _, tc := range testcases {
1015
			if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
1016 1017
				// before 1.9 embedded struct field have fieldname == type
				if tc.name == "astruct2" {
1018
					tc.value = `interface {}(*struct { github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType; X int }) *{github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType: github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType {X: 1, Y: 2}, X: 10}`
1019 1020
				}
			}
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
			if tc.err == nil {
				assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
				assertVariable(t, variable, tc)
			} else {
				if err == nil {
					t.Fatalf("Expected error %s, got no error (%s)", tc.err.Error(), tc.name)
				}
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
		}
	})
}
1036 1037 1038

func TestConstants(t *testing.T) {
	testcases := []varTest{
1039 1040 1041
		{"a", true, "constTwo (2)", "", "main.ConstType", nil},
		{"b", true, "constThree (3)", "", "main.ConstType", nil},
		{"c", true, "bitZero|bitOne (3)", "", "main.BitFieldType", nil},
1042 1043 1044 1045 1046 1047
		{"d", true, "33", "", "main.BitFieldType", nil},
		{"e", true, "10", "", "main.ConstType", nil},
		{"f", true, "0", "", "main.BitFieldType", nil},
		{"bitZero", true, "1", "", "main.BitFieldType", nil},
		{"bitOne", true, "2", "", "main.BitFieldType", nil},
		{"constTwo", true, "2", "", "main.ConstType", nil},
1048
		{"pkg.SomeConst", true, "2", "", "int", nil},
1049 1050 1051 1052
	}
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
		// Not supported on 1.9 or earlier
1053
		t.Skip("constants added in go 1.10")
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
	}
	withTestProcess("consts", t, func(p proc.Process, fixture protest.Fixture) {
		assertNoError(proc.Continue(p), t, "Continue")
		for _, testcase := range testcases {
			variable, err := evalVariable(p, testcase.name, pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", testcase.name))
			assertVariable(t, variable, testcase)
		}
	})
}
1064

1065 1066 1067 1068
func setFunctionBreakpoint(p proc.Process, t testing.TB, fname string) *proc.Breakpoint {
	_, f, l, _ := runtime.Caller(1)
	f = filepath.Base(f)

1069
	addr, err := proc.FindFunctionLocation(p, fname, 0)
1070
	if err != nil {
1071
		t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
1072
	}
1073 1074 1075 1076 1077
	bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil)
	if err != nil {
		t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
	}
	return bp
1078 1079 1080 1081
}

func TestIssue1075(t *testing.T) {
	withTestProcess("clientdo", t, func(p proc.Process, fixture protest.Fixture) {
1082
		setFunctionBreakpoint(p, t, "net/http.(*Client).Do")
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
		assertNoError(proc.Continue(p), t, "Continue()")
		for i := 0; i < 10; i++ {
			scope, err := proc.GoroutineScope(p.CurrentThread())
			assertNoError(err, t, fmt.Sprintf("GoroutineScope (%d)", i))
			vars, err := scope.LocalVariables(pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("LocalVariables (%d)", i))
			for _, v := range vars {
				api.ConvertVar(v).SinglelineString()
			}
		}
	})
}
1095

1096 1097 1098 1099 1100 1101
type testCaseCallFunction struct {
	expr string   // call expression to evaluate
	outs []string // list of return parameters in this format: <param name>:<param type>:<param value>
	err  error    // if not nil should return an error
}

1102 1103 1104
func TestCallFunction(t *testing.T) {
	protest.MustSupportFunctionCalls(t, testBackend)

1105
	var testcases = []testCaseCallFunction{
1106 1107
		// Basic function call injection tests

1108 1109 1110 1111 1112 1113 1114
		{"call1(one, two)", []string{":int:3"}, nil},
		{"call1(one+two, 4)", []string{":int:7"}, nil},
		{"callpanic()", []string{`~panic:interface {}:interface {}(string) "callpanic panicked"`}, nil},
		{`stringsJoin(nil, "")`, []string{`:string:""`}, nil},
		{`stringsJoin(stringslice, comma)`, []string{`:string:"one,two,three"`}, nil},
		{`stringsJoin(s1, comma)`, nil, errors.New("could not find symbol value for s1")},
		{`stringsJoin(intslice, comma)`, nil, errors.New("can not convert value of type []int to []string")},
1115
		{`noreturncall(2)`, nil, nil},
1116

1117 1118 1119 1120 1121 1122 1123
		// Expression tests
		{`square(2) + 1`, []string{":int:5"}, nil},
		{`intcallpanic(1) + 1`, []string{":int:2"}, nil},
		{`intcallpanic(0) + 1`, []string{`~panic:interface {}:interface {}(string) "panic requested"`}, nil},
		{`onetwothree(5)[1] + 2`, []string{":int:9"}, nil},

		// Call types tests (methods, function pointers, etc.)
1124 1125
		// The following set of calls was constructed using https://docs.google.com/document/d/1bMwCey-gmqZVTpRax-ESeVuZGmjwbocYs1iHplK-cjo/pub as a reference

1126 1127
		{`a.VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil}, // direct call of a method with value receiver / on a value

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
		{`a.PRcvr(2)`, []string{`:string:"2 - 3 = -1"`}, nil},  // direct call of a method with pointer receiver / on a value
		{`pa.VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil},  // direct call of a method with value receiver / on a pointer
		{`pa.PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil}, // direct call of a method with pointer receiver / on a pointer

		{`vable_pa.VRcvr(6)`, []string{`:string:"6 + 6 = 12"`}, nil}, // indirect call of method on interface / containing value with value method
		{`pable_pa.PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil},  // indirect call of method on interface / containing pointer with value method
		{`vable_a.VRcvr(5)`, []string{`:string:"5 + 3 = 8"`}, nil},   // indirect call of method on interface / containing pointer with pointer method

		{`pa.nonexistent()`, nil, errors.New("pa has no member nonexistent")},
		{`a.nonexistent()`, nil, errors.New("a has no member nonexistent")},
		{`vable_pa.nonexistent()`, nil, errors.New("vable_pa has no member nonexistent")},
		{`vable_a.nonexistent()`, nil, errors.New("vable_a has no member nonexistent")},
		{`pable_pa.nonexistent()`, nil, errors.New("pable_pa has no member nonexistent")},

1142 1143 1144 1145 1146 1147 1148
		{`fn2glob(10, 20)`, []string{":int:30"}, nil},               // indirect call of func value / set to top-level func
		{`fn2clos(11)`, []string{`:string:"1 + 6 + 11 = 18"`}, nil}, // indirect call of func value / set to func literal
		{`fn2clos(12)`, []string{`:string:"2 + 6 + 12 = 20"`}, nil},
		{`fn2valmeth(13)`, []string{`:string:"13 + 6 = 19"`}, nil}, // indirect call of func value / set to value method
		{`fn2ptrmeth(14)`, []string{`:string:"14 - 6 = 8"`}, nil},  // indirect call of func value / set to pointer method

		{"fn2nil()", nil, errors.New("nil pointer dereference")},
1149 1150

		{"ga.PRcvr(2)", []string{`:string:"2 - 0 = 2"`}, nil},
1151

1152
		// Nested function calls tests
1153 1154 1155 1156 1157 1158

		{`onetwothree(intcallpanic(2))`, []string{`:[]int:[]int len: 3, cap: 3, [3,4,5]`}, nil},
		{`onetwothree(intcallpanic(0))`, []string{`~panic:interface {}:interface {}(string) "panic requested"`}, nil},
		{`onetwothree(intcallpanic(2)+1)`, []string{`:[]int:[]int len: 3, cap: 3, [4,5,6]`}, nil},
		{`onetwothree(intcallpanic("not a number"))`, nil, errors.New("can not convert \"not a number\" constant to int")},

1159 1160 1161
		// Variable setting tests
		{`pa2 = getAStructPtr(8); pa2`, []string{`pa2:*main.astruct:*main.astruct {X: 8}`}, nil},

1162 1163
		// Escape tests

1164
		{"escapeArg(&a2)", nil, errors.New("cannot use &a2 as argument pa2 in function main.escapeArg: stack object passed to escaping pointer: pa2")},
1165 1166 1167 1168

		// Issue 1577
		{"1+2", []string{`::3`}, nil},
		{`"de"+"mo"`, []string{`::"demo"`}, nil},
1169 1170
	}

1171 1172 1173 1174 1175 1176
	var testcases112 = []testCaseCallFunction{
		// string allocation requires trusted argument order, which we don't have in Go 1.11
		{`stringsJoin(stringslice, ",")`, []string{`:string:"one,two,three"`}, nil},
		{`str = "a new string"; str`, []string{`str:string:"a new string"`}, nil},
	}

1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
	var testcases113 = []testCaseCallFunction{
		{`curriedAdd(2)(3)`, []string{`:int:5`}, nil},

		// Method calls on a value returned by a function

		{`getAStruct(3).VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil}, // direct call of a method with value receiver / on a value

		{`getAStruct(3).PRcvr(2)`, nil, errors.New("cannot use getAStruct(3).PRcvr as argument pa in function main.(*astruct).PRcvr: stack object passed to escaping pointer: pa")}, // direct call of a method with pointer receiver / on a value
		{`getAStructPtr(6).VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil},  // direct call of a method with value receiver / on a pointer
		{`getAStructPtr(6).PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil}, // direct call of a method with pointer receiver / on a pointer

		{`getVRcvrableFromAStruct(3).VRcvr(6)`, []string{`:string:"6 + 3 = 9"`}, nil},     // indirect call of method on interface / containing value with value method
		{`getPRcvrableFromAStructPtr(6).PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil},  // indirect call of method on interface / containing pointer with value method
		{`getVRcvrableFromAStructPtr(6).VRcvr(5)`, []string{`:string:"5 + 6 = 11"`}, nil}, // indirect call of method on interface / containing pointer with pointer method
	}
1192

1193
	withTestProcess("fncall", t, func(p proc.Process, fixture protest.Fixture) {
1194
		_, err := proc.FindFunctionLocation(p, "runtime.debugCallV1", 0)
1195 1196 1197 1198 1199
		if err != nil {
			t.Skip("function calls not supported on this version of go")
		}
		assertNoError(proc.Continue(p), t, "Continue()")
		for _, tc := range testcases {
1200 1201
			testCallFunction(t, p, tc)
		}
1202

1203 1204 1205 1206 1207 1208
		if goversion.VersionAfterOrEqual(runtime.Version(), 1, 12) {
			for _, tc := range testcases112 {
				testCallFunction(t, p, tc)
			}
		}

1209 1210 1211
		if goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) {
			for _, tc := range testcases113 {
				testCallFunction(t, p, tc)
1212
			}
1213
		}
1214

1215 1216 1217 1218
		// LEAVE THIS AS THE LAST ITEM, IT BREAKS THE TARGET PROCESS!!!
		testCallFunction(t, p, testCaseCallFunction{"-unsafe escapeArg(&a2)", nil, nil})
	})
}
1219

1220 1221
func testCallFunction(t *testing.T, p proc.Process, tc testCaseCallFunction) {
	const unsafePrefix = "-unsafe "
1222

1223 1224 1225 1226 1227 1228 1229 1230 1231
	var callExpr, varExpr string

	if semicolon := strings.Index(tc.expr, ";"); semicolon >= 0 {
		callExpr = tc.expr[:semicolon]
		varExpr = tc.expr[semicolon+1:]
	} else {
		callExpr = tc.expr
	}

1232
	checkEscape := true
1233 1234
	if strings.HasPrefix(callExpr, unsafePrefix) {
		callExpr = callExpr[len(unsafePrefix):]
1235 1236 1237
		checkEscape = false
	}
	t.Logf("call %q", tc.expr)
1238
	err := proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), callExpr, pnormalLoadConfig, checkEscape)
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
	if tc.err != nil {
		t.Logf("\terr = %v\n", err)
		if err == nil {
			t.Fatalf("call %q: expected error %q, got no error", tc.expr, tc.err.Error())
		}
		if tc.err.Error() != err.Error() {
			t.Fatalf("call %q: expected error %q, got %q", tc.expr, tc.err.Error(), err.Error())
		}
		return
	}
1249

1250 1251 1252
	if err != nil {
		t.Fatalf("call %q: error %q", tc.expr, err.Error())
	}
1253

1254 1255
	retvalsVar := p.CurrentThread().Common().ReturnValues(pnormalLoadConfig)
	retvals := make([]*api.Variable, len(retvalsVar))
1256

1257 1258 1259
	for i := range retvals {
		retvals[i] = api.ConvertVar(retvalsVar[i])
	}
1260

1261 1262 1263 1264 1265 1266 1267 1268
	if varExpr != "" {
		scope, err := proc.GoroutineScope(p.CurrentThread())
		assertNoError(err, t, "GoroutineScope")
		v, err := scope.EvalExpression(varExpr, pnormalLoadConfig)
		assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", varExpr))
		retvals = append(retvals, api.ConvertVar(v))
	}

1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
	for i := range retvals {
		t.Logf("\t%s = %s", retvals[i].Name, retvals[i].SinglelineString())
	}

	if len(retvals) != len(tc.outs) {
		t.Fatalf("call %q: wrong number of return parameters", tc.expr)
	}

	for i := range retvals {
		outfields := strings.SplitN(tc.outs[i], ":", 3)
		tgtName, tgtType, tgtValue := outfields[0], outfields[1], outfields[2]

		if tgtName != "" && tgtName != retvals[i].Name {
			t.Fatalf("call %q output parameter %d: expected name %q, got %q", tc.expr, i, tgtName, retvals[i].Name)
1283
		}
1284 1285 1286 1287 1288 1289 1290 1291

		if retvals[i].Type != tgtType {
			t.Fatalf("call %q, output parameter %d: expected type %q, got %q", tc.expr, i, tgtType, retvals[i].Type)
		}
		if cvs := retvals[i].SinglelineString(); cvs != tgtValue {
			t.Fatalf("call %q, output parameter %d: expected value %q, got %q", tc.expr, i, tgtValue, cvs)
		}
	}
1292
}
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334

func TestIssue1531(t *testing.T) {
	// Go 1.12 introduced a change to the map representation where empty cells can be marked with 1 instead of just 0.
	withTestProcess("issue1531", t, func(p proc.Process, fixture protest.Fixture) {
		assertNoError(proc.Continue(p), t, "Continue()")

		hasKeys := func(mv *proc.Variable, keys ...string) {
			n := 0
			for i := 0; i < len(mv.Children); i += 2 {
				cv := &mv.Children[i]
				s := constant.StringVal(cv.Value)
				found := false
				for j := range keys {
					if keys[j] == s {
						found = true
						break
					}
				}
				if !found {
					t.Errorf("key %q not allowed", s)
					return
				}
				n++
			}
			if n != len(keys) {
				t.Fatalf("wrong number of keys found")
			}
		}

		mv, err := evalVariable(p, "m", pnormalLoadConfig)
		assertNoError(err, t, "EvalVariable(m)")
		cmv := api.ConvertVar(mv)
		t.Logf("m = %s", cmv.SinglelineString())
		hasKeys(mv, "s", "r", "v")

		mmv, err := evalVariable(p, "mm", pnormalLoadConfig)
		assertNoError(err, t, "EvalVariable(mm)")
		cmmv := api.ConvertVar(mmv)
		t.Logf("mm = %s", cmmv.SinglelineString())
		hasKeys(mmv, "r", "t", "v")
	})
}
1335

1336 1337 1338 1339 1340
func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint {
	_, f, l, _ := runtime.Caller(1)
	f = filepath.Base(f)

	addr, err := proc.FindFileLocation(p, fixture.Source, lineno)
1341
	if err != nil {
1342
		t.Fatalf("%s:%d: FindFileLocation(%s, %d): %v", f, l, fixture.Source, lineno, err)
1343 1344 1345
	}
	bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil)
	if err != nil {
1346
		t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err)
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
	}
	return bp
}

func currentLocation(p proc.Process, t *testing.T) (pc uint64, f string, ln int, fn *proc.Function) {
	regs, err := p.CurrentThread().Registers(false)
	if err != nil {
		t.Fatalf("Registers error: %v", err)
	}
	f, l, fn := p.BinInfo().PCToLine(regs.PC())
	t.Logf("at %#x %s:%d %v", regs.PC(), f, l, fn)
	return regs.PC(), f, l, fn
}

func assertCurrentLocationFunction(p proc.Process, t *testing.T, fnname string) {
	_, _, _, fn := currentLocation(p, t)
	if fn == nil {
		t.Fatalf("Not in a function")
	}
	if fn.Name != fnname {
		t.Fatalf("Wrong function %s %s", fn.Name, fnname)
	}
}

func TestPluginVariables(t *testing.T) {
A
Alessandro Arzilli 已提交
1372
	pluginFixtures := protest.WithPlugins(t, protest.AllNonOptimized, "plugin1/", "plugin2/")
1373

A
Alessandro Arzilli 已提交
1374
	withTestProcessArgs("plugintest2", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, func(p proc.Process, fixture protest.Fixture) {
1375
		setFileBreakpoint(p, t, fixture, 41)
1376 1377
		assertNoError(proc.Continue(p), t, "Continue 1")

1378
		bp := setFunctionBreakpoint(p, t, "github.com/go-delve/delve/_fixtures/plugin2.TypesTest")
1379
		t.Logf("bp.Addr = %#x", bp.Addr)
1380
		setFunctionBreakpoint(p, t, "github.com/go-delve/delve/_fixtures/plugin2.aIsNotNil")
1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431

		for _, image := range p.BinInfo().Images {
			t.Logf("%#x %s\n", image.StaticBase, image.Path)
		}

		assertNoError(proc.Continue(p), t, "Continue 2")

		// test that PackageVariables returns variables from the executable and plugins
		scope, err := evalScope(p)
		assertNoError(err, t, "evalScope")
		allvars, err := scope.PackageVariables(pnormalLoadConfig)
		assertNoError(err, t, "PackageVariables")
		var plugin2AFound, mainExeGlobalFound bool
		for _, v := range allvars {
			switch v.Name {
			case "github.com/go-delve/delve/_fixtures/plugin2.A":
				plugin2AFound = true
			case "main.ExeGlobal":
				mainExeGlobalFound = true
			}
		}
		if !plugin2AFound {
			t.Fatalf("variable plugin2.A not found in the output of PackageVariables")
		}
		if !mainExeGlobalFound {
			t.Fatalf("variable main.ExeGlobal not found in the output of PackageVariables")
		}

		// read interface variable, inside plugin code, with a concrete type defined in the executable
		vs, err := evalVariable(p, "s", pnormalLoadConfig)
		assertNoError(err, t, "Eval(s)")
		assertVariable(t, vs, varTest{"s", true, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.Something(*main.asomething) *{n: 2}`, ``, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.Something`, nil})

		// test that the concrete type -> interface{} conversion works across plugins (mostly tests proc.dwarfToRuntimeType)
		assertNoError(setVariable(p, "plugin2.A", "main.ExeGlobal"), t, "setVariable(plugin2.A = main.ExeGlobal)")
		assertNoError(proc.Continue(p), t, "Continue 3")
		assertCurrentLocationFunction(p, t, "github.com/go-delve/delve/_fixtures/plugin2.aIsNotNil")
		vstr, err := evalVariable(p, "str", pnormalLoadConfig)
		assertNoError(err, t, "Eval(str)")
		assertVariable(t, vstr, varTest{"str", true, `"success"`, ``, `string`, nil})

		assertNoError(proc.StepOut(p), t, "StepOut")
		assertNoError(proc.StepOut(p), t, "StepOut")
		assertNoError(proc.Next(p), t, "Next")

		// read interface variable, inside executable code, with a concrete type defined in a plugin
		vb, err := evalVariable(p, "b", pnormalLoadConfig)
		assertNoError(err, t, "Eval(b)")
		assertVariable(t, vb, varTest{"b", true, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.SomethingElse(*github.com/go-delve/delve/_fixtures/plugin2.asomethingelse) *{x: 1, y: 4}`, ``, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.SomethingElse`, nil})
	})
}