github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/misc/ios/go_darwin_arm_exec.go (about) 1 // Copyright 2015 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 // This program can be used as go_darwin_arm_exec by the Go tool. 6 // It executes binaries on an iOS device using the XCode toolchain 7 // and the ios-deploy program: https://github.com/phonegap/ios-deploy 8 // 9 // This script supports an extra flag, -lldb, that pauses execution 10 // just before the main program begins and allows the user to control 11 // the remote lldb session. This flag is appended to the end of the 12 // script's arguments and is not passed through to the underlying 13 // binary. 14 // 15 // This script requires that three environment variables be set: 16 // GOIOS_DEV_ID: The codesigning developer id or certificate identifier 17 // GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids. 18 // GOIOS_TEAM_ID: The team id that owns the app id prefix. 19 // $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these. 20 package main 21 22 import ( 23 "bytes" 24 "encoding/xml" 25 "errors" 26 "fmt" 27 "go/build" 28 "io" 29 "io/ioutil" 30 "log" 31 "net" 32 "os" 33 "os/exec" 34 "os/signal" 35 "path/filepath" 36 "runtime" 37 "strings" 38 "syscall" 39 "time" 40 ) 41 42 const debug = false 43 44 var tmpdir string 45 46 var ( 47 devID string 48 appID string 49 teamID string 50 bundleID string 51 deviceID string 52 ) 53 54 // lock is a file lock to serialize iOS runs. It is global to avoid the 55 // garbage collector finalizing it, closing the file and releasing the 56 // lock prematurely. 57 var lock *os.File 58 59 func main() { 60 log.SetFlags(0) 61 log.SetPrefix("go_darwin_arm_exec: ") 62 if debug { 63 log.Println(strings.Join(os.Args, " ")) 64 } 65 if len(os.Args) < 2 { 66 log.Fatal("usage: go_darwin_arm_exec a.out") 67 } 68 69 // e.g. B393DDEB490947F5A463FD074299B6C0AXXXXXXX 70 devID = getenv("GOIOS_DEV_ID") 71 72 // e.g. Z8B3JBXXXX.org.golang.sample, Z8B3JBXXXX prefix is available at 73 // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID. 74 appID = getenv("GOIOS_APP_ID") 75 76 // e.g. Z8B3JBXXXX, available at 77 // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID. 78 teamID = getenv("GOIOS_TEAM_ID") 79 80 // Device IDs as listed with ios-deploy -c. 81 deviceID = os.Getenv("GOIOS_DEVICE_ID") 82 83 parts := strings.SplitN(appID, ".", 2) 84 // For compatibility with the old builders, use a fallback bundle ID 85 bundleID = "golang.gotest" 86 if len(parts) == 2 { 87 bundleID = parts[1] 88 } 89 90 exitCode, err := runMain() 91 if err != nil { 92 log.Fatalf("%v\n", err) 93 } 94 os.Exit(exitCode) 95 } 96 97 func runMain() (int, error) { 98 var err error 99 tmpdir, err = ioutil.TempDir("", "go_darwin_arm_exec_") 100 if err != nil { 101 return 1, err 102 } 103 if !debug { 104 defer os.RemoveAll(tmpdir) 105 } 106 107 appdir := filepath.Join(tmpdir, "gotest.app") 108 os.RemoveAll(appdir) 109 110 if err := assembleApp(appdir, os.Args[1]); err != nil { 111 return 1, err 112 } 113 114 // This wrapper uses complicated machinery to run iOS binaries. It 115 // works, but only when running one binary at a time. 116 // Use a file lock to make sure only one wrapper is running at a time. 117 // 118 // The lock file is never deleted, to avoid concurrent locks on distinct 119 // files with the same path. 120 lockName := filepath.Join(os.TempDir(), "go_darwin_arm_exec-"+deviceID+".lock") 121 lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666) 122 if err != nil { 123 return 1, err 124 } 125 if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil { 126 return 1, err 127 } 128 129 if err := uninstall(bundleID); err != nil { 130 return 1, err 131 } 132 133 if err := install(appdir); err != nil { 134 return 1, err 135 } 136 137 if err := mountDevImage(); err != nil { 138 return 1, err 139 } 140 141 // Kill any hanging debug bridges that might take up port 3222. 142 exec.Command("killall", "idevicedebugserverproxy").Run() 143 144 closer, err := startDebugBridge() 145 if err != nil { 146 return 1, err 147 } 148 defer closer() 149 150 if err := run(appdir, bundleID, os.Args[2:]); err != nil { 151 // If the lldb driver completed with an exit code, use that. 152 if err, ok := err.(*exec.ExitError); ok { 153 if ws, ok := err.Sys().(interface{ ExitStatus() int }); ok { 154 return ws.ExitStatus(), nil 155 } 156 } 157 return 1, err 158 } 159 return 0, nil 160 } 161 162 func getenv(envvar string) string { 163 s := os.Getenv(envvar) 164 if s == "" { 165 log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar) 166 } 167 return s 168 } 169 170 func assembleApp(appdir, bin string) error { 171 if err := os.MkdirAll(appdir, 0755); err != nil { 172 return err 173 } 174 175 if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil { 176 return err 177 } 178 179 pkgpath, err := copyLocalData(appdir) 180 if err != nil { 181 return err 182 } 183 184 entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist") 185 if err := ioutil.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil { 186 return err 187 } 188 if err := ioutil.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil { 189 return err 190 } 191 if err := ioutil.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil { 192 return err 193 } 194 195 cmd := exec.Command( 196 "codesign", 197 "-f", 198 "-s", devID, 199 "--entitlements", entitlementsPath, 200 appdir, 201 ) 202 if debug { 203 log.Println(strings.Join(cmd.Args, " ")) 204 } 205 cmd.Stdout = os.Stdout 206 cmd.Stderr = os.Stderr 207 if err := cmd.Run(); err != nil { 208 return fmt.Errorf("codesign: %v", err) 209 } 210 return nil 211 } 212 213 // mountDevImage ensures a developer image is mounted on the device. 214 // The image contains the device lldb server for idevicedebugserverproxy 215 // to connect to. 216 func mountDevImage() error { 217 // Check for existing mount. 218 cmd := idevCmd(exec.Command("ideviceimagemounter", "-l", "-x")) 219 out, err := cmd.CombinedOutput() 220 if err != nil { 221 os.Stderr.Write(out) 222 return fmt.Errorf("ideviceimagemounter: %v", err) 223 } 224 var info struct { 225 Dict struct { 226 Data []byte `xml:",innerxml"` 227 } `xml:"dict"` 228 } 229 if err := xml.Unmarshal(out, &info); err != nil { 230 return fmt.Errorf("mountDevImage: failed to decode mount information: %v", err) 231 } 232 dict, err := parsePlistDict(info.Dict.Data) 233 if err != nil { 234 return fmt.Errorf("mountDevImage: failed to parse mount information: %v", err) 235 } 236 if dict["ImagePresent"] == "true" && dict["Status"] == "Complete" { 237 return nil 238 } 239 // Some devices only give us an ImageSignature key. 240 if _, exists := dict["ImageSignature"]; exists { 241 return nil 242 } 243 // No image is mounted. Find a suitable image. 244 imgPath, err := findDevImage() 245 if err != nil { 246 return err 247 } 248 sigPath := imgPath + ".signature" 249 cmd = idevCmd(exec.Command("ideviceimagemounter", imgPath, sigPath)) 250 if out, err := cmd.CombinedOutput(); err != nil { 251 os.Stderr.Write(out) 252 return fmt.Errorf("ideviceimagemounter: %v", err) 253 } 254 return nil 255 } 256 257 // findDevImage use the device iOS version and build to locate a suitable 258 // developer image. 259 func findDevImage() (string, error) { 260 cmd := idevCmd(exec.Command("ideviceinfo")) 261 out, err := cmd.Output() 262 if err != nil { 263 return "", fmt.Errorf("ideviceinfo: %v", err) 264 } 265 var iosVer, buildVer string 266 lines := bytes.Split(out, []byte("\n")) 267 for _, line := range lines { 268 spl := bytes.SplitN(line, []byte(": "), 2) 269 if len(spl) != 2 { 270 continue 271 } 272 key, val := string(spl[0]), string(spl[1]) 273 switch key { 274 case "ProductVersion": 275 iosVer = val 276 case "BuildVersion": 277 buildVer = val 278 } 279 } 280 if iosVer == "" || buildVer == "" { 281 return "", errors.New("failed to parse ideviceinfo output") 282 } 283 verSplit := strings.Split(iosVer, ".") 284 if len(verSplit) > 2 { 285 // Developer images are specific to major.minor ios version. 286 // Cut off the patch version. 287 iosVer = strings.Join(verSplit[:2], ".") 288 } 289 sdkBase := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport" 290 patterns := []string{fmt.Sprintf("%s (%s)", iosVer, buildVer), fmt.Sprintf("%s (*)", iosVer), fmt.Sprintf("%s*", iosVer)} 291 for _, pattern := range patterns { 292 matches, err := filepath.Glob(filepath.Join(sdkBase, pattern, "DeveloperDiskImage.dmg")) 293 if err != nil { 294 return "", fmt.Errorf("findDevImage: %v", err) 295 } 296 if len(matches) > 0 { 297 return matches[0], nil 298 } 299 } 300 return "", fmt.Errorf("failed to find matching developer image for iOS version %s build %s", iosVer, buildVer) 301 } 302 303 // startDebugBridge ensures that the idevicedebugserverproxy runs on 304 // port 3222. 305 func startDebugBridge() (func(), error) { 306 errChan := make(chan error, 1) 307 cmd := idevCmd(exec.Command("idevicedebugserverproxy", "3222")) 308 var stderr bytes.Buffer 309 cmd.Stderr = &stderr 310 if err := cmd.Start(); err != nil { 311 return nil, fmt.Errorf("idevicedebugserverproxy: %v", err) 312 } 313 go func() { 314 if err := cmd.Wait(); err != nil { 315 if _, ok := err.(*exec.ExitError); ok { 316 errChan <- fmt.Errorf("idevicedebugserverproxy: %s", stderr.Bytes()) 317 } else { 318 errChan <- fmt.Errorf("idevicedebugserverproxy: %v", err) 319 } 320 } 321 errChan <- nil 322 }() 323 closer := func() { 324 cmd.Process.Kill() 325 <-errChan 326 } 327 // Dial localhost:3222 to ensure the proxy is ready. 328 delay := time.Second / 4 329 for attempt := 0; attempt < 5; attempt++ { 330 conn, err := net.DialTimeout("tcp", "localhost:3222", 5*time.Second) 331 if err == nil { 332 conn.Close() 333 return closer, nil 334 } 335 select { 336 case <-time.After(delay): 337 delay *= 2 338 case err := <-errChan: 339 return nil, err 340 } 341 } 342 closer() 343 return nil, errors.New("failed to set up idevicedebugserverproxy") 344 } 345 346 // findDeviceAppPath returns the device path to the app with the 347 // given bundle ID. It parses the output of ideviceinstaller -l -o xml, 348 // looking for the bundle ID and the corresponding Path value. 349 func findDeviceAppPath(bundleID string) (string, error) { 350 cmd := idevCmd(exec.Command("ideviceinstaller", "-l", "-o", "xml")) 351 out, err := cmd.CombinedOutput() 352 if err != nil { 353 os.Stderr.Write(out) 354 return "", fmt.Errorf("ideviceinstaller: -l -o xml %v", err) 355 } 356 var list struct { 357 Apps []struct { 358 Data []byte `xml:",innerxml"` 359 } `xml:"array>dict"` 360 } 361 if err := xml.Unmarshal(out, &list); err != nil { 362 return "", fmt.Errorf("failed to parse ideviceinstaller output: %v", err) 363 } 364 for _, app := range list.Apps { 365 values, err := parsePlistDict(app.Data) 366 if err != nil { 367 return "", fmt.Errorf("findDeviceAppPath: failed to parse app dict: %v", err) 368 } 369 if values["CFBundleIdentifier"] == bundleID { 370 if path, ok := values["Path"]; ok { 371 return path, nil 372 } 373 } 374 } 375 return "", fmt.Errorf("failed to find device path for bundle: %s", bundleID) 376 } 377 378 // Parse an xml encoded plist. Plist values are mapped to string. 379 func parsePlistDict(dict []byte) (map[string]string, error) { 380 d := xml.NewDecoder(bytes.NewReader(dict)) 381 values := make(map[string]string) 382 var key string 383 var hasKey bool 384 for { 385 tok, err := d.Token() 386 if err == io.EOF { 387 break 388 } 389 if err != nil { 390 return nil, err 391 } 392 if tok, ok := tok.(xml.StartElement); ok { 393 if tok.Name.Local == "key" { 394 if err := d.DecodeElement(&key, &tok); err != nil { 395 return nil, err 396 } 397 hasKey = true 398 } else if hasKey { 399 var val string 400 var err error 401 switch n := tok.Name.Local; n { 402 case "true", "false": 403 // Bools are represented as <true/> and <false/>. 404 val = n 405 err = d.Skip() 406 default: 407 err = d.DecodeElement(&val, &tok) 408 } 409 if err != nil { 410 return nil, err 411 } 412 values[key] = val 413 hasKey = false 414 } else { 415 if err := d.Skip(); err != nil { 416 return nil, err 417 } 418 } 419 } 420 } 421 return values, nil 422 } 423 424 func uninstall(bundleID string) error { 425 cmd := idevCmd(exec.Command( 426 "ideviceinstaller", 427 "-U", bundleID, 428 )) 429 if out, err := cmd.CombinedOutput(); err != nil { 430 os.Stderr.Write(out) 431 return fmt.Errorf("ideviceinstaller -U %q: %s", bundleID, err) 432 } 433 return nil 434 } 435 436 func install(appdir string) error { 437 attempt := 0 438 for { 439 cmd := idevCmd(exec.Command( 440 "ideviceinstaller", 441 "-i", appdir, 442 )) 443 if out, err := cmd.CombinedOutput(); err != nil { 444 // Sometimes, installing the app fails for some reason. 445 // Give the device a few seconds and try again. 446 if attempt < 5 { 447 time.Sleep(5 * time.Second) 448 attempt++ 449 continue 450 } 451 os.Stderr.Write(out) 452 return fmt.Errorf("ideviceinstaller -i %q: %v (%d attempts)", appdir, err, attempt) 453 } 454 return nil 455 } 456 } 457 458 func idevCmd(cmd *exec.Cmd) *exec.Cmd { 459 if deviceID != "" { 460 // Inject -u device_id after the executable, but before the arguments. 461 args := []string{cmd.Args[0], "-u", deviceID} 462 cmd.Args = append(args, cmd.Args[1:]...) 463 } 464 return cmd 465 } 466 467 func run(appdir, bundleID string, args []string) error { 468 var env []string 469 for _, e := range os.Environ() { 470 // Don't override TMPDIR on the device. 471 if strings.HasPrefix(e, "TMPDIR=") { 472 continue 473 } 474 env = append(env, e) 475 } 476 attempt := 0 477 for { 478 // The device app path reported by the device might be stale, so retry 479 // the lookup of the device path along with the lldb launching below. 480 deviceapp, err := findDeviceAppPath(bundleID) 481 if err != nil { 482 // The device app path might not yet exist for a newly installed app. 483 if attempt == 5 { 484 return err 485 } 486 attempt++ 487 time.Sleep(5 * time.Second) 488 continue 489 } 490 lldb := exec.Command( 491 "python", 492 "-", // Read script from stdin. 493 appdir, 494 deviceapp, 495 ) 496 lldb.Args = append(lldb.Args, args...) 497 lldb.Env = env 498 lldb.Stdin = strings.NewReader(lldbDriver) 499 lldb.Stdout = os.Stdout 500 var out bytes.Buffer 501 lldb.Stderr = io.MultiWriter(&out, os.Stderr) 502 err = lldb.Start() 503 if err == nil { 504 // Forward SIGQUIT to the lldb driver which in turn will forward 505 // to the running program. 506 sigs := make(chan os.Signal, 1) 507 signal.Notify(sigs, syscall.SIGQUIT) 508 proc := lldb.Process 509 go func() { 510 for sig := range sigs { 511 proc.Signal(sig) 512 } 513 }() 514 err = lldb.Wait() 515 signal.Stop(sigs) 516 close(sigs) 517 } 518 // If the program was not started it can be retried without papering over 519 // real test failures. 520 started := bytes.HasPrefix(out.Bytes(), []byte("lldb: running program")) 521 if started || err == nil || attempt == 5 { 522 return err 523 } 524 // Sometimes, the app was not yet ready to launch or the device path was 525 // stale. Retry. 526 attempt++ 527 time.Sleep(5 * time.Second) 528 } 529 } 530 531 func copyLocalDir(dst, src string) error { 532 if err := os.Mkdir(dst, 0755); err != nil { 533 return err 534 } 535 536 d, err := os.Open(src) 537 if err != nil { 538 return err 539 } 540 defer d.Close() 541 fi, err := d.Readdir(-1) 542 if err != nil { 543 return err 544 } 545 546 for _, f := range fi { 547 if f.IsDir() { 548 if f.Name() == "testdata" { 549 if err := cp(dst, filepath.Join(src, f.Name())); err != nil { 550 return err 551 } 552 } 553 continue 554 } 555 if err := cp(dst, filepath.Join(src, f.Name())); err != nil { 556 return err 557 } 558 } 559 return nil 560 } 561 562 func cp(dst, src string) error { 563 out, err := exec.Command("cp", "-a", src, dst).CombinedOutput() 564 if err != nil { 565 os.Stderr.Write(out) 566 } 567 return err 568 } 569 570 func copyLocalData(dstbase string) (pkgpath string, err error) { 571 cwd, err := os.Getwd() 572 if err != nil { 573 return "", err 574 } 575 576 finalPkgpath, underGoRoot, err := subdir() 577 if err != nil { 578 return "", err 579 } 580 cwd = strings.TrimSuffix(cwd, finalPkgpath) 581 582 // Copy all immediate files and testdata directories between 583 // the package being tested and the source root. 584 pkgpath = "" 585 for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) { 586 if debug { 587 log.Printf("copying %s", pkgpath) 588 } 589 pkgpath = filepath.Join(pkgpath, element) 590 dst := filepath.Join(dstbase, pkgpath) 591 src := filepath.Join(cwd, pkgpath) 592 if err := copyLocalDir(dst, src); err != nil { 593 return "", err 594 } 595 } 596 597 if underGoRoot { 598 // Copy timezone file. 599 // 600 // Typical apps have the zoneinfo.zip in the root of their app bundle, 601 // read by the time package as the working directory at initialization. 602 // As we move the working directory to the GOROOT pkg directory, we 603 // install the zoneinfo.zip file in the pkgpath. 604 err := cp( 605 filepath.Join(dstbase, pkgpath), 606 filepath.Join(cwd, "lib", "time", "zoneinfo.zip"), 607 ) 608 if err != nil { 609 return "", err 610 } 611 // Copy src/runtime/textflag.h for (at least) Test386EndToEnd in 612 // cmd/asm/internal/asm. 613 runtimePath := filepath.Join(dstbase, "src", "runtime") 614 if err := os.MkdirAll(runtimePath, 0755); err != nil { 615 return "", err 616 } 617 err = cp( 618 filepath.Join(runtimePath, "textflag.h"), 619 filepath.Join(cwd, "src", "runtime", "textflag.h"), 620 ) 621 if err != nil { 622 return "", err 623 } 624 } 625 626 return finalPkgpath, nil 627 } 628 629 // subdir determines the package based on the current working directory, 630 // and returns the path to the package source relative to $GOROOT (or $GOPATH). 631 func subdir() (pkgpath string, underGoRoot bool, err error) { 632 cwd, err := os.Getwd() 633 if err != nil { 634 return "", false, err 635 } 636 if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) { 637 subdir, err := filepath.Rel(root, cwd) 638 if err != nil { 639 return "", false, err 640 } 641 return subdir, true, nil 642 } 643 644 for _, p := range filepath.SplitList(build.Default.GOPATH) { 645 if !strings.HasPrefix(cwd, p) { 646 continue 647 } 648 subdir, err := filepath.Rel(p, cwd) 649 if err == nil { 650 return subdir, false, nil 651 } 652 } 653 return "", false, fmt.Errorf( 654 "working directory %q is not in either GOROOT(%q) or GOPATH(%q)", 655 cwd, 656 runtime.GOROOT(), 657 build.Default.GOPATH, 658 ) 659 } 660 661 func infoPlist(pkgpath string) string { 662 return `<?xml version="1.0" encoding="UTF-8"?> 663 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 664 <plist version="1.0"> 665 <dict> 666 <key>CFBundleName</key><string>golang.gotest</string> 667 <key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array> 668 <key>CFBundleExecutable</key><string>gotest</string> 669 <key>CFBundleVersion</key><string>1.0</string> 670 <key>CFBundleIdentifier</key><string>` + bundleID + `</string> 671 <key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string> 672 <key>LSRequiresIPhoneOS</key><true/> 673 <key>CFBundleDisplayName</key><string>gotest</string> 674 <key>GoExecWrapperWorkingDirectory</key><string>` + pkgpath + `</string> 675 </dict> 676 </plist> 677 ` 678 } 679 680 func entitlementsPlist() string { 681 return `<?xml version="1.0" encoding="UTF-8"?> 682 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 683 <plist version="1.0"> 684 <dict> 685 <key>keychain-access-groups</key> 686 <array><string>` + appID + `</string></array> 687 <key>get-task-allow</key> 688 <true/> 689 <key>application-identifier</key> 690 <string>` + appID + `</string> 691 <key>com.apple.developer.team-identifier</key> 692 <string>` + teamID + `</string> 693 </dict> 694 </plist> 695 ` 696 } 697 698 const resourceRules = `<?xml version="1.0" encoding="UTF-8"?> 699 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 700 <plist version="1.0"> 701 <dict> 702 <key>rules</key> 703 <dict> 704 <key>.*</key> 705 <true/> 706 <key>Info.plist</key> 707 <dict> 708 <key>omit</key> 709 <true/> 710 <key>weight</key> 711 <integer>10</integer> 712 </dict> 713 <key>ResourceRules.plist</key> 714 <dict> 715 <key>omit</key> 716 <true/> 717 <key>weight</key> 718 <integer>100</integer> 719 </dict> 720 </dict> 721 </dict> 722 </plist> 723 ` 724 725 const lldbDriver = ` 726 import sys 727 import os 728 import signal 729 730 exe, device_exe, args = sys.argv[1], sys.argv[2], sys.argv[3:] 731 732 env = [] 733 for k, v in os.environ.items(): 734 env.append(k + "=" + v) 735 736 sys.path.append('/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python') 737 738 import lldb 739 740 debugger = lldb.SBDebugger.Create() 741 debugger.SetAsync(True) 742 debugger.SkipLLDBInitFiles(True) 743 744 err = lldb.SBError() 745 target = debugger.CreateTarget(exe, None, 'remote-ios', True, err) 746 if not target.IsValid() or not err.Success(): 747 sys.stderr.write("lldb: failed to setup up target: %s\n" % (err)) 748 sys.exit(1) 749 750 target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_exe)) 751 752 listener = debugger.GetListener() 753 process = target.ConnectRemote(listener, 'connect://localhost:3222', None, err) 754 if not err.Success(): 755 sys.stderr.write("lldb: failed to connect to remote target: %s\n" % (err)) 756 sys.exit(1) 757 758 # Don't stop on signals. 759 sigs = process.GetUnixSignals() 760 for i in range(0, sigs.GetNumSignals()): 761 sig = sigs.GetSignalAtIndex(i) 762 sigs.SetShouldStop(sig, False) 763 sigs.SetShouldNotify(sig, False) 764 765 event = lldb.SBEvent() 766 running = False 767 prev_handler = None 768 while True: 769 if not listener.WaitForEvent(1, event): 770 continue 771 if not lldb.SBProcess.EventIsProcessEvent(event): 772 continue 773 if running: 774 # Pass through stdout and stderr. 775 while True: 776 out = process.GetSTDOUT(8192) 777 if not out: 778 break 779 sys.stdout.write(out) 780 while True: 781 out = process.GetSTDERR(8192) 782 if not out: 783 break 784 sys.stderr.write(out) 785 state = process.GetStateFromEvent(event) 786 if state in [lldb.eStateCrashed, lldb.eStateDetached, lldb.eStateUnloaded, lldb.eStateExited]: 787 if running: 788 signal.signal(signal.SIGQUIT, prev_handler) 789 break 790 elif state == lldb.eStateConnected: 791 process.RemoteLaunch(args, env, None, None, None, None, 0, False, err) 792 if not err.Success(): 793 sys.stderr.write("lldb: failed to launch remote process: %s\n" % (err)) 794 process.Kill() 795 debugger.Terminate() 796 sys.exit(1) 797 # Forward SIGQUIT to the program. 798 def signal_handler(signal, frame): 799 process.Signal(signal) 800 prev_handler = signal.signal(signal.SIGQUIT, signal_handler) 801 # Tell the Go driver that the program is running and should not be retried. 802 sys.stderr.write("lldb: running program\n") 803 running = True 804 # Process stops once at the beginning. Continue. 805 process.Continue() 806 807 exitStatus = process.GetExitStatus() 808 process.Kill() 809 debugger.Terminate() 810 sys.exit(exitStatus) 811 `