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

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: Use pointer receiver 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) 2015 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 adbPath = 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 use.")
34 )
35
36 // Struct used for running ADB commands.
37 type ADB struct {
38 path string
39 serial string
40 }
41
42 // ADBFromFlags returns an ADB instance based on flags passed to the program.
43 func ADBFromFlags() *ADB {
44 rv := &ADB{
45 path: *adbPath,
46 serial: *serial,
47 }
48 if rv.path == "" {
49 rv.path = "adb"
50 }
51 return rv
52 }
53
54 // Cmd creates an exec.Cmd object for the given ADB command.
55 func (adb *ADB) Cmd(log bool, args ...string) *exec.Cmd {
56 cmd := []string{}
57 if adb.serial != "" {
58 cmd = append(cmd, "-s", adb.serial)
59 }
60 cmd = append(cmd, args...)
61 if log {
62 glog.Infof("Exec `%s %s`", adb.path, strings.Join(cmd, " "))
63 }
64 return exec.Command(adb.path, cmd...)
65 }
66
67 // KillSkia kills any running process.
68 func (adb *ADB) Kill(proc string) error {
69 return adb.Cmd(false, "shell", "am", "force-stop", proc).Run()
70 }
71
72 // PollSkia checks to see if the given process is running. If so, return the pid , otherwise return -1.
73 func (adb *ADB) Poll(proc string) (int64, error) {
74 output, err := adb.Cmd(false, "shell", "ps").Output()
75 if err != nil {
76 return -1, fmt.Errorf("Failed to check the running processes on the device: %v", err)
77 }
78 for _, line := range strings.Split(string(output), "\n") {
79 if strings.Contains(line, proc) {
80 fields := strings.Fields(line)
81 pid, err := strconv.ParseInt(fields[1], 10, 32)
82 if err != nil {
83 return -1, fmt.Errorf("Failed to parse \"%s\" to an integer: %v", fields[1], err)
84 }
85 return pid, nil
86 }
87 }
88 return -1, nil
89 }
90
91 // ReadLinesFromPipe reads from the given pipe and logs its output.
92 func ReadLinesFromPipe(pipe io.Reader, lines chan string) {
93 buf := []byte{}
94
95 // writeLine recovers from a panic when writing to the channel.
96 writeLine := func(s string) {
97 defer func() {
98 if r := recover(); r != nil {
99 glog.Warningf("Panic writing to channel... are w e exiting?")
100 }
101 }()
102 lines <- s
103 }
104
105 // readLines reads output lines from the buffer and pushes them into the channel.
106 readlines := func() {
107 readIdx := 0
108 // Read lines from buf.
109 for i, b := range buf {
110 if b == '\n' {
111 s := string(buf[readIdx:i])
112 writeLine(s)
113 readIdx = i + 1
114 }
115 }
116 // Remove the lines we read.
117 buf = buf[readIdx:]
118 }
119
120 // Loop forever, reading bytes from the pipe.
121 readbuf := make([]byte, 4096)
122 for {
123 read, err := pipe.Read(readbuf)
124 if read > 0 {
125 buf = append(buf, readbuf[:read]...)
126
127 // Read lines from the buffers.
128 readlines()
129 }
130 if err != nil {
131 if err == io.EOF {
132 break
133 } else {
134 glog.Error(err)
135 }
136 } else if read == 0 {
137 time.Sleep(20 * time.Millisecond)
138 }
139 }
140 // Read any remaining lines from the buffers.
141 readlines()
142 // "Flush" any incomplete lines from the buffers.
143 writeLine(string(buf))
144 }
145
146 // RunApp launches the given app on the device, pipes its log output to stdout,
147 // and returns when the app finishes running, with an error if the app did not
148 // complete successfully.
149 func RunApp(adb *ADB, appName string, args []string) error {
150 // Kill any running instances of the app.
151 if err := adb.Kill(COM_SKIA); err != nil {
152 return fmt.Errorf("Failed to kill app: %v", err)
153 }
154
155 // Clear the ADB log.
156 if err := adb.Cmd(false, "logcat", "-c").Run(); err != nil {
157 return fmt.Errorf("Failed to clear ADB log: %v", err)
158 }
159
160 // Prepare to read the subprocess output.
161 logProc := adb.Cmd(false, "logcat")
162 defer func() {
163 // Cleanup.
164 if err := logProc.Process.Kill(); err != nil {
165 glog.Errorf("Failed to kill logcat process: %v", err)
166 }
167 }()
168
169 stdout, err := logProc.StdoutPipe()
170 if err != nil {
171 return fmt.Errorf("Failed to obtain stdout pipe: %v", err)
172 }
173 stdoutLines := make(chan string)
174 stderr, err := logProc.StderrPipe()
175 if err != nil {
176 return fmt.Errorf("Failed to obtain stderr pipe: %v", err)
177 }
178 stderrLines := make(chan string)
179
180 go ReadLinesFromPipe(stdout, stdoutLines)
181 go ReadLinesFromPipe(stderr, stderrLines)
182 if err := logProc.Start(); err != nil {
183 return fmt.Errorf("Failed to start logcat process.")
184 }
185
186 // Launch the app.
187 stop := make(chan bool, 1)
188 activity := fmt.Sprintf("%s.%s/%s.%sActivity", COM_SKIA, strings.ToLower (appName), COM_SKIA, appName)
189 flags := strings.Join(args, " ")
190 cmd := []string{"shell", "am", "start", "-S", "-n", activity, "--es", "c mdLineFlags", flags}
191 output, err := adb.Cmd(true, cmd...).Output()
192 if err != nil {
193 return fmt.Errorf("Failed to launch app: %v", err)
194 }
195 glog.Info(string(output))
196 // Make a handler for SIGINT so that we can kill the app if this script is interrupted.
197 go func() {
198 interrupt := make(chan os.Signal, 1)
199 signal.Notify(interrupt, os.Interrupt)
200 for _ = range interrupt {
201 glog.Infof("Received SIGINT; killing app.")
202 stop <- true
203 close(stdoutLines)
204 close(stderrLines)
205 if err := adb.Kill(COM_SKIA); err != nil {
206 glog.Errorf("Failed to kill app: %v", err)
207 }
208 if err := logProc.Process.Kill(); err != nil {
209 glog.Errorf("Failed to kill logcat process: %v", err)
210 }
211 }
212 }()
213
214 // Loop until the app finishes or we're interrupted, writing output as a ppropriate.
215 // TODO(borenet): VisualBench should print its exit code. This script sh ould exit
216 // with that code, or a non-zero code if the process ended without print ing any code.
217 second := time.Tick(time.Second)
218 PollLoop:
219 for {
220 select {
221 case <-second:
222 // Poll the Skia process every second to make sure it's still running.
223 pid, err := adb.Poll(COM_SKIA)
224 if err != nil {
225 glog.Errorf("Failed to poll Skia process: %v", e rr)
226 } else if pid < 0 {
227 glog.Infof("Skia process is no longer running!")
228 break PollLoop
229 }
230 case <-stop:
231 break PollLoop
232 case line := <-stdoutLines:
233 glog.Info(line)
234 case line := <-stderrLines:
235 glog.Error(line)
236 }
237 }
238
239 return nil
240 }
241
242 func main() {
243 common.Init()
244 args := flag.Args()
245 if err := RunApp(ADBFromFlags(), *app, args); err != nil {
246 glog.Fatal(err)
247 }
248 }
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