github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/runtime/runtime-lldb_test.go (about)

     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  	"debug/elf"
     9  	"debug/macho"
    10  	"encoding/binary"
    11  	"internal/testenv"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  )
    21  
    22  var lldbPath string
    23  
    24  func checkLldbPython(t *testing.T) {
    25  	cmd := exec.Command("lldb", "-P")
    26  	out, err := cmd.CombinedOutput()
    27  	if err != nil {
    28  		t.Skipf("skipping due to issue running lldb: %v\n%s", err, out)
    29  	}
    30  	lldbPath = strings.TrimSpace(string(out))
    31  
    32  	cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath)
    33  	out, err = cmd.CombinedOutput()
    34  
    35  	if err != nil {
    36  		t.Skipf("skipping due to issue running python: %v\n%s", err, out)
    37  	}
    38  	if string(out) != "go lldb python support\n" {
    39  		t.Skipf("skipping due to lack of python lldb support: %s", out)
    40  	}
    41  
    42  	if runtime.GOOS == "darwin" {
    43  		// Try to see if we have debugging permissions.
    44  		cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status")
    45  		out, err = cmd.CombinedOutput()
    46  		if err != nil {
    47  			t.Skipf("DevToolsSecurity failed: %v", err)
    48  		} else if !strings.Contains(string(out), "enabled") {
    49  			t.Skip(string(out))
    50  		}
    51  		cmd = exec.Command("/usr/bin/groups")
    52  		out, err = cmd.CombinedOutput()
    53  		if err != nil {
    54  			t.Skipf("groups failed: %v", err)
    55  		} else if !strings.Contains(string(out), "_developer") {
    56  			t.Skip("Not in _developer group")
    57  		}
    58  	}
    59  }
    60  
    61  const lldbHelloSource = `
    62  package main
    63  import "fmt"
    64  func main() {
    65  	mapvar := make(map[string]string,5)
    66  	mapvar["abc"] = "def"
    67  	mapvar["ghi"] = "jkl"
    68  	intvar := 42
    69  	ptrvar := &intvar
    70  	fmt.Println("hi") // line 10
    71  	_ = ptrvar
    72  }
    73  `
    74  
    75  const lldbScriptSource = `
    76  import sys
    77  sys.path.append(sys.argv[1])
    78  import lldb
    79  import os
    80  
    81  TIMEOUT_SECS = 5
    82  
    83  debugger = lldb.SBDebugger.Create()
    84  debugger.SetAsync(True)
    85  target = debugger.CreateTargetWithFileAndArch("a.exe", None)
    86  if target:
    87    print "Created target"
    88    main_bp = target.BreakpointCreateByLocation("main.go", 10)
    89    if main_bp:
    90      print "Created breakpoint"
    91    process = target.LaunchSimple(None, None, os.getcwd())
    92    if process:
    93      print "Process launched"
    94      listener = debugger.GetListener()
    95      process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged)
    96      while True:
    97        event = lldb.SBEvent()
    98        if listener.WaitForEvent(TIMEOUT_SECS, event):
    99          if lldb.SBProcess.GetRestartedFromEvent(event):
   100            continue
   101          state = process.GetState()
   102          if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]:
   103            continue
   104        else:
   105          print "Timeout launching"
   106        break
   107      if state == lldb.eStateStopped:
   108        for t in process.threads:
   109          if t.GetStopReason() == lldb.eStopReasonBreakpoint:
   110            print "Hit breakpoint"
   111            frame = t.GetFrameAtIndex(0)
   112            if frame:
   113              if frame.line_entry:
   114                print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line)
   115              if frame.function:
   116                print "Stopped in %s" % (frame.function.name,)
   117              var = frame.FindVariable('intvar')
   118              if var:
   119                print "intvar = %s" % (var.GetValue(),)
   120              else:
   121                print "no intvar"
   122      else:
   123        print "Process state", state
   124      process.Destroy()
   125  else:
   126    print "Failed to create target a.exe"
   127  
   128  lldb.SBDebugger.Destroy(debugger)
   129  sys.exit()
   130  `
   131  
   132  const expectedLldbOutput = `Created target
   133  Created breakpoint
   134  Process launched
   135  Hit breakpoint
   136  Stopped at main.go:10
   137  Stopped in main.main
   138  intvar = 42
   139  `
   140  
   141  func TestLldbPython(t *testing.T) {
   142  	testenv.MustHaveGoBuild(t)
   143  	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
   144  		t.Skip("gdb test can fail with GOROOT_FINAL pending")
   145  	}
   146  
   147  	checkLldbPython(t)
   148  
   149  	dir, err := ioutil.TempDir("", "go-build")
   150  	if err != nil {
   151  		t.Fatalf("failed to create temp directory: %v", err)
   152  	}
   153  	defer os.RemoveAll(dir)
   154  
   155  	src := filepath.Join(dir, "main.go")
   156  	err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644)
   157  	if err != nil {
   158  		t.Fatalf("failed to create file: %v", err)
   159  	}
   160  
   161  	cmd := exec.Command("go", "build", "-gcflags", "-N -l", "-o", "a.exe")
   162  	cmd.Dir = dir
   163  	out, err := cmd.CombinedOutput()
   164  	if err != nil {
   165  		t.Fatalf("building source %v\n%s", err, out)
   166  	}
   167  
   168  	src = filepath.Join(dir, "script.py")
   169  	err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755)
   170  	if err != nil {
   171  		t.Fatalf("failed to create script: %v", err)
   172  	}
   173  
   174  	cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath)
   175  	cmd.Dir = dir
   176  	got, _ := cmd.CombinedOutput()
   177  
   178  	if string(got) != expectedLldbOutput {
   179  		if strings.Contains(string(got), "Timeout launching") {
   180  			t.Skip("Timeout launching")
   181  		}
   182  		t.Fatalf("Unexpected lldb output:\n%s", got)
   183  	}
   184  }
   185  
   186  // Check that aranges are valid even when lldb isn't installed.
   187  func TestDwarfAranges(t *testing.T) {
   188  	testenv.MustHaveGoBuild(t)
   189  	dir, err := ioutil.TempDir("", "go-build")
   190  	if err != nil {
   191  		t.Fatalf("failed to create temp directory: %v", err)
   192  	}
   193  	defer os.RemoveAll(dir)
   194  
   195  	src := filepath.Join(dir, "main.go")
   196  	err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644)
   197  	if err != nil {
   198  		t.Fatalf("failed to create file: %v", err)
   199  	}
   200  
   201  	cmd := exec.Command("go", "build", "-o", "a.exe")
   202  	cmd.Dir = dir
   203  	out, err := cmd.CombinedOutput()
   204  	if err != nil {
   205  		t.Fatalf("building source %v\n%s", err, out)
   206  	}
   207  
   208  	filename := filepath.Join(dir, "a.exe")
   209  	if f, err := elf.Open(filename); err == nil {
   210  		sect := f.Section(".debug_aranges")
   211  		if sect == nil {
   212  			t.Fatal("Missing aranges section")
   213  		}
   214  		verifyAranges(t, f.ByteOrder, sect.Open())
   215  	} else if f, err := macho.Open(filename); err == nil {
   216  		sect := f.Section("__debug_aranges")
   217  		if sect == nil {
   218  			t.Fatal("Missing aranges section")
   219  		}
   220  		verifyAranges(t, f.ByteOrder, sect.Open())
   221  	} else {
   222  		t.Skip("Not an elf or macho binary.")
   223  	}
   224  }
   225  
   226  func verifyAranges(t *testing.T, byteorder binary.ByteOrder, data io.ReadSeeker) {
   227  	var header struct {
   228  		UnitLength  uint32 // does not include the UnitLength field
   229  		Version     uint16
   230  		Offset      uint32
   231  		AddressSize uint8
   232  		SegmentSize uint8
   233  	}
   234  	for {
   235  		offset, err := data.Seek(0, 1)
   236  		if err != nil {
   237  			t.Fatalf("Seek error: %v", err)
   238  		}
   239  		if err = binary.Read(data, byteorder, &header); err == io.EOF {
   240  			return
   241  		} else if err != nil {
   242  			t.Fatalf("Error reading arange header: %v", err)
   243  		}
   244  		tupleSize := int64(header.SegmentSize) + 2*int64(header.AddressSize)
   245  		lastTupleOffset := offset + int64(header.UnitLength) + 4 - tupleSize
   246  		if lastTupleOffset%tupleSize != 0 {
   247  			t.Fatalf("Invalid arange length %d, (addr %d, seg %d)", header.UnitLength, header.AddressSize, header.SegmentSize)
   248  		}
   249  		if _, err = data.Seek(lastTupleOffset, 0); err != nil {
   250  			t.Fatalf("Seek error: %v", err)
   251  		}
   252  		buf := make([]byte, tupleSize)
   253  		if n, err := data.Read(buf); err != nil || int64(n) < tupleSize {
   254  			t.Fatalf("Read error: %v", err)
   255  		}
   256  		for _, val := range buf {
   257  			if val != 0 {
   258  				t.Fatalf("Invalid terminator")
   259  			}
   260  		}
   261  	}
   262  }