| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package peek_test |
| |
| import ( |
| "flag" |
| "fmt" |
| "log" |
| "os" |
| "os/exec" |
| "reflect" |
| "regexp" |
| "sync" |
| "testing" |
| |
| "golang.org/x/debug" |
| "golang.org/x/debug/local" |
| "golang.org/x/debug/remote" |
| ) |
| |
| var expectedVarValues = map[string]interface{}{ |
| `main.Z_bool_false`: false, |
| `main.Z_bool_true`: true, |
| `main.Z_complex128`: complex128(1.987654321 - 2.987654321i), |
| `main.Z_complex64`: complex64(1.54321 + 2.54321i), |
| `main.Z_float32`: float32(1.54321), |
| `main.Z_float64`: float64(1.987654321), |
| `main.Z_int16`: int16(-32321), |
| `main.Z_int32`: int32(-1987654321), |
| `main.Z_int64`: int64(-9012345678987654321), |
| `main.Z_int8`: int8(-121), |
| `main.Z_uint16`: uint16(54321), |
| `main.Z_uint32`: uint32(3217654321), |
| `main.Z_uint64`: uint64(12345678900987654321), |
| `main.Z_uint8`: uint8(231), |
| } |
| |
| // TODO: the string forms of some types we're testing aren't stable |
| var expectedVars = map[string]string{ |
| `main.Z_array`: `[5]int8{-121, 121, 3, 2, 1}`, |
| `main.Z_array_empty`: `[0]int8{}`, |
| `main.Z_bool_false`: `false`, |
| `main.Z_bool_true`: `true`, |
| `main.Z_channel`: `(chan int16 0xX)`, |
| `main.Z_channel_2`: `(chan int16 0xX)`, |
| `main.Z_channel_buffered`: `(chan int16 0xX [6/10])`, |
| `main.Z_channel_nil`: `(chan int16 <nil>)`, |
| `main.Z_array_of_empties`: `[2]{}{{} {}, ({} 0xX)}`, |
| `main.Z_complex128`: `(1.987654321-2.987654321i)`, |
| `main.Z_complex64`: `(1.54321+2.54321i)`, |
| `main.Z_float32`: `1.54321`, |
| `main.Z_float64`: `1.987654321`, |
| `main.Z_func_int8_r_int8`: `func(int8, *int8) void @0xX `, |
| `main.Z_func_int8_r_pint8`: `func(int8, **int8) void @0xX `, |
| `main.Z_func_bar`: `func(*main.FooStruct) void @0xX `, |
| `main.Z_func_nil`: `func(int8, *int8) void @0xX `, |
| `main.Z_int`: `-21`, |
| `main.Z_int16`: `-32321`, |
| `main.Z_int32`: `-1987654321`, |
| `main.Z_int64`: `-9012345678987654321`, |
| `main.Z_int8`: `-121`, |
| `main.Z_int_typedef`: `88`, |
| `main.Z_interface`: `("*main.FooStruct", 0xX)`, |
| `main.Z_interface_nil`: `(<nil>, <nil>)`, |
| `main.Z_interface_typed_nil`: `("*main.FooStruct", <nil>)`, |
| `main.Z_map`: `map[-21:3.54321]`, |
| `main.Z_map_2`: `map[1024:1]`, |
| `main.Z_map_3`: `map[1024:1 512:-1]`, |
| `main.Z_map_empty`: `map[]`, |
| `main.Z_map_nil`: `map[]`, |
| `main.Z_pointer`: `0xX`, |
| `main.Z_pointer_nil`: `0x0`, |
| `main.Z_slice`: `[]uint8{115, 108, 105, 99, 101}`, |
| `main.Z_slice_2`: `[]int8{-121, 121}`, |
| `main.Z_slice_nil`: `[]uint8{}`, |
| `main.Z_string`: `"I'm a string"`, |
| `main.Z_struct`: `main.FooStruct {21, "hi"}`, |
| `main.Z_uint`: `21`, |
| `main.Z_uint16`: `54321`, |
| `main.Z_uint32`: `3217654321`, |
| `main.Z_uint64`: `12345678900987654321`, |
| `main.Z_uint8`: `231`, |
| `main.Z_uintptr`: `21`, |
| `main.Z_unsafe_pointer`: `0xX`, |
| `main.Z_unsafe_pointer_nil`: `0x0`, |
| } |
| |
| // expectedEvaluate contains expected results of the debug.Evaluate function. |
| // A nil value indicates that an error is expected. |
| var expectedEvaluate = map[string]debug.Value{ |
| `x`: int16(42), |
| `local_array`: debug.Array{42, 42, 5, 8}, |
| `local_channel`: debug.Channel{42, 42, 42, 0, 0, 2, 0}, |
| `local_channel_buffered`: debug.Channel{42, 42, 42, 6, 10, 2, 8}, |
| `local_map`: debug.Map{42, 42, 1}, |
| `local_map_2`: debug.Map{42, 42, 1}, |
| `local_map_3`: debug.Map{42, 42, 2}, |
| `local_map_empty`: debug.Map{42, 42, 0}, |
| `x + 5`: int16(47), |
| `x - 5`: int16(37), |
| `x / 5`: int16(8), |
| `x % 5`: int16(2), |
| `x & 2`: int16(2), |
| `x | 1`: int16(43), |
| `x ^ 3`: int16(41), |
| `5 + x`: int16(47), |
| `5 - x`: int16(-37), |
| `100 / x`: int16(2), |
| `100 % x`: int16(16), |
| `2 & x`: int16(2), |
| `1 | x`: int16(43), |
| `3 ^ x`: int16(41), |
| `12`: 12, |
| `+42`: 42, |
| `23i`: 23i, |
| `34.0`: 34.0, |
| `34.5`: 34.5, |
| `1e5`: 100000.0, |
| `0x42`: 66, |
| `'c'`: 'c', |
| `"de"`: debug.String{2, `de`}, |
| "`ef`": debug.String{2, `ef`}, |
| `"de" + "fg"`: debug.String{4, `defg`}, |
| `/* comment */ -5`: -5, |
| `false`: false, |
| `true`: true, |
| `!false`: true, |
| `!true`: false, |
| `5 + 5`: 10, |
| `true || false`: true, |
| `false || false`: false, |
| `true && false`: false, |
| `true && true`: true, |
| `!(5 > 8)`: true, |
| `10 + 'a'`: 'k', |
| `10 + 10.5`: 20.5, |
| `10 + 10.5i`: 10 + 10.5i, |
| `'a' + 10.5`: 107.5, |
| `'a' + 10.5i`: 97 + 10.5i, |
| `10.5 + 20.5i`: 10.5 + 20.5i, |
| `10 * 20`: 200, |
| `10.0 - 20.5`: -10.5, |
| `(6 + 8i) * 4`: 24 + 32i, |
| `(6 + 8i) * (1 + 1i)`: -2 + 14i, |
| `(6 + 8i) * (6 - 8i)`: complex128(100), |
| `(6 + 8i) / (3 + 4i)`: complex128(2), |
| `local_array[2]`: int8(3), |
| `&local_array[1]`: debug.Pointer{42, 42}, |
| `local_map[-21]`: float32(3.54321), |
| `local_map[+21]`: float32(0), |
| `local_map_3[1024]`: int8(1), |
| `local_map_3[512]`: int8(-1), |
| `local_map_empty[21]`: float32(0), |
| `"hello"[2]`: uint8('l'), |
| `local_array[1:3][1]`: int8(3), |
| `local_array[0:4][2:3][0]`: int8(3), |
| `local_array[:]`: debug.Slice{debug.Array{42, 42, 5, 8}, 5}, |
| `local_array[:2]`: debug.Slice{debug.Array{42, 42, 2, 8}, 5}, |
| `local_array[2:]`: debug.Slice{debug.Array{42, 42, 3, 8}, 3}, |
| `local_array[1:3]`: debug.Slice{debug.Array{42, 42, 2, 8}, 4}, |
| `local_array[:3:4]`: debug.Slice{debug.Array{42, 42, 3, 8}, 4}, |
| `local_array[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, |
| `local_array[1:][1:][1:]`: debug.Slice{debug.Array{42, 42, 2, 8}, 2}, |
| `(&local_array)[:]`: debug.Slice{debug.Array{42, 42, 5, 8}, 5}, |
| `(&local_array)[:2]`: debug.Slice{debug.Array{42, 42, 2, 8}, 5}, |
| `(&local_array)[2:]`: debug.Slice{debug.Array{42, 42, 3, 8}, 3}, |
| `(&local_array)[1:3]`: debug.Slice{debug.Array{42, 42, 2, 8}, 4}, |
| `(&local_array)[:3:4]`: debug.Slice{debug.Array{42, 42, 3, 8}, 4}, |
| `(&local_array)[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, |
| `lookup("main.Z_array")`: debug.Array{42, 42, 5, 8}, |
| `lookup("main.Z_array_empty")`: debug.Array{42, 42, 0, 8}, |
| `lookup("main.Z_bool_false")`: false, |
| `lookup("main.Z_bool_true")`: true, |
| `lookup("main.Z_channel")`: debug.Channel{42, 42, 42, 0, 0, 2, 0}, |
| `lookup("main.Z_channel_buffered")`: debug.Channel{42, 42, 42, 6, 10, 2, 8}, |
| `lookup("main.Z_channel_nil")`: debug.Channel{42, 0, 0, 0, 0, 2, 0}, |
| `lookup("main.Z_array_of_empties")`: debug.Array{42, 42, 2, 0}, |
| `lookup("main.Z_complex128")`: complex128(1.987654321 - 2.987654321i), |
| `lookup("main.Z_complex64")`: complex64(1.54321 + 2.54321i), |
| `lookup("main.Z_float32")`: float32(1.54321), |
| `lookup("main.Z_float64")`: float64(1.987654321), |
| `lookup("main.Z_func_int8_r_int8")`: debug.Func{42}, |
| `lookup("main.Z_func_int8_r_pint8")`: debug.Func{42}, |
| `lookup("main.Z_func_bar")`: debug.Func{42}, |
| `lookup("main.Z_func_nil")`: debug.Func{0}, |
| `lookup("main.Z_int")`: -21, |
| `lookup("main.Z_int16")`: int16(-32321), |
| `lookup("main.Z_int32")`: int32(-1987654321), |
| `lookup("main.Z_int64")`: int64(-9012345678987654321), |
| `lookup("main.Z_int8")`: int8(-121), |
| `lookup("main.Z_int_typedef")`: int16(88), |
| `lookup("main.Z_interface")`: debug.Interface{}, |
| `lookup("main.Z_interface_nil")`: debug.Interface{}, |
| `lookup("main.Z_interface_typed_nil")`: debug.Interface{}, |
| `lookup("main.Z_map")`: debug.Map{42, 42, 1}, |
| `lookup("main.Z_map_2")`: debug.Map{42, 42, 1}, |
| `lookup("main.Z_map_3")`: debug.Map{42, 42, 2}, |
| `lookup("main.Z_map_empty")`: debug.Map{42, 42, 0}, |
| `lookup("main.Z_map_nil")`: debug.Map{42, 42, 0}, |
| `lookup("main.Z_pointer")`: debug.Pointer{42, 42}, |
| `lookup("main.Z_pointer_nil")`: debug.Pointer{42, 0}, |
| `lookup("main.Z_slice")`: debug.Slice{debug.Array{42, 42, 5, 8}, 5}, |
| `lookup("main.Z_slice_2")`: debug.Slice{debug.Array{42, 42, 2, 8}, 5}, |
| `lookup("main.Z_slice_nil")`: debug.Slice{debug.Array{42, 0, 0, 8}, 0}, |
| `lookup("main.Z_string")`: debug.String{12, `I'm a string`}, |
| `lookup("main.Z_struct")`: debug.Struct{[]debug.StructField{{"a", debug.Var{}}, {"b", debug.Var{}}}}, |
| `lookup("main.Z_uint")`: uint(21), |
| `lookup("main.Z_uint16")`: uint16(54321), |
| `lookup("main.Z_uint32")`: uint32(3217654321), |
| `lookup("main.Z_uint64")`: uint64(12345678900987654321), |
| `lookup("main.Z_uint8")`: uint8(231), |
| `lookup("main.Z_uintptr")`: uint(21), |
| `lookup("main.Z_unsafe_pointer")`: debug.Pointer{0, 42}, |
| `lookup("main.Z_unsafe_pointer_nil")`: debug.Pointer{0, 0}, |
| `lookup("main.Z_int") + lookup("main.Z_int")`: -42, |
| `lookup("main.Z_int16") < 0`: true, |
| `lookup("main.Z_uint32") + lookup("main.Z_uint32")`: uint32(2140341346), |
| `lookup("main.Z_bool_true") || lookup("main.Z_bool_false")`: true, |
| `lookup("main.Z_bool_true") && lookup("main.Z_bool_false")`: false, |
| `lookup("main.Z_bool_false") || lookup("main.Z_bool_false")`: false, |
| `!lookup("main.Z_bool_true")`: false, |
| `!lookup("main.Z_bool_false")`: true, |
| `lookup("main.Z_array")[2]`: int8(3), |
| `lookup("main.Z_array")[1:3][1]`: int8(3), |
| `lookup("main.Z_array")[0:4][2:3][0]`: int8(3), |
| `lookup("main.Z_array_of_empties")[0]`: debug.Struct{}, |
| `lookup("main.Z_complex128") * 10.0`: complex128(19.87654321 - 29.87654321i), |
| `lookup("main.Z_complex64") * 0.1`: complex64(0.154321 + 0.254321i), |
| `lookup("main.Z_float32") * 10.0`: float32(15.4321), |
| `lookup("main.Z_float64") * 0.1`: float64(0.1987654321), |
| `lookup("main.Z_int") + 1`: int(-20), |
| `lookup("main.Z_int16") - 10`: int16(-32331), |
| `lookup("main.Z_int32") / 10`: int32(-198765432), |
| `lookup("main.Z_int64") / 10`: int64(-901234567898765432), |
| `lookup("main.Z_int8") + 10`: int8(-111), |
| `lookup("main.Z_map")[-21]`: float32(3.54321), |
| `lookup("main.Z_map")[+21]`: float32(0), |
| `lookup("main.Z_map_empty")[21]`: float32(0), |
| `lookup("main.Z_slice")[1]`: uint8(108), |
| `lookup("main.Z_slice_2")[1]`: int8(121), |
| `lookup("main.Z_slice")[1:5][0:3][1]`: uint8('i'), |
| `lookup("main.Z_array")[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, |
| `(&lookup("main.Z_array"))[1:3:4]`: debug.Slice{debug.Array{42, 42, 2, 8}, 3}, |
| `lookup("main.Z_string") + "!"`: debug.String{13, `I'm a string!`}, |
| `lookup("main.Z_struct").a`: 21, |
| `(&lookup("main.Z_struct")).a`: 21, |
| `lookup("main.Z_uint")/10`: uint(2), |
| `lookup("main.Z_uint16")/10`: uint16(5432), |
| `lookup("main.Z_uint32")/10`: uint32(321765432), |
| `lookup("main.Z_uint64")/10`: uint64(1234567890098765432), |
| `lookup("main.Z_uint8")/10`: uint8(23), |
| `lookup("main.Z_pointer").a`: 21, |
| `(*lookup("main.Z_pointer")).a`: 21, |
| `(&*lookup("main.Z_pointer")).a`: 21, |
| `lookup("main.Z_pointer").b`: debug.String{2, `hi`}, |
| `(*lookup("main.Z_pointer")).b`: debug.String{2, `hi`}, |
| `(&*lookup("main.Z_pointer")).b`: debug.String{2, `hi`}, |
| `lookup("main.Z_map_nil")[32]`: float32(0), |
| `&lookup("main.Z_int16")`: debug.Pointer{42, 42}, |
| `&lookup("main.Z_array")[1]`: debug.Pointer{42, 42}, |
| `&lookup("main.Z_slice")[1]`: debug.Pointer{42, 42}, |
| `*&lookup("main.Z_int16")`: int16(-32321), |
| `*&*&*&*&lookup("main.Z_int16")`: int16(-32321), |
| `lookup("time.Local")`: debug.Pointer{42, 42}, |
| `5 + false`: nil, |
| ``: nil, |
| `x + ""`: nil, |
| `x / 0`: nil, |
| `0 / 0`: nil, |
| `'a' / ('a'-'a')`: nil, |
| `0.0 / 0.0`: nil, |
| `3i / 0.0`: nil, |
| `x % 0`: nil, |
| `0 % 0`: nil, |
| `'a' % ('a'-'a')`: nil, |
| `local_array[-2] + 1`: nil, |
| `local_array[22] + 1`: nil, |
| `local_slice[-2] + 1`: nil, |
| `local_slice[22] + 1`: nil, |
| `local_string[-2]`: nil, |
| `local_string[22]`: nil, |
| `"hello"[-2]`: nil, |
| `"hello"[22]`: nil, |
| `local_pointer_nil.a`: nil, |
| `(local_struct).c`: nil, |
| `(&local_struct).c`: nil, |
| `(*local_pointer).c`: nil, |
| `lookup("not a real symbol")`: nil, |
| `lookup("x")`: nil, |
| `lookup(x)`: nil, |
| `lookup(42)`: nil, |
| } |
| |
| func isHex(r uint8) bool { |
| switch { |
| case '0' <= r && r <= '9': |
| return true |
| case 'a' <= r && r <= 'f': |
| return true |
| case 'A' <= r && r <= 'F': |
| return true |
| default: |
| return false |
| } |
| } |
| |
| // structRE is used by matches to remove 'struct ' from type names, which is not |
| // output by every version of the compiler. |
| var structRE = regexp.MustCompile("struct *") |
| |
| // Check s matches the pattern in p. |
| // An 'X' in p greedily matches one or more hex characters in s. |
| func matches(p, s string) bool { |
| // Remove 'struct' and following spaces from s. |
| s = structRE.ReplaceAllString(s, "") |
| j := 0 |
| for i := 0; i < len(p); i++ { |
| if j == len(s) { |
| return false |
| } |
| c := p[i] |
| if c == 'X' { |
| if !isHex(s[j]) { |
| return false |
| } |
| for j < len(s) && isHex(s[j]) { |
| j++ |
| } |
| continue |
| } |
| if c != s[j] { |
| return false |
| } |
| j++ |
| } |
| return j == len(s) |
| } |
| |
| const ( |
| proxySrc = "golang.org/x/debug/cmd/debugproxy" |
| traceeSrc = "golang.org/x/debug/tests/peek/testdata" |
| ) |
| |
| var ( |
| // Locations of the proxy and tracee executables. |
| proxyBinary = "./debugproxy.out" |
| traceeBinary = "./tracee.out" |
| // Onces that ensure initProxy and initTracee are called at most once. |
| proxyOnce sync.Once |
| traceeOnce sync.Once |
| // Flags for setting the location of the proxy and tracee, so they don't need to be built. |
| proxyFlag = flag.String("proxy", "", "Location of debugproxy. If empty, proxy will be built.") |
| traceeFlag = flag.String("target", "", "Location of target. If empty, target will be built.") |
| // Executables this test has built, which will be removed on completion of the tests. |
| filesToRemove []string |
| ) |
| |
| func TestMain(m *testing.M) { |
| flag.Parse() |
| x := m.Run() |
| for _, f := range filesToRemove { |
| os.Remove(f) |
| } |
| os.Exit(x) |
| } |
| |
| func run(name string, args ...string) error { |
| cmd := exec.Command(name, args...) |
| cmd.Stdout = os.Stdout |
| cmd.Stderr = os.Stderr |
| return cmd.Run() |
| } |
| |
| func initProxy() { |
| if *proxyFlag != "" { |
| proxyBinary = *proxyFlag |
| remote.DebugproxyCmd = proxyBinary |
| return |
| } |
| if err := run("go", "build", "-o", proxyBinary, proxySrc); err != nil { |
| log.Fatalf("couldn't build proxy: %v", err) |
| } |
| filesToRemove = append(filesToRemove, proxyBinary) |
| remote.DebugproxyCmd = proxyBinary |
| } |
| |
| func initTracee() { |
| if *traceeFlag != "" { |
| traceeBinary = *traceeFlag |
| return |
| } |
| if err := run("go", "build", "-o", traceeBinary, traceeSrc); err != nil { |
| log.Fatalf("couldn't build target: %v", err) |
| } |
| filesToRemove = append(filesToRemove, traceeBinary) |
| } |
| |
| func TestLocalProgram(t *testing.T) { |
| traceeOnce.Do(initTracee) |
| prog, err := local.New(traceeBinary) |
| if err != nil { |
| t.Fatal("local.New:", err) |
| } |
| testProgram(t, prog) |
| } |
| |
| func TestRemoteProgram(t *testing.T) { |
| traceeOnce.Do(initTracee) |
| proxyOnce.Do(initProxy) |
| prog, err := remote.New("localhost", traceeBinary) |
| if err != nil { |
| t.Fatal("remote.New:", err) |
| } |
| testProgram(t, prog) |
| } |
| |
| func testProgram(t *testing.T, prog debug.Program) { |
| _, err := prog.Run("some", "arguments") |
| if err != nil { |
| log.Fatalf("Run: %v", err) |
| } |
| |
| pcs, err := prog.BreakpointAtFunction("main.foo") |
| if err != nil { |
| log.Fatalf("BreakpointAtFunction: %v", err) |
| } |
| fmt.Printf("breakpoints set at %x\n", pcs) |
| |
| _, err = prog.Resume() |
| if err != nil { |
| log.Fatalf("Resume: %v", err) |
| } |
| |
| gs, err := prog.Goroutines() |
| if err != nil { |
| t.Fatalf("Goroutines(): got error %s", err) |
| } |
| for _, g := range gs { |
| fmt.Println(g) |
| for _, f := range g.StackFrames { |
| fmt.Println(f) |
| } |
| } |
| |
| frames, err := prog.Frames(100) |
| if err != nil { |
| log.Fatalf("prog.Frames error: %v", err) |
| } |
| fmt.Printf("%#v\n", frames) |
| if len(frames) == 0 { |
| t.Fatalf("no stack frames returned") |
| } |
| if frames[0].Function != "main.foo" { |
| t.Errorf("function name: got %s expected main.foo", frames[0].Function) |
| } |
| if len(frames[0].Params) != 2 { |
| t.Errorf("got %d parameters, expected 2", len(frames[0].Params)) |
| } else { |
| x := frames[0].Params[0] |
| y := frames[0].Params[1] |
| if x.Name != "x" { |
| x, y = y, x |
| } |
| if x.Name != "x" { |
| t.Errorf("parameter name: got %s expected x", x.Name) |
| } |
| if y.Name != "y" { |
| t.Errorf("parameter name: got %s expected y", y.Name) |
| } |
| if val, err := prog.Value(x.Var); err != nil { |
| t.Errorf("value of x: %s", err) |
| } else if val != int16(42) { |
| t.Errorf("value of x: got %T(%v) expected int16(42)", val, val) |
| } |
| if val, err := prog.Value(y.Var); err != nil { |
| t.Errorf("value of y: %s", err) |
| } else if val != float32(1.5) { |
| t.Errorf("value of y: got %T(%v) expected float32(1.5)", val, val) |
| } |
| } |
| |
| varnames, err := prog.Eval(`re:main\.Z_.*`) |
| if err != nil { |
| log.Fatalf("prog.Eval error: %v", err) |
| } |
| |
| // Evaluate each of the variables found above, and check they match |
| // expectedVars. |
| seen := make(map[string]bool) |
| for _, v := range varnames { |
| val, err := prog.Eval("val:" + v) |
| if err != nil { |
| log.Fatalf("prog.Eval error for %s: %v", v, err) |
| } else { |
| fmt.Printf("%s = %v\n", v, val) |
| if seen[v] { |
| log.Fatalf("repeated variable %s\n", v) |
| } |
| seen[v] = true |
| if len(val) != 1 { |
| log.Fatalf("should be one value for %s\n", v) |
| } |
| expected, ok := expectedVars[v] |
| if !ok { |
| log.Fatalf("unexpected variable %s\n", v) |
| } else { |
| if !matches(expected, val[0]) { |
| log.Fatalf("expected %s = %s\n", v, expected) |
| } |
| } |
| } |
| } |
| for v, e := range expectedVars { |
| if !seen[v] { |
| log.Fatalf("didn't get %s = %s\n", v, e) |
| } |
| } |
| |
| // Remove the breakpoint at main.foo. |
| err = prog.DeleteBreakpoints(pcs) |
| if err != nil { |
| log.Fatalf("DeleteBreakpoints: %v", err) |
| } |
| |
| // Set a breakpoint at line 125, resume, and check we stopped there. |
| pcsLine125, err := prog.BreakpointAtLine("testdata/main.go", 125) |
| if err != nil { |
| t.Fatal("BreakpointAtLine:", err) |
| } |
| status, err := prog.Resume() |
| if err != nil { |
| log.Fatalf("Resume: %v", err) |
| } |
| stoppedAt := func(pcs []uint64) bool { |
| for _, pc := range pcs { |
| if status.PC == pc { |
| return true |
| } |
| } |
| return false |
| } |
| if !stoppedAt(pcsLine125) { |
| t.Errorf("stopped at %X; expected one of %X.", status.PC, pcsLine125) |
| } |
| |
| for k, v := range expectedEvaluate { |
| val, err := prog.Evaluate(k) |
| if v == nil { |
| if err == nil { |
| t.Errorf("got Evaluate(%s) = %v, expected error", k, val) |
| } |
| continue |
| } |
| if err != nil { |
| t.Errorf("Evaluate(%s): got error %s, expected %v", k, err, v) |
| continue |
| } |
| typ := reflect.TypeOf(v) |
| if typ != reflect.TypeOf(val) && typ != reflect.TypeOf(int(0)) && typ != reflect.TypeOf(uint(0)) { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) |
| continue |
| } |
| |
| // For types with fields like Address, TypeID, etc., we can't know the exact |
| // value, so we only test whether those fields are zero or not. |
| switch v := v.(type) { |
| default: |
| if v != val { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) |
| } |
| case debug.Array: |
| val := val.(debug.Array) |
| if v.ElementTypeID == 0 && val.ElementTypeID != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val) |
| } |
| if v.ElementTypeID != 0 && val.ElementTypeID == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val) |
| } |
| if v.Address == 0 && val.Address != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) |
| } |
| if v.Address != 0 && val.Address == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) |
| } |
| case debug.Slice: |
| val := val.(debug.Slice) |
| if v.ElementTypeID == 0 && val.ElementTypeID != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val) |
| } |
| if v.ElementTypeID != 0 && val.ElementTypeID == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val) |
| } |
| if v.Address == 0 && val.Address != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) |
| } |
| if v.Address != 0 && val.Address == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) |
| } |
| case debug.Map: |
| val := val.(debug.Map) |
| if v.TypeID == 0 && val.TypeID != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero TypeID", k, val) |
| } |
| if v.TypeID != 0 && val.TypeID == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero TypeID", k, val) |
| } |
| if v.Address == 0 && val.Address != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) |
| } |
| if v.Address != 0 && val.Address == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) |
| } |
| case debug.Pointer: |
| val := val.(debug.Pointer) |
| if v.TypeID == 0 && val.TypeID != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero TypeID", k, val) |
| } |
| if v.TypeID != 0 && val.TypeID == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero TypeID", k, val) |
| } |
| if v.Address == 0 && val.Address != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) |
| } |
| if v.Address != 0 && val.Address == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) |
| } |
| case debug.Channel: |
| val := val.(debug.Channel) |
| if v.ElementTypeID == 0 && val.ElementTypeID != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val) |
| } |
| if v.ElementTypeID != 0 && val.ElementTypeID == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val) |
| } |
| if v.Address == 0 && val.Address != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) |
| } |
| if v.Address != 0 && val.Address == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) |
| } |
| if v.Buffer == 0 && val.Buffer != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Buffer", k, val) |
| } |
| if v.Buffer != 0 && val.Buffer == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Buffer", k, val) |
| } |
| case debug.Struct: |
| val := val.(debug.Struct) |
| if len(v.Fields) != len(val.Fields) { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) |
| break |
| } |
| for i := range v.Fields { |
| a := v.Fields[i].Name |
| b := val.Fields[i].Name |
| if a != b { |
| t.Errorf("Evaluate(%s): field name mismatch: %s vs %s", k, a, b) |
| break |
| } |
| } |
| case debug.Func: |
| val := val.(debug.Func) |
| if v.Address == 0 && val.Address != 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val) |
| } |
| if v.Address != 0 && val.Address == 0 { |
| t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val) |
| } |
| case int: |
| // ints in a remote program can be returned as int32 or int64 |
| switch val := val.(type) { |
| case int32: |
| if val != int32(v) { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) |
| } |
| case int64: |
| if val != int64(v) { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) |
| } |
| default: |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) |
| } |
| case uint: |
| // uints in a remote program can be returned as uint32 or uint64 |
| switch val := val.(type) { |
| case uint32: |
| if val != uint32(v) { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) |
| } |
| case uint64: |
| if val != uint64(v) { |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v) |
| } |
| default: |
| t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v) |
| } |
| } |
| } |
| |
| // Evaluate a struct. |
| v := `lookup("main.Z_struct")` |
| val, err := prog.Evaluate(v) |
| if err != nil { |
| t.Fatalf("Evaluate: %s", err) |
| } |
| s, ok := val.(debug.Struct) |
| if !ok { |
| t.Fatalf("got Evaluate(%q) = %T(%v), expected debug.Struct", v, val, val) |
| } |
| // Check the values of its fields. |
| if len(s.Fields) != 2 { |
| t.Fatalf("got Evaluate(%q) = %+v, expected 2 fields", v, s) |
| } |
| if v0, err := prog.Value(s.Fields[0].Var); err != nil { |
| t.Errorf("Value: %s", err) |
| } else if v0 != int32(21) && v0 != int64(21) { |
| t.Errorf("Value: got %T(%v), expected 21", v0, v0) |
| } |
| if v1, err := prog.Value(s.Fields[1].Var); err != nil { |
| t.Errorf("Value: %s", err) |
| } else if v1 != (debug.String{2, "hi"}) { |
| t.Errorf("Value: got %T(%v), expected `hi`", v1, v1) |
| } |
| |
| // Remove the breakpoint at line 125, set a breakpoint at main.f1 and main.f2, |
| // then delete the breakpoint at main.f1. Resume, then check we stopped at |
| // main.f2. |
| err = prog.DeleteBreakpoints(pcsLine125) |
| if err != nil { |
| log.Fatalf("DeleteBreakpoints: %v", err) |
| } |
| pcs1, err := prog.BreakpointAtFunction("main.f1") |
| if err != nil { |
| log.Fatalf("BreakpointAtFunction: %v", err) |
| } |
| pcs2, err := prog.BreakpointAtFunction("main.f2") |
| if err != nil { |
| log.Fatalf("BreakpointAtFunction: %v", err) |
| } |
| err = prog.DeleteBreakpoints(pcs1) |
| if err != nil { |
| log.Fatalf("DeleteBreakpoints: %v", err) |
| } |
| status, err = prog.Resume() |
| if err != nil { |
| log.Fatalf("Resume: %v", err) |
| } |
| if !stoppedAt(pcs2) { |
| t.Errorf("stopped at %X; expected one of %X.", status.PC, pcs2) |
| } |
| |
| // Check we get the expected results calling VarByName then Value |
| // for the variables in expectedVarValues. |
| for name, exp := range expectedVarValues { |
| if v, err := prog.VarByName(name); err != nil { |
| t.Errorf("VarByName(%s): %s", name, err) |
| } else if val, err := prog.Value(v); err != nil { |
| t.Errorf("value of %s: %s", name, err) |
| } else if val != exp { |
| t.Errorf("value of %s: got %T(%v) want %T(%v)", name, val, val, exp, exp) |
| } |
| } |
| |
| // Check some error cases for VarByName and Value. |
| if _, err = prog.VarByName("not a real name"); err == nil { |
| t.Error("VarByName for invalid name: expected error") |
| } |
| if _, err = prog.Value(debug.Var{}); err == nil { |
| t.Error("value of invalid var: expected error") |
| } |
| if v, err := prog.VarByName("main.Z_int16"); err != nil { |
| t.Error("VarByName(main.Z_int16) error:", err) |
| } else { |
| v.Address = 0 |
| // v now has a valid type but a bad address. |
| _, err = prog.Value(v) |
| if err == nil { |
| t.Error("value of invalid location: expected error") |
| } |
| } |
| |
| // checkValue tests that we can get a Var for a variable with the given name, |
| // that we can then get the value of that Var, and that calling fn for that |
| // value succeeds. |
| checkValue := func(name string, fn func(val debug.Value) error) { |
| if v, err := prog.VarByName(name); err != nil { |
| t.Errorf("VarByName(%s): %s", name, err) |
| } else if val, err := prog.Value(v); err != nil { |
| t.Errorf("value of %s: %s", name, err) |
| } else if err := fn(val); err != nil { |
| t.Errorf("value of %s: %s", name, err) |
| } |
| } |
| |
| checkValue("main.Z_uintptr", func(val debug.Value) error { |
| if val != uint32(21) && val != uint64(21) { |
| // Z_uintptr should be an unsigned integer with size equal to the debugged |
| // program's address size. |
| return fmt.Errorf("got %T(%v) want 21", val, val) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_int", func(val debug.Value) error { |
| if val != int32(-21) && val != int64(-21) { |
| return fmt.Errorf("got %T(%v) want -21", val, val) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_uint", func(val debug.Value) error { |
| if val != uint32(21) && val != uint64(21) { |
| return fmt.Errorf("got %T(%v) want 21", val, val) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_pointer", func(val debug.Value) error { |
| if _, ok := val.(debug.Pointer); !ok { |
| return fmt.Errorf("got %T(%v) expected Pointer", val, val) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_pointer_nil", func(val debug.Value) error { |
| if p, ok := val.(debug.Pointer); !ok { |
| return fmt.Errorf("got %T(%v) expected Pointer", val, val) |
| } else if p.Address != 0 { |
| return fmt.Errorf("got %T(%v) expected nil pointer", val, val) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_array", func(val debug.Value) error { |
| a, ok := val.(debug.Array) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Array", val, val) |
| } |
| if a.Len() != 5 { |
| return fmt.Errorf("got array length %d expected 5", a.Len()) |
| } |
| expected := [5]int8{-121, 121, 3, 2, 1} |
| for i := uint64(0); i < 5; i++ { |
| if v, err := prog.Value(a.Element(i)); err != nil { |
| return fmt.Errorf("reading element %d: %s", i, err) |
| } else if v != expected[i] { |
| return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i]) |
| } |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_slice", func(val debug.Value) error { |
| s, ok := val.(debug.Slice) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Slice", val, val) |
| } |
| if s.Len() != 5 { |
| return fmt.Errorf("got slice length %d expected 5", s.Len()) |
| } |
| expected := []uint8{115, 108, 105, 99, 101} |
| for i := uint64(0); i < 5; i++ { |
| if v, err := prog.Value(s.Element(i)); err != nil { |
| return fmt.Errorf("reading element %d: %s", i, err) |
| } else if v != expected[i] { |
| return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i]) |
| } |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_map_empty", func(val debug.Value) error { |
| m, ok := val.(debug.Map) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Map", val, val) |
| } |
| if m.Length != 0 { |
| return fmt.Errorf("got map length %d expected 0", m.Length) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_map_nil", func(val debug.Value) error { |
| m, ok := val.(debug.Map) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Map", val, val) |
| } |
| if m.Length != 0 { |
| return fmt.Errorf("got map length %d expected 0", m.Length) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_map_3", func(val debug.Value) error { |
| m, ok := val.(debug.Map) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Map", val, val) |
| } |
| if m.Length != 2 { |
| return fmt.Errorf("got map length %d expected 2", m.Length) |
| } |
| keyVar0, valVar0, err := prog.MapElement(m, 0) |
| if err != nil { |
| return err |
| } |
| keyVar1, valVar1, err := prog.MapElement(m, 1) |
| if err != nil { |
| return err |
| } |
| key0, err := prog.Value(keyVar0) |
| if err != nil { |
| return err |
| } |
| key1, err := prog.Value(keyVar1) |
| if err != nil { |
| return err |
| } |
| val0, err := prog.Value(valVar0) |
| if err != nil { |
| return err |
| } |
| val1, err := prog.Value(valVar1) |
| if err != nil { |
| return err |
| } |
| // The map should contain 1024,1 and 512,-1 in some order. |
| ok1 := key0 == int16(1024) && val0 == int8(1) && key1 == int16(512) && val1 == int8(-1) |
| ok2 := key1 == int16(1024) && val1 == int8(1) && key0 == int16(512) && val0 == int8(-1) |
| if !ok1 && !ok2 { |
| return fmt.Errorf("got values (%d,%d) and (%d,%d), expected (1024,1) and (512,-1) in some order", key0, val0, key1, val1) |
| } |
| _, _, err = prog.MapElement(m, 2) |
| if err == nil { |
| return fmt.Errorf("MapElement: reading at a bad index succeeded, expected error") |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_string", func(val debug.Value) error { |
| s, ok := val.(debug.String) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected String", val, val) |
| } |
| if s.Length != 12 { |
| return fmt.Errorf("got string length %d expected 12", s.Length) |
| } |
| expected := "I'm a string" |
| if s.String != expected { |
| return fmt.Errorf("got %s expected %s", s.String, expected) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_channel", func(val debug.Value) error { |
| c, ok := val.(debug.Channel) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Channel", val, val) |
| } |
| if c.Buffer == 0 { |
| return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer) |
| } |
| if c.Length != 0 { |
| return fmt.Errorf("got length %d expected 0", c.Length) |
| } |
| if c.Capacity != 0 { |
| return fmt.Errorf("got capacity %d expected 0", c.Capacity) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_channel_2", func(val debug.Value) error { |
| c, ok := val.(debug.Channel) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Channel", val, val) |
| } |
| if c.Buffer == 0 { |
| return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer) |
| } |
| if c.Length != 0 { |
| return fmt.Errorf("got length %d expected 0", c.Length) |
| } |
| if c.Capacity != 0 { |
| return fmt.Errorf("got capacity %d expected 0", c.Capacity) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_channel_nil", func(val debug.Value) error { |
| c, ok := val.(debug.Channel) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Channel", val, val) |
| } |
| if c.Buffer != 0 { |
| return fmt.Errorf("got buffer address %d expected 0", c.Buffer) |
| } |
| if c.Length != 0 { |
| return fmt.Errorf("got length %d expected 0", c.Length) |
| } |
| if c.Capacity != 0 { |
| return fmt.Errorf("got capacity %d expected 0", c.Capacity) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_channel_buffered", func(val debug.Value) error { |
| c, ok := val.(debug.Channel) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Channel", val, val) |
| } |
| if c.Buffer == 0 { |
| return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer) |
| } |
| if c.Length != 6 { |
| return fmt.Errorf("got length %d expected 6", c.Length) |
| } |
| if c.Capacity != 10 { |
| return fmt.Errorf("got capacity %d expected 10", c.Capacity) |
| } |
| if c.Stride != 2 { |
| return fmt.Errorf("got stride %d expected 2", c.Stride) |
| } |
| expected := []int16{8, 9, 10, 11, 12, 13} |
| for i := uint64(0); i < 6; i++ { |
| if v, err := prog.Value(c.Element(i)); err != nil { |
| return fmt.Errorf("reading element %d: %s", i, err) |
| } else if v != expected[i] { |
| return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i]) |
| } |
| } |
| v := c.Element(6) |
| if v.Address != 0 { |
| return fmt.Errorf("invalid element returned Var with address %d, expected 0", v.Address) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_func_bar", func(val debug.Value) error { |
| f, ok := val.(debug.Func) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Func", val, val) |
| } |
| if f.Address == 0 { |
| return fmt.Errorf("got func address %d expected nonzero", f.Address) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_func_nil", func(val debug.Value) error { |
| f, ok := val.(debug.Func) |
| if !ok { |
| return fmt.Errorf("got %T(%v) expected Func", val, val) |
| } |
| if f.Address != 0 { |
| return fmt.Errorf("got func address %d expected zero", f.Address) |
| } |
| return nil |
| }) |
| } |