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

Unified 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, 5 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: platform_tools/android/bin/android_run_app.go
diff --git a/platform_tools/android/bin/android_run_app.go b/platform_tools/android/bin/android_run_app.go
new file mode 100644
index 0000000000000000000000000000000000000000..69cc48a113e2b222f438293c83abef3457d07320
--- /dev/null
+++ b/platform_tools/android/bin/android_run_app.go
@@ -0,0 +1,240 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ Run a Skia app to completion, piping the log to stdout.
+*/
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "os/signal"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/skia-dev/glog"
+ "go.skia.org/infra/go/common"
+)
+
+const (
+ COM_SKIA = "com.skia"
+)
+
+var (
+ adb = flag.String("adb", "", "Path to the ADB executable.")
+ app = flag.String("app", "VisualBench", "Name of the app to run.")
+ serial = flag.String("s", "", "Serial number for the Android device to use.")
+)
+
+// ADB returns an os.Cmd for the given ADB command.
+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
+ cmd := []string{}
+ if serial != "" {
+ cmd = append(cmd, "-s", serial)
+ }
+ cmd = append(cmd, args...)
+ if log {
+ glog.Infof("Exec `%s %s`", adb, strings.Join(cmd, " "))
+ }
+ return exec.Command(adb, cmd...)
+}
+
+// RunADB runs the given command through ADB.
+func RunADB(adb, serial string, log bool, args ...string) ([]byte, error) {
+ 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.
+}
+
+// KillSkia kills any running Skia process.
+func KillSkia(adb, serial string) error {
+ _, err := RunADB(adb, serial, false, "shell", "am", "force-stop", COM_SKIA)
+ return err
+}
+
+// PollSkia checks to see if any Skia process is running. If so, return the pid, otherwise return -1.
+func PollSkia(adb, serial string) (int64, error) {
+ 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.
+ if err != nil {
+ return -1, fmt.Errorf("Failed to check the running processes on the device: %v", err)
+ }
+ for _, line := range strings.Split(string(output), "\n") {
+ if strings.Contains(line, COM_SKIA) {
+ fields := strings.Fields(line)
+ pid, err := strconv.ParseInt(fields[1], 10, 32)
+ if err != nil {
+ return -1, fmt.Errorf("Failed to parse \"%s\" to an integer: %v", fields[1], err)
+ }
+ return pid, nil
+ }
+ }
+ return -1, nil
+}
+
+// ReadLinesFromPipe reads from the given pipe and logs its output.
+func ReadLinesFromPipe(pipe io.Reader, lines chan string) {
+ buf := []byte{}
+
+ // writeLine recovers from a panic when writing to the channel.
+ writeLine := func(s string) {
+ defer func() {
+ if r := recover(); r != nil {
+ glog.Warningf("Panic writing to channel... are we exiting?")
+ }
+ }()
+ lines <- s
+ }
+
+ // readLines reads output lines from the buffer and pushes them into the channel.
+ readlines := func() {
+ readIdx := 0
+ // Read lines from buf.
+ for i, b := range buf {
+ if b == '\n' {
+ s := string(buf[readIdx:i])
+ writeLine(s)
+ readIdx = i + 1
+ }
+ }
+ // Remove the lines we read.
+ buf = buf[readIdx:]
+ }
+
+ // Loop forever, reading bytes from the pipe.
+ readbuf := make([]byte, 4096)
+ for {
+ read, err := pipe.Read(readbuf)
+ if read > 0 {
+ buf = append(buf, readbuf[:read]...)
+
+ // Read lines from the buffers.
+ readlines()
+ }
+ if err != nil {
+ if err == io.EOF {
+ break
+ } else {
+ glog.Error(err)
+ }
+ } else if read == 0 {
+ time.Sleep(20 * time.Millisecond)
+ }
+ }
+ // Read any remaining lines from the buffers.
+ readlines()
+ // "Flush" any incomplete lines from the buffers.
+ writeLine(string(buf))
+}
+
+// RunApp launches the given app on the device, pipes its log output to stdout,
+// and returns when the app finishes running, with an error if the app did not
+// complete successfully.
+func RunApp(adb, serial, appName string, args []string) error {
+ // Kill any running instances of the app.
+ if err := KillSkia(adb, serial); err != nil {
+ return fmt.Errorf("Failed to kill app: %v", err)
+ }
+
+ // Clear the ADB log.
+ if _, err := RunADB(adb, serial, false, "logcat", "-c"); err != nil {
+ return fmt.Errorf("Failed to clear ADB log: %v", err)
+ }
+
+ // Prepare to read the subprocess output.
+ logProc := ADB(adb, serial, false, "logcat")
+ defer func() {
+ // Cleanup.
+ if err := logProc.Process.Kill(); err != nil {
+ glog.Errorf("Failed to kill logcat process: %v", err)
+ }
+ }()
+
+ stdout, err := logProc.StdoutPipe()
+ if err != nil {
+ return fmt.Errorf("Failed to obtain stdout pipe: %v", err)
+ }
+ stdoutLines := make(chan string)
+ stderr, err := logProc.StderrPipe()
+ if err != nil {
+ return fmt.Errorf("Failed to obtain stderr pipe: %v", err)
+ }
+ stderrLines := make(chan string)
+
+ go ReadLinesFromPipe(stdout, stdoutLines)
+ go ReadLinesFromPipe(stderr, stderrLines)
+ if err := logProc.Start(); err != nil {
+ return fmt.Errorf("Failed to start logcat process.")
+ }
+
+ // Launch the app.
+ stop := make(chan bool, 1)
+ activity := fmt.Sprintf("%s.%s/%s.%sActivity", COM_SKIA, strings.ToLower(appName), COM_SKIA, appName)
+ flags := strings.Join(args, " ")
+ cmd := []string{"shell", "am", "start", "-S", "-n", activity, "--es", "cmdLineFlags", flags}
+ output, err := RunADB(adb, serial, true, cmd...)
+ if err != nil {
+ return fmt.Errorf("Failed to launch app: %v", err)
+ }
+ glog.Info(string(output))
+ // Make a handler for SIGINT so that we can kill the app if this script is interrupted.
+ go func() {
+ interrupt := make(chan os.Signal, 1)
+ signal.Notify(interrupt, os.Interrupt)
+ for _ = range interrupt {
+ glog.Infof("Received SIGINT; killing app.")
+ stop <- true
+ close(stdoutLines)
+ close(stderrLines)
+ if err := KillSkia(adb, serial); err != nil {
+ glog.Errorf("Failed to kill app: %v", err)
+ }
+ if err := logProc.Process.Kill(); err != nil {
+ glog.Errorf("Failed to kill logcat process: %v", err)
+ }
+ }
+ }()
+
+ // Loop until the app finishes or we're interrupted, writing output as appropriate.
+ // TODO(borenet): VisualBench should print its exit code. This script should exit
+ // with that code, or a non-zero code if the process ended without printing any code.
+ second := time.Tick(time.Second)
+PollLoop:
+ for {
+ select {
+ case <-second:
+ // Poll the Skia process every second to make sure it's still running.
+ pid, err := PollSkia(adb, serial)
+ if err != nil {
+ glog.Errorf("Failed to poll Skia process: %v", err)
+ } else if pid < 0 {
+ glog.Infof("Skia process is no longer running!")
+ break PollLoop
+ }
+ case <-stop:
+ break PollLoop
+ case line := <-stdoutLines:
+ glog.Info(line)
+ case line := <-stderrLines:
+ glog.Error(line)
+ }
+ }
+
+ return nil
+}
+
+func main() {
+ common.Init()
+ args := flag.Args()
+ adbPath := *adb
+ if adbPath == "" {
+ adbPath = "adb"
+ }
+ if err := RunApp(adbPath, *serial, *app, args); err != nil {
+ glog.Fatal(err)
+ }
+}
« 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