Source file
misc/ios/go_ios_exec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package main
21
22 import (
23 "bytes"
24 "encoding/xml"
25 "errors"
26 "fmt"
27 "go/build"
28 "io"
29 "log"
30 "net"
31 "os"
32 "os/exec"
33 "os/signal"
34 "path/filepath"
35 "runtime"
36 "strconv"
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
55
56
57 var lock *os.File
58
59 func main() {
60 log.SetFlags(0)
61 log.SetPrefix("go_ios_exec: ")
62 if debug {
63 log.Println(strings.Join(os.Args, " "))
64 }
65 if len(os.Args) < 2 {
66 log.Fatal("usage: go_ios_exec a.out")
67 }
68
69
70 bundleID = "golang.gotest"
71
72 exitCode, err := runMain()
73 if err != nil {
74 log.Fatalf("%v\n", err)
75 }
76 os.Exit(exitCode)
77 }
78
79 func runMain() (int, error) {
80 var err error
81 tmpdir, err = os.MkdirTemp("", "go_ios_exec_")
82 if err != nil {
83 return 1, err
84 }
85 if !debug {
86 defer os.RemoveAll(tmpdir)
87 }
88
89 appdir := filepath.Join(tmpdir, "gotest.app")
90 os.RemoveAll(appdir)
91
92 if err := assembleApp(appdir, os.Args[1]); err != nil {
93 return 1, err
94 }
95
96
97
98
99
100
101
102 lockName := filepath.Join(os.TempDir(), "go_ios_exec-"+deviceID+".lock")
103 lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666)
104 if err != nil {
105 return 1, err
106 }
107 if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
108 return 1, err
109 }
110
111 if goarch := os.Getenv("GOARCH"); goarch == "arm64" {
112 err = runOnDevice(appdir)
113 } else {
114 err = runOnSimulator(appdir)
115 }
116 if err != nil {
117
118 if err, ok := err.(*exec.ExitError); ok {
119 if ws, ok := err.Sys().(interface{ ExitStatus() int }); ok {
120 return ws.ExitStatus(), nil
121 }
122 }
123 return 1, err
124 }
125 return 0, nil
126 }
127
128 func runOnSimulator(appdir string) error {
129 if err := installSimulator(appdir); err != nil {
130 return err
131 }
132
133 return runSimulator(appdir, bundleID, os.Args[2:])
134 }
135
136 func runOnDevice(appdir string) error {
137
138 devID = getenv("GOIOS_DEV_ID")
139
140
141
142 appID = getenv("GOIOS_APP_ID")
143
144
145
146 teamID = getenv("GOIOS_TEAM_ID")
147
148
149 deviceID = os.Getenv("GOIOS_DEVICE_ID")
150
151 if _, id, ok := strings.Cut(appID, "."); ok {
152 bundleID = id
153 }
154
155 if err := signApp(appdir); err != nil {
156 return err
157 }
158
159 if err := uninstallDevice(bundleID); err != nil {
160 return err
161 }
162
163 if err := installDevice(appdir); err != nil {
164 return err
165 }
166
167 if err := mountDevImage(); err != nil {
168 return err
169 }
170
171
172 exec.Command("killall", "idevicedebugserverproxy").Run()
173
174 closer, err := startDebugBridge()
175 if err != nil {
176 return err
177 }
178 defer closer()
179
180 return runDevice(appdir, bundleID, os.Args[2:])
181 }
182
183 func getenv(envvar string) string {
184 s := os.Getenv(envvar)
185 if s == "" {
186 log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar)
187 }
188 return s
189 }
190
191 func assembleApp(appdir, bin string) error {
192 if err := os.MkdirAll(appdir, 0755); err != nil {
193 return err
194 }
195
196 if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil {
197 return err
198 }
199
200 pkgpath, err := copyLocalData(appdir)
201 if err != nil {
202 return err
203 }
204
205 entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
206 if err := os.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil {
207 return err
208 }
209 if err := os.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil {
210 return err
211 }
212 if err := os.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil {
213 return err
214 }
215 return nil
216 }
217
218 func signApp(appdir string) error {
219 entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist")
220 cmd := exec.Command(
221 "codesign",
222 "-f",
223 "-s", devID,
224 "--entitlements", entitlementsPath,
225 appdir,
226 )
227 if debug {
228 log.Println(strings.Join(cmd.Args, " "))
229 }
230 cmd.Stdout = os.Stdout
231 cmd.Stderr = os.Stderr
232 if err := cmd.Run(); err != nil {
233 return fmt.Errorf("codesign: %v", err)
234 }
235 return nil
236 }
237
238
239
240
241 func mountDevImage() error {
242
243 cmd := idevCmd(exec.Command("ideviceimagemounter", "-l", "-x"))
244 out, err := cmd.CombinedOutput()
245 if err != nil {
246 os.Stderr.Write(out)
247 return fmt.Errorf("ideviceimagemounter: %v", err)
248 }
249 var info struct {
250 Dict struct {
251 Data []byte `xml:",innerxml"`
252 } `xml:"dict"`
253 }
254 if err := xml.Unmarshal(out, &info); err != nil {
255 return fmt.Errorf("mountDevImage: failed to decode mount information: %v", err)
256 }
257 dict, err := parsePlistDict(info.Dict.Data)
258 if err != nil {
259 return fmt.Errorf("mountDevImage: failed to parse mount information: %v", err)
260 }
261 if dict["ImagePresent"] == "true" && dict["Status"] == "Complete" {
262 return nil
263 }
264
265 if _, exists := dict["ImageSignature"]; exists {
266 return nil
267 }
268
269 imgPath, err := findDevImage()
270 if err != nil {
271 return err
272 }
273 sigPath := imgPath + ".signature"
274 cmd = idevCmd(exec.Command("ideviceimagemounter", imgPath, sigPath))
275 if out, err := cmd.CombinedOutput(); err != nil {
276 os.Stderr.Write(out)
277 return fmt.Errorf("ideviceimagemounter: %v", err)
278 }
279 return nil
280 }
281
282
283
284 func findDevImage() (string, error) {
285 cmd := idevCmd(exec.Command("ideviceinfo"))
286 out, err := cmd.Output()
287 if err != nil {
288 return "", fmt.Errorf("ideviceinfo: %v", err)
289 }
290 var iosVer, buildVer string
291 lines := bytes.Split(out, []byte("\n"))
292 for _, line := range lines {
293 key, val, ok := strings.Cut(string(line), ": ")
294 if !ok {
295 continue
296 }
297 switch key {
298 case "ProductVersion":
299 iosVer = val
300 case "BuildVersion":
301 buildVer = val
302 }
303 }
304 if iosVer == "" || buildVer == "" {
305 return "", errors.New("failed to parse ideviceinfo output")
306 }
307 verSplit := strings.Split(iosVer, ".")
308 if len(verSplit) > 2 {
309
310
311 iosVer = strings.Join(verSplit[:2], ".")
312 }
313 sdkBase := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"
314 patterns := []string{fmt.Sprintf("%s (%s)", iosVer, buildVer), fmt.Sprintf("%s (*)", iosVer), fmt.Sprintf("%s*", iosVer)}
315 for _, pattern := range patterns {
316 matches, err := filepath.Glob(filepath.Join(sdkBase, pattern, "DeveloperDiskImage.dmg"))
317 if err != nil {
318 return "", fmt.Errorf("findDevImage: %v", err)
319 }
320 if len(matches) > 0 {
321 return matches[0], nil
322 }
323 }
324 return "", fmt.Errorf("failed to find matching developer image for iOS version %s build %s", iosVer, buildVer)
325 }
326
327
328
329 func startDebugBridge() (func(), error) {
330 errChan := make(chan error, 1)
331 cmd := idevCmd(exec.Command("idevicedebugserverproxy", "3222"))
332 var stderr bytes.Buffer
333 cmd.Stderr = &stderr
334 if err := cmd.Start(); err != nil {
335 return nil, fmt.Errorf("idevicedebugserverproxy: %v", err)
336 }
337 go func() {
338 if err := cmd.Wait(); err != nil {
339 if _, ok := err.(*exec.ExitError); ok {
340 errChan <- fmt.Errorf("idevicedebugserverproxy: %s", stderr.Bytes())
341 } else {
342 errChan <- fmt.Errorf("idevicedebugserverproxy: %v", err)
343 }
344 }
345 errChan <- nil
346 }()
347 closer := func() {
348 cmd.Process.Kill()
349 <-errChan
350 }
351
352 delay := time.Second / 4
353 for attempt := 0; attempt < 5; attempt++ {
354 conn, err := net.DialTimeout("tcp", "localhost:3222", 5*time.Second)
355 if err == nil {
356 conn.Close()
357 return closer, nil
358 }
359 select {
360 case <-time.After(delay):
361 delay *= 2
362 case err := <-errChan:
363 return nil, err
364 }
365 }
366 closer()
367 return nil, errors.New("failed to set up idevicedebugserverproxy")
368 }
369
370
371
372
373 func findDeviceAppPath(bundleID string) (string, error) {
374 cmd := idevCmd(exec.Command("ideviceinstaller", "-l", "-o", "xml"))
375 out, err := cmd.CombinedOutput()
376 if err != nil {
377 os.Stderr.Write(out)
378 return "", fmt.Errorf("ideviceinstaller: -l -o xml %v", err)
379 }
380 var list struct {
381 Apps []struct {
382 Data []byte `xml:",innerxml"`
383 } `xml:"array>dict"`
384 }
385 if err := xml.Unmarshal(out, &list); err != nil {
386 return "", fmt.Errorf("failed to parse ideviceinstaller output: %v", err)
387 }
388 for _, app := range list.Apps {
389 values, err := parsePlistDict(app.Data)
390 if err != nil {
391 return "", fmt.Errorf("findDeviceAppPath: failed to parse app dict: %v", err)
392 }
393 if values["CFBundleIdentifier"] == bundleID {
394 if path, ok := values["Path"]; ok {
395 return path, nil
396 }
397 }
398 }
399 return "", fmt.Errorf("failed to find device path for bundle: %s", bundleID)
400 }
401
402
403 func parsePlistDict(dict []byte) (map[string]string, error) {
404 d := xml.NewDecoder(bytes.NewReader(dict))
405 values := make(map[string]string)
406 var key string
407 var hasKey bool
408 for {
409 tok, err := d.Token()
410 if err == io.EOF {
411 break
412 }
413 if err != nil {
414 return nil, err
415 }
416 if tok, ok := tok.(xml.StartElement); ok {
417 if tok.Name.Local == "key" {
418 if err := d.DecodeElement(&key, &tok); err != nil {
419 return nil, err
420 }
421 hasKey = true
422 } else if hasKey {
423 var val string
424 var err error
425 switch n := tok.Name.Local; n {
426 case "true", "false":
427
428 val = n
429 err = d.Skip()
430 default:
431 err = d.DecodeElement(&val, &tok)
432 }
433 if err != nil {
434 return nil, err
435 }
436 values[key] = val
437 hasKey = false
438 } else {
439 if err := d.Skip(); err != nil {
440 return nil, err
441 }
442 }
443 }
444 }
445 return values, nil
446 }
447
448 func installSimulator(appdir string) error {
449 cmd := exec.Command(
450 "xcrun", "simctl", "install",
451 "booted",
452 appdir,
453 )
454 if out, err := cmd.CombinedOutput(); err != nil {
455 os.Stderr.Write(out)
456 return fmt.Errorf("xcrun simctl install booted %q: %v", appdir, err)
457 }
458 return nil
459 }
460
461 func uninstallDevice(bundleID string) error {
462 cmd := idevCmd(exec.Command(
463 "ideviceinstaller",
464 "-U", bundleID,
465 ))
466 if out, err := cmd.CombinedOutput(); err != nil {
467 os.Stderr.Write(out)
468 return fmt.Errorf("ideviceinstaller -U %q: %s", bundleID, err)
469 }
470 return nil
471 }
472
473 func installDevice(appdir string) error {
474 attempt := 0
475 for {
476 cmd := idevCmd(exec.Command(
477 "ideviceinstaller",
478 "-i", appdir,
479 ))
480 if out, err := cmd.CombinedOutput(); err != nil {
481
482
483 if attempt < 5 {
484 time.Sleep(5 * time.Second)
485 attempt++
486 continue
487 }
488 os.Stderr.Write(out)
489 return fmt.Errorf("ideviceinstaller -i %q: %v (%d attempts)", appdir, err, attempt)
490 }
491 return nil
492 }
493 }
494
495 func idevCmd(cmd *exec.Cmd) *exec.Cmd {
496 if deviceID != "" {
497
498 args := []string{cmd.Args[0], "-u", deviceID}
499 cmd.Args = append(args, cmd.Args[1:]...)
500 }
501 return cmd
502 }
503
504 func runSimulator(appdir, bundleID string, args []string) error {
505 cmd := exec.Command(
506 "xcrun", "simctl", "launch",
507 "--wait-for-debugger",
508 "booted",
509 bundleID,
510 )
511 out, err := cmd.CombinedOutput()
512 if err != nil {
513 os.Stderr.Write(out)
514 return fmt.Errorf("xcrun simctl launch booted %q: %v", bundleID, err)
515 }
516 var processID int
517 var ignore string
518 if _, err := fmt.Sscanf(string(out), "%s %d", &ignore, &processID); err != nil {
519 return fmt.Errorf("runSimulator: couldn't find processID from `simctl launch`: %v (%q)", err, out)
520 }
521 _, err = runLLDB("ios-simulator", appdir, strconv.Itoa(processID), args)
522 return err
523 }
524
525 func runDevice(appdir, bundleID string, args []string) error {
526 attempt := 0
527 for {
528
529
530 deviceapp, err := findDeviceAppPath(bundleID)
531 if err != nil {
532
533 if attempt == 5 {
534 return err
535 }
536 attempt++
537 time.Sleep(5 * time.Second)
538 continue
539 }
540 out, err := runLLDB("remote-ios", appdir, deviceapp, args)
541
542
543 started := bytes.HasPrefix(out, []byte("lldb: running program"))
544 if started || err == nil || attempt == 5 {
545 return err
546 }
547
548
549 attempt++
550 time.Sleep(5 * time.Second)
551 }
552 }
553
554 func runLLDB(target, appdir, deviceapp string, args []string) ([]byte, error) {
555 var env []string
556 for _, e := range os.Environ() {
557
558 if strings.HasPrefix(e, "TMPDIR=") || strings.HasPrefix(e, "HOME=") || strings.HasPrefix(e, "GOCACHE=") {
559 continue
560 }
561 env = append(env, e)
562 }
563 lldb := exec.Command(
564 "python",
565 "-",
566 target,
567 appdir,
568 deviceapp,
569 )
570 lldb.Args = append(lldb.Args, args...)
571 lldb.Env = env
572 lldb.Stdin = strings.NewReader(lldbDriver)
573 lldb.Stdout = os.Stdout
574 var out bytes.Buffer
575 lldb.Stderr = io.MultiWriter(&out, os.Stderr)
576 err := lldb.Start()
577 if err == nil {
578
579
580 sigs := make(chan os.Signal, 1)
581 signal.Notify(sigs, syscall.SIGQUIT)
582 proc := lldb.Process
583 go func() {
584 for sig := range sigs {
585 proc.Signal(sig)
586 }
587 }()
588 err = lldb.Wait()
589 signal.Stop(sigs)
590 close(sigs)
591 }
592 return out.Bytes(), err
593 }
594
595 func copyLocalDir(dst, src string) error {
596 if err := os.Mkdir(dst, 0755); err != nil {
597 return err
598 }
599
600 d, err := os.Open(src)
601 if err != nil {
602 return err
603 }
604 defer d.Close()
605 fi, err := d.Readdir(-1)
606 if err != nil {
607 return err
608 }
609
610 for _, f := range fi {
611 if f.IsDir() {
612 if f.Name() == "testdata" {
613 if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
614 return err
615 }
616 }
617 continue
618 }
619 if err := cp(dst, filepath.Join(src, f.Name())); err != nil {
620 return err
621 }
622 }
623 return nil
624 }
625
626 func cp(dst, src string) error {
627 out, err := exec.Command("cp", "-a", src, dst).CombinedOutput()
628 if err != nil {
629 os.Stderr.Write(out)
630 }
631 return err
632 }
633
634 func copyLocalData(dstbase string) (pkgpath string, err error) {
635 cwd, err := os.Getwd()
636 if err != nil {
637 return "", err
638 }
639
640 finalPkgpath, underGoRoot, err := subdir()
641 if err != nil {
642 return "", err
643 }
644 cwd = strings.TrimSuffix(cwd, finalPkgpath)
645
646
647
648 pkgpath = ""
649 for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
650 if debug {
651 log.Printf("copying %s", pkgpath)
652 }
653 pkgpath = filepath.Join(pkgpath, element)
654 dst := filepath.Join(dstbase, pkgpath)
655 src := filepath.Join(cwd, pkgpath)
656 if err := copyLocalDir(dst, src); err != nil {
657 return "", err
658 }
659 }
660
661 if underGoRoot {
662
663
664
665
666
667
668 err := cp(
669 filepath.Join(dstbase, pkgpath),
670 filepath.Join(cwd, "lib", "time", "zoneinfo.zip"),
671 )
672 if err != nil {
673 return "", err
674 }
675
676
677 runtimePath := filepath.Join(dstbase, "src", "runtime")
678 if err := os.MkdirAll(runtimePath, 0755); err != nil {
679 return "", err
680 }
681 err = cp(
682 filepath.Join(runtimePath, "textflag.h"),
683 filepath.Join(cwd, "src", "runtime", "textflag.h"),
684 )
685 if err != nil {
686 return "", err
687 }
688 }
689
690 return finalPkgpath, nil
691 }
692
693
694
695 func subdir() (pkgpath string, underGoRoot bool, err error) {
696 cwd, err := os.Getwd()
697 if err != nil {
698 return "", false, err
699 }
700 cwd, err = filepath.EvalSymlinks(cwd)
701 if err != nil {
702 log.Fatal(err)
703 }
704 goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
705 if err != nil {
706 return "", false, err
707 }
708 if strings.HasPrefix(cwd, goroot) {
709 subdir, err := filepath.Rel(goroot, cwd)
710 if err != nil {
711 return "", false, err
712 }
713 return subdir, true, nil
714 }
715
716 for _, p := range filepath.SplitList(build.Default.GOPATH) {
717 pabs, err := filepath.EvalSymlinks(p)
718 if err != nil {
719 return "", false, err
720 }
721 if !strings.HasPrefix(cwd, pabs) {
722 continue
723 }
724 subdir, err := filepath.Rel(pabs, cwd)
725 if err == nil {
726 return subdir, false, nil
727 }
728 }
729 return "", false, fmt.Errorf(
730 "working directory %q is not in either GOROOT(%q) or GOPATH(%q)",
731 cwd,
732 runtime.GOROOT(),
733 build.Default.GOPATH,
734 )
735 }
736
737 func infoPlist(pkgpath string) string {
738 return `<?xml version="1.0" encoding="UTF-8"?>
739 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
740 <plist version="1.0">
741 <dict>
742 <key>CFBundleName</key><string>golang.gotest</string>
743 <key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array>
744 <key>CFBundleExecutable</key><string>gotest</string>
745 <key>CFBundleVersion</key><string>1.0</string>
746 <key>CFBundleShortVersionString</key><string>1.0</string>
747 <key>CFBundleIdentifier</key><string>` + bundleID + `</string>
748 <key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string>
749 <key>LSRequiresIPhoneOS</key><true/>
750 <key>CFBundleDisplayName</key><string>gotest</string>
751 <key>GoExecWrapperWorkingDirectory</key><string>` + pkgpath + `</string>
752 </dict>
753 </plist>
754 `
755 }
756
757 func entitlementsPlist() string {
758 return `<?xml version="1.0" encoding="UTF-8"?>
759 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
760 <plist version="1.0">
761 <dict>
762 <key>keychain-access-groups</key>
763 <array><string>` + appID + `</string></array>
764 <key>get-task-allow</key>
765 <true/>
766 <key>application-identifier</key>
767 <string>` + appID + `</string>
768 <key>com.apple.developer.team-identifier</key>
769 <string>` + teamID + `</string>
770 </dict>
771 </plist>
772 `
773 }
774
775 const resourceRules = `<?xml version="1.0" encoding="UTF-8"?>
776 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
777 <plist version="1.0">
778 <dict>
779 <key>rules</key>
780 <dict>
781 <key>.*</key>
782 <true/>
783 <key>Info.plist</key>
784 <dict>
785 <key>omit</key>
786 <true/>
787 <key>weight</key>
788 <integer>10</integer>
789 </dict>
790 <key>ResourceRules.plist</key>
791 <dict>
792 <key>omit</key>
793 <true/>
794 <key>weight</key>
795 <integer>100</integer>
796 </dict>
797 </dict>
798 </dict>
799 </plist>
800 `
801
802 const lldbDriver = `
803 import sys
804 import os
805 import signal
806
807 platform, exe, device_exe_or_pid, args = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4:]
808
809 env = []
810 for k, v in os.environ.items():
811 env.append(k + "=" + v)
812
813 sys.path.append('/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python')
814
815 import lldb
816
817 debugger = lldb.SBDebugger.Create()
818 debugger.SetAsync(True)
819 debugger.SkipLLDBInitFiles(True)
820
821 err = lldb.SBError()
822 target = debugger.CreateTarget(exe, None, platform, True, err)
823 if not target.IsValid() or not err.Success():
824 sys.stderr.write("lldb: failed to setup up target: %s\n" % (err))
825 sys.exit(1)
826
827 listener = debugger.GetListener()
828
829 if platform == 'remote-ios':
830 target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_exe_or_pid))
831 process = target.ConnectRemote(listener, 'connect://localhost:3222', None, err)
832 else:
833 process = target.AttachToProcessWithID(listener, int(device_exe_or_pid), err)
834
835 if not err.Success():
836 sys.stderr.write("lldb: failed to connect to remote target %s: %s\n" % (device_exe_or_pid, err))
837 sys.exit(1)
838
839 # Don't stop on signals.
840 sigs = process.GetUnixSignals()
841 for i in range(0, sigs.GetNumSignals()):
842 sig = sigs.GetSignalAtIndex(i)
843 sigs.SetShouldStop(sig, False)
844 sigs.SetShouldNotify(sig, False)
845
846 event = lldb.SBEvent()
847 running = False
848 prev_handler = None
849
850 def signal_handler(signal, frame):
851 process.Signal(signal)
852
853 def run_program():
854 # Forward SIGQUIT to the program.
855 prev_handler = signal.signal(signal.SIGQUIT, signal_handler)
856 # Tell the Go driver that the program is running and should not be retried.
857 sys.stderr.write("lldb: running program\n")
858 running = True
859 # Process is stopped at attach/launch. Let it run.
860 process.Continue()
861
862 if platform != 'remote-ios':
863 # For the local emulator the program is ready to run.
864 # For remote device runs, we need to wait for eStateConnected,
865 # below.
866 run_program()
867
868 while True:
869 if not listener.WaitForEvent(1, event):
870 continue
871 if not lldb.SBProcess.EventIsProcessEvent(event):
872 continue
873 if running:
874 # Pass through stdout and stderr.
875 while True:
876 out = process.GetSTDOUT(8192)
877 if not out:
878 break
879 sys.stdout.write(out)
880 while True:
881 out = process.GetSTDERR(8192)
882 if not out:
883 break
884 sys.stderr.write(out)
885 state = process.GetStateFromEvent(event)
886 if state in [lldb.eStateCrashed, lldb.eStateDetached, lldb.eStateUnloaded, lldb.eStateExited]:
887 if running:
888 signal.signal(signal.SIGQUIT, prev_handler)
889 break
890 elif state == lldb.eStateConnected:
891 if platform == 'remote-ios':
892 process.RemoteLaunch(args, env, None, None, None, None, 0, False, err)
893 if not err.Success():
894 sys.stderr.write("lldb: failed to launch remote process: %s\n" % (err))
895 process.Kill()
896 debugger.Terminate()
897 sys.exit(1)
898 run_program()
899
900 exitStatus = process.GetExitStatus()
901 exitDesc = process.GetExitDescription()
902 process.Kill()
903 debugger.Terminate()
904 if exitStatus == 0 and exitDesc is not None:
905 # Ensure tests fail when killed by a signal.
906 exitStatus = 123
907
908 sys.exit(exitStatus)
909 `
910
View as plain text