Home | History | Annotate | Download | only in runtime
      1 // Copyright 2016 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package runtime_test
      6 
      7 import (
      8 	"internal/testenv"
      9 	"io/ioutil"
     10 	"os"
     11 	"os/exec"
     12 	"path/filepath"
     13 	"runtime"
     14 	"strings"
     15 	"testing"
     16 )
     17 
     18 var lldbPath string
     19 
     20 func checkLldbPython(t *testing.T) {
     21 	cmd := exec.Command("lldb", "-P")
     22 	out, err := cmd.CombinedOutput()
     23 	if err != nil {
     24 		t.Skipf("skipping due to issue running lldb: %v\n%s", err, out)
     25 	}
     26 	lldbPath = strings.TrimSpace(string(out))
     27 
     28 	cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath)
     29 	out, err = cmd.CombinedOutput()
     30 
     31 	if err != nil {
     32 		t.Skipf("skipping due to issue running python: %v\n%s", err, out)
     33 	}
     34 	if string(out) != "go lldb python support\n" {
     35 		t.Skipf("skipping due to lack of python lldb support: %s", out)
     36 	}
     37 
     38 	if runtime.GOOS == "darwin" {
     39 		// Try to see if we have debugging permissions.
     40 		cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status")
     41 		out, err = cmd.CombinedOutput()
     42 		if err != nil {
     43 			t.Skipf("DevToolsSecurity failed: %v", err)
     44 		} else if !strings.Contains(string(out), "enabled") {
     45 			t.Skip(string(out))
     46 		}
     47 		cmd = exec.Command("/usr/bin/groups")
     48 		out, err = cmd.CombinedOutput()
     49 		if err != nil {
     50 			t.Skipf("groups failed: %v", err)
     51 		} else if !strings.Contains(string(out), "_developer") {
     52 			t.Skip("Not in _developer group")
     53 		}
     54 	}
     55 }
     56 
     57 const lldbHelloSource = `
     58 package main
     59 import "fmt"
     60 func main() {
     61 	mapvar := make(map[string]string,5)
     62 	mapvar["abc"] = "def"
     63 	mapvar["ghi"] = "jkl"
     64 	intvar := 42
     65 	ptrvar := &intvar
     66 	fmt.Println("hi") // line 10
     67 	_ = ptrvar
     68 }
     69 `
     70 
     71 const lldbScriptSource = `
     72 import sys
     73 sys.path.append(sys.argv[1])
     74 import lldb
     75 import os
     76 
     77 TIMEOUT_SECS = 5
     78 
     79 debugger = lldb.SBDebugger.Create()
     80 debugger.SetAsync(True)
     81 target = debugger.CreateTargetWithFileAndArch("a.exe", None)
     82 if target:
     83   print "Created target"
     84   main_bp = target.BreakpointCreateByLocation("main.go", 10)
     85   if main_bp:
     86     print "Created breakpoint"
     87   process = target.LaunchSimple(None, None, os.getcwd())
     88   if process:
     89     print "Process launched"
     90     listener = debugger.GetListener()
     91     process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged)
     92     while True:
     93       event = lldb.SBEvent()
     94       if listener.WaitForEvent(TIMEOUT_SECS, event):
     95         if lldb.SBProcess.GetRestartedFromEvent(event):
     96           continue
     97         state = process.GetState()
     98         if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]:
     99           continue
    100       else:
    101         print "Timeout launching"
    102       break
    103     if state == lldb.eStateStopped:
    104       for t in process.threads:
    105         if t.GetStopReason() == lldb.eStopReasonBreakpoint:
    106           print "Hit breakpoint"
    107           frame = t.GetFrameAtIndex(0)
    108           if frame:
    109             if frame.line_entry:
    110               print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line)
    111             if frame.function:
    112               print "Stopped in %s" % (frame.function.name,)
    113             var = frame.FindVariable('intvar')
    114             if var:
    115               print "intvar = %s" % (var.GetValue(),)
    116             else:
    117               print "no intvar"
    118     else:
    119       print "Process state", state
    120     process.Destroy()
    121 else:
    122   print "Failed to create target a.exe"
    123 
    124 lldb.SBDebugger.Destroy(debugger)
    125 sys.exit()
    126 `
    127 
    128 const expectedLldbOutput = `Created target
    129 Created breakpoint
    130 Process launched
    131 Hit breakpoint
    132 Stopped at main.go:10
    133 Stopped in main.main
    134 intvar = 42
    135 `
    136 
    137 func TestLldbPython(t *testing.T) {
    138 	testenv.MustHaveGoBuild(t)
    139 	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
    140 		t.Skip("gdb test can fail with GOROOT_FINAL pending")
    141 	}
    142 
    143 	checkLldbPython(t)
    144 
    145 	dir, err := ioutil.TempDir("", "go-build")
    146 	if err != nil {
    147 		t.Fatalf("failed to create temp directory: %v", err)
    148 	}
    149 	defer os.RemoveAll(dir)
    150 
    151 	src := filepath.Join(dir, "main.go")
    152 	err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644)
    153 	if err != nil {
    154 		t.Fatalf("failed to create file: %v", err)
    155 	}
    156 
    157 	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe")
    158 	cmd.Dir = dir
    159 	out, err := cmd.CombinedOutput()
    160 	if err != nil {
    161 		t.Fatalf("building source %v\n%s", err, out)
    162 	}
    163 
    164 	src = filepath.Join(dir, "script.py")
    165 	err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755)
    166 	if err != nil {
    167 		t.Fatalf("failed to create script: %v", err)
    168 	}
    169 
    170 	cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath)
    171 	cmd.Dir = dir
    172 	got, _ := cmd.CombinedOutput()
    173 
    174 	if string(got) != expectedLldbOutput {
    175 		if strings.Contains(string(got), "Timeout launching") {
    176 			t.Skip("Timeout launching")
    177 		}
    178 		t.Fatalf("Unexpected lldb output:\n%s", got)
    179 	}
    180 }
    181