github.com/aloncn/graphics-go@v0.0.1/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 }