| // Copyright 2016 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 runtime_test |
| |
| import ( |
| "internal/testenv" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "regexp" |
| "runtime" |
| "strings" |
| "testing" |
| ) |
| |
| var lldbPath string |
| |
| func checkLldbPython(t *testing.T) { |
| cmd := exec.Command("lldb", "-P") |
| out, err := cmd.CombinedOutput() |
| if err != nil { |
| t.Skipf("skipping due to issue running lldb: %v\n%s", err, out) |
| } |
| lldbPath = strings.TrimSpace(string(out)) |
| |
| cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath) |
| out, err = cmd.CombinedOutput() |
| |
| if err != nil { |
| t.Skipf("skipping due to issue running python: %v\n%s", err, out) |
| } |
| if string(out) != "go lldb python support\n" { |
| t.Skipf("skipping due to lack of python lldb support: %s", out) |
| } |
| |
| if runtime.GOOS == "darwin" { |
| // Try to see if we have debugging permissions. |
| cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status") |
| out, err = cmd.CombinedOutput() |
| if err != nil { |
| t.Skipf("DevToolsSecurity failed: %v", err) |
| } else if !strings.Contains(string(out), "enabled") { |
| t.Skip(string(out)) |
| } |
| cmd = exec.Command("/usr/bin/groups") |
| out, err = cmd.CombinedOutput() |
| if err != nil { |
| t.Skipf("groups failed: %v", err) |
| } else if !strings.Contains(string(out), "_developer") { |
| t.Skip("Not in _developer group") |
| } |
| } |
| } |
| |
| const lldbHelloSource = ` |
| package main |
| import "fmt" |
| func main() { |
| mapvar := make(map[string]string,5) |
| mapvar["abc"] = "def" |
| mapvar["ghi"] = "jkl" |
| intvar := 42 |
| ptrvar := &intvar |
| fmt.Println("hi") // line 10 |
| _ = ptrvar |
| } |
| ` |
| |
| const lldbScriptSource = ` |
| import sys |
| sys.path.append(sys.argv[1]) |
| import lldb |
| import os |
| |
| TIMEOUT_SECS = 5 |
| |
| debugger = lldb.SBDebugger.Create() |
| debugger.SetAsync(True) |
| target = debugger.CreateTargetWithFileAndArch("a.exe", None) |
| if target: |
| print "Created target" |
| main_bp = target.BreakpointCreateByLocation("main.go", 10) |
| if main_bp.GetNumLocations() != 0: |
| print "Created breakpoint" |
| else: |
| # This happens if lldb can't read the program's DWARF. See https://golang.org/issue/25925. |
| print "SKIP: no matching locations for breakpoint" |
| exit(1) |
| process = target.LaunchSimple(None, None, os.getcwd()) |
| if process: |
| print "Process launched" |
| listener = debugger.GetListener() |
| process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) |
| while True: |
| event = lldb.SBEvent() |
| if listener.WaitForEvent(TIMEOUT_SECS, event): |
| if lldb.SBProcess.GetRestartedFromEvent(event): |
| continue |
| state = process.GetState() |
| if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]: |
| continue |
| else: |
| print "SKIP: Timeout launching" |
| break |
| if state == lldb.eStateStopped: |
| for t in process.threads: |
| if t.GetStopReason() == lldb.eStopReasonBreakpoint: |
| print "Hit breakpoint" |
| frame = t.GetFrameAtIndex(0) |
| if frame: |
| if frame.line_entry: |
| print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line) |
| if frame.function: |
| print "Stopped in %s" % (frame.function.name,) |
| var = frame.FindVariable('intvar') |
| if var: |
| print "intvar = %s" % (var.GetValue(),) |
| else: |
| print "no intvar" |
| else: |
| print "Process state", state |
| process.Destroy() |
| else: |
| print "Failed to create target a.exe" |
| |
| lldb.SBDebugger.Destroy(debugger) |
| sys.exit() |
| ` |
| |
| const expectedLldbOutput = `Created target |
| Created breakpoint |
| Process launched |
| Hit breakpoint |
| Stopped at main.go:10 |
| Stopped in main.main |
| intvar = 42 |
| ` |
| |
| func TestLldbPython(t *testing.T) { |
| testenv.MustHaveGoBuild(t) |
| if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final { |
| t.Skip("gdb test can fail with GOROOT_FINAL pending") |
| } |
| |
| checkLldbPython(t) |
| |
| dir, err := ioutil.TempDir("", "go-build") |
| if err != nil { |
| t.Fatalf("failed to create temp directory: %v", err) |
| } |
| defer os.RemoveAll(dir) |
| |
| src := filepath.Join(dir, "main.go") |
| err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644) |
| if err != nil { |
| t.Fatalf("failed to create file: %v", err) |
| } |
| |
| cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe") |
| cmd.Dir = dir |
| out, err := cmd.CombinedOutput() |
| if err != nil { |
| t.Fatalf("building source %v\n%s", err, out) |
| } |
| |
| src = filepath.Join(dir, "script.py") |
| err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755) |
| if err != nil { |
| t.Fatalf("failed to create script: %v", err) |
| } |
| |
| cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath) |
| cmd.Dir = dir |
| got, _ := cmd.CombinedOutput() |
| |
| if string(got) != expectedLldbOutput { |
| skipReason := regexp.MustCompile("SKIP: .*\n").Find(got) |
| if skipReason != nil { |
| t.Skip(string(skipReason)) |
| } |
| t.Fatalf("Unexpected lldb output:\n%s", got) |
| } |
| } |