| // 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. |
| |
| // Demo program that starts another program and calls Ogle library functions |
| // to debug it. |
| |
| package ogler |
| |
| import ( |
| "fmt" |
| "log" |
| "os" |
| "os/exec" |
| "testing" |
| |
| "golang.org/x/debug/ogle/program" |
| "golang.org/x/debug/ogle/program/client" |
| "golang.org/x/debug/ogle/program/local" |
| ) |
| |
| 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), |
| } |
| |
| 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 int 0xX)`, |
| `main.Z_channel_buffered`: `(chan int 0xX [0/10])`, |
| `main.Z_channel_nil`: `(chan int <nil>)`, |
| `main.Z_array_of_empties`: `[2]struct struct {}{struct struct {} {}, (struct struct {} 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_int`: `-21`, |
| `main.Z_int16`: `-32321`, |
| `main.Z_int32`: `-1987654321`, |
| `main.Z_int64`: `-9012345678987654321`, |
| `main.Z_int8`: `-121`, |
| `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_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`: `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`, |
| } |
| |
| 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 |
| } |
| } |
| |
| // 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 { |
| 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) |
| } |
| |
| func run(name string, args ...string) error { |
| cmd := exec.Command(name, args...) |
| cmd.Stdout = os.Stdout |
| cmd.Stderr = os.Stderr |
| return cmd.Run() |
| } |
| |
| const ( |
| proxySrc = "golang.org/x/debug/ogle/cmd/ogleproxy" |
| proxyBinary = "./ogleproxy" |
| traceeSrc = "golang.org/x/debug/ogle/demo/tracee" |
| traceeBinary = "./tracee" |
| ) |
| |
| func TestMain(m *testing.M) { |
| os.Exit(buildAndRunTests(m)) |
| } |
| |
| func buildAndRunTests(m *testing.M) int { |
| if err := run("go", "build", "-o", proxyBinary, proxySrc); err != nil { |
| fmt.Println(err) |
| return 1 |
| } |
| client.OgleproxyCmd = proxyBinary |
| defer os.Remove(proxyBinary) |
| if err := run("go", "build", "-o", traceeBinary, traceeSrc); err != nil { |
| fmt.Println(err) |
| return 1 |
| } |
| defer os.Remove(traceeBinary) |
| return m.Run() |
| } |
| |
| func TestLocalProgram(t *testing.T) { |
| prog, err := local.New(traceeBinary) |
| if err != nil { |
| t.Fatal("local.New:", err) |
| } |
| testProgram(t, prog) |
| } |
| |
| func TestRemoteProgram(t *testing.T) { |
| prog, err := client.New("localhost", traceeBinary) |
| if err != nil { |
| t.Fatal("client.New:", err) |
| } |
| testProgram(t, prog) |
| } |
| |
| func testProgram(t *testing.T, prog program.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) |
| } |
| |
| 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 80, resume, and check we stopped there. |
| pcsLine80, err := prog.BreakpointAtLine("tracee/main.go", 80) |
| 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(pcsLine80) { |
| t.Errorf("stopped at %X; expected one of %X.", status.PC, pcsLine80) |
| } |
| |
| // Remove the breakpoint at line 80, 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(pcsLine80) |
| 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(program.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 program.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 program.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 program.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 program.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 program.Value) error { |
| if _, ok := val.(program.Pointer); !ok { |
| return fmt.Errorf("got %T(%v) expected Pointer", val, val) |
| } |
| return nil |
| }) |
| |
| checkValue("main.Z_pointer_nil", func(val program.Value) error { |
| if p, ok := val.(program.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 program.Value) error { |
| a, ok := val.(program.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 |
| }) |
| } |