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