Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1468)

Side by Side Diff: platform_tools/android/bin/android_run_app.go

Issue 1256353002: Add android_run_app.go (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Fix package names Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /*
6 Run a Skia app to completion, piping the log to stdout.
7 */
8
9 package main
10
11 import (
12 "flag"
13 "fmt"
14 "io"
15 "os"
16 "os/exec"
17 "os/signal"
18 "strconv"
19 "strings"
20 "time"
21
22 "github.com/skia-dev/glog"
23 "go.skia.org/infra/go/common"
24 )
25
26 const (
27 COM_SKIA = "com.skia"
28 )
29
30 var (
31 adb = flag.String("adb", "", "Path to the ADB executable.")
32 app = flag.String("app", "VisualBench", "Name of the app to run.")
33 serial = flag.String("s", "", "Serial number for the Android device to u se.")
34 )
35
36 // ADB returns an os.Cmd for the given ADB command.
37 func ADB(adb, serial string, log bool, args ...string) *exec.Cmd {
stephana 2015/07/30 13:37:42 Nit: I think "adbCommand" or adbCmd would be more
borenet 2015/07/30 17:37:49 I took the suggestion of making ADB a struct. The
38 cmd := []string{}
39 if serial != "" {
40 cmd = append(cmd, "-s", serial)
41 }
42 cmd = append(cmd, args...)
43 if log {
44 glog.Infof("Exec `%s %s`", adb, strings.Join(cmd, " "))
45 }
46 return exec.Command(adb, cmd...)
47 }
48
49 // RunADB runs the given command through ADB.
50 func RunADB(adb, serial string, log bool, args ...string) ([]byte, error) {
51 return ADB(adb, serial, log, args...).Output()
stephana 2015/07/30 13:37:42 This is an unnecessary level of indirection. Inste
borenet 2015/07/30 17:37:49 Removed.
52 }
53
54 // KillSkia kills any running Skia process.
55 func KillSkia(adb, serial string) error {
56 _, err := RunADB(adb, serial, false, "shell", "am", "force-stop", COM_SK IA)
57 return err
58 }
59
60 // PollSkia checks to see if any Skia process is running. If so, return the pid, otherwise return -1.
61 func PollSkia(adb, serial string) (int64, error) {
62 output, err := RunADB(adb, serial, false, "shell", "ps")
stephana 2015/07/30 13:37:42 It might make sense to create a type 'ADB' instead
borenet 2015/07/30 17:37:48 I like this! Done.
63 if err != nil {
64 return -1, fmt.Errorf("Failed to check the running processes on the device: %v", err)
65 }
66 for _, line := range strings.Split(string(output), "\n") {
67 if strings.Contains(line, COM_SKIA) {
68 fields := strings.Fields(line)
69 pid, err := strconv.ParseInt(fields[1], 10, 32)
70 if err != nil {
71 return -1, fmt.Errorf("Failed to parse \"%s\" to an integer: %v", fields[1], err)
72 }
73 return pid, nil
74 }
75 }
76 return -1, nil
77 }
78
79 // ReadLinesFromPipe reads from the given pipe and logs its output.
80 func ReadLinesFromPipe(pipe io.Reader, lines chan string) {
81 buf := []byte{}
82
83 // writeLine recovers from a panic when writing to the channel.
84 writeLine := func(s string) {
85 defer func() {
86 if r := recover(); r != nil {
87 glog.Warningf("Panic writing to channel... are w e exiting?")
88 }
89 }()
90 lines <- s
91 }
92
93 // readLines reads output lines from the buffer and pushes them into the channel.
94 readlines := func() {
95 readIdx := 0
96 // Read lines from buf.
97 for i, b := range buf {
98 if b == '\n' {
99 s := string(buf[readIdx:i])
100 writeLine(s)
101 readIdx = i + 1
102 }
103 }
104 // Remove the lines we read.
105 buf = buf[readIdx:]
106 }
107
108 // Loop forever, reading bytes from the pipe.
109 readbuf := make([]byte, 4096)
110 for {
111 read, err := pipe.Read(readbuf)
112 if read > 0 {
113 buf = append(buf, readbuf[:read]...)
114
115 // Read lines from the buffers.
116 readlines()
117 }
118 if err != nil {
119 if err == io.EOF {
120 break
121 } else {
122 glog.Error(err)
123 }
124 } else if read == 0 {
125 time.Sleep(20 * time.Millisecond)
126 }
127 }
128 // Read any remaining lines from the buffers.
129 readlines()
130 // "Flush" any incomplete lines from the buffers.
131 writeLine(string(buf))
132 }
133
134 // RunApp launches the given app on the device, pipes its log output to stdout,
135 // and returns when the app finishes running, with an error if the app did not
136 // complete successfully.
137 func RunApp(adb, serial, appName string, args []string) error {
138 // Kill any running instances of the app.
139 if err := KillSkia(adb, serial); err != nil {
140 return fmt.Errorf("Failed to kill app: %v", err)
141 }
142
143 // Clear the ADB log.
144 if _, err := RunADB(adb, serial, false, "logcat", "-c"); err != nil {
145 return fmt.Errorf("Failed to clear ADB log: %v", err)
146 }
147
148 // Prepare to read the subprocess output.
149 logProc := ADB(adb, serial, false, "logcat")
150 defer func() {
151 // Cleanup.
152 if err := logProc.Process.Kill(); err != nil {
153 glog.Errorf("Failed to kill logcat process: %v", err)
154 }
155 }()
156
157 stdout, err := logProc.StdoutPipe()
158 if err != nil {
159 return fmt.Errorf("Failed to obtain stdout pipe: %v", err)
160 }
161 stdoutLines := make(chan string)
162 stderr, err := logProc.StderrPipe()
163 if err != nil {
164 return fmt.Errorf("Failed to obtain stderr pipe: %v", err)
165 }
166 stderrLines := make(chan string)
167
168 go ReadLinesFromPipe(stdout, stdoutLines)
169 go ReadLinesFromPipe(stderr, stderrLines)
170 if err := logProc.Start(); err != nil {
171 return fmt.Errorf("Failed to start logcat process.")
172 }
173
174 // Launch the app.
175 stop := make(chan bool, 1)
176 activity := fmt.Sprintf("%s.%s/%s.%sActivity", COM_SKIA, strings.ToLower (appName), COM_SKIA, appName)
177 flags := strings.Join(args, " ")
178 cmd := []string{"shell", "am", "start", "-S", "-n", activity, "--es", "c mdLineFlags", flags}
179 output, err := RunADB(adb, serial, true, cmd...)
180 if err != nil {
181 return fmt.Errorf("Failed to launch app: %v", err)
182 }
183 glog.Info(string(output))
184 // Make a handler for SIGINT so that we can kill the app if this script is interrupted.
185 go func() {
186 interrupt := make(chan os.Signal, 1)
187 signal.Notify(interrupt, os.Interrupt)
188 for _ = range interrupt {
189 glog.Infof("Received SIGINT; killing app.")
190 stop <- true
191 close(stdoutLines)
192 close(stderrLines)
193 if err := KillSkia(adb, serial); err != nil {
194 glog.Errorf("Failed to kill app: %v", err)
195 }
196 if err := logProc.Process.Kill(); err != nil {
197 glog.Errorf("Failed to kill logcat process: %v", err)
198 }
199 }
200 }()
201
202 // Loop until the app finishes or we're interrupted, writing output as a ppropriate.
203 // TODO(borenet): VisualBench should print its exit code. This script sh ould exit
204 // with that code, or a non-zero code if the process ended without print ing any code.
205 second := time.Tick(time.Second)
206 PollLoop:
207 for {
208 select {
209 case <-second:
210 // Poll the Skia process every second to make sure it's still running.
211 pid, err := PollSkia(adb, serial)
212 if err != nil {
213 glog.Errorf("Failed to poll Skia process: %v", e rr)
214 } else if pid < 0 {
215 glog.Infof("Skia process is no longer running!")
216 break PollLoop
217 }
218 case <-stop:
219 break PollLoop
220 case line := <-stdoutLines:
221 glog.Info(line)
222 case line := <-stderrLines:
223 glog.Error(line)
224 }
225 }
226
227 return nil
228 }
229
230 func main() {
231 common.Init()
232 args := flag.Args()
233 adbPath := *adb
234 if adbPath == "" {
235 adbPath = "adb"
236 }
237 if err := RunApp(adbPath, *serial, *app, args); err != nil {
238 glog.Fatal(err)
239 }
240 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698