blob: 372c72cf56548911e6ea780242c7d097346015e2 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package replay
import (
"fmt"
"io"
"net"
"os"
"os/exec"
"time"
"android.googlesource.com/platform/tools/gpu/atexit"
"android.googlesource.com/platform/tools/gpu/service"
)
// DisableLocalDeviceCache can be used to disable the disk-cache for the local
// device. If true, it is passed as a flag to replayd on spawning. This can be
// used for disabling the cache for tests.
var DisableLocalDeviceCache = false
// deviceOS is an enumerator of operating systems that the replay target may be
// running on.
type deviceOS uint8
// These must be kept in sync with TARGET_OS in cc/common/gapic/target.h
const (
osLinux deviceOS = 1
osOSX deviceOS = 2
osWindows deviceOS = 3
osAndroid deviceOS = 4
)
func (os deviceOS) IsLinux() bool { return os == osLinux }
func (os deviceOS) IsOSX() bool { return os == osOSX }
func (os deviceOS) IsWindows() bool { return os == osWindows }
func (os deviceOS) IsAndroid() bool { return os == osAndroid }
func (os deviceOS) String() string {
switch os {
case osLinux:
return "linux"
case osOSX:
return "darwin"
case osWindows:
return "windows"
case osAndroid:
return "android"
default:
return fmt.Sprintf("Unknown<%d>", os)
}
}
// Device is the interface for a discovered replay device.
type Device interface {
// ID returns the identifier for the replay device.
ID() service.DeviceId
// Info returns the service Device describing the replay device.
Info() *service.Device
// Connect opens a connection to the replay device.
Connect() (io.ReadWriteCloser, error)
}
type deviceBase struct {
id service.DeviceId
device *service.Device
}
func (d deviceBase) ID() service.DeviceId {
return d.id
}
func (d deviceBase) Info() *service.Device {
return d.device
}
type androidDevice struct {
deviceBase
}
type localDevice struct {
deviceBase
}
func (androidDevice) Connect() (io.ReadWriteCloser, error) {
return net.Dial("tcp", "localhost:9285") // TODO: Remove the hardcoded port number.
}
func (localDevice) Connect() (io.ReadWriteCloser, error) {
endpoint := "localhost:9284" // TODO: Remove the hardcoded port number.
conn, err := net.Dial("tcp", endpoint)
if err != nil {
if err := spawnChild(replayd); err != nil {
return nil, err
}
for i := 0; i < 10; i++ {
conn, err = net.Dial("tcp", endpoint)
if err == nil {
return conn, err
}
time.Sleep(100 * time.Millisecond)
}
}
return conn, err
}
func spawnChild(path string) error {
null, err := os.OpenFile(os.DevNull, os.O_RDWR, 0)
if err != nil {
return err
}
path, err = exec.LookPath(path)
if err != nil {
return err
}
args := []string{path}
if DisableLocalDeviceCache {
args = append(args, "--nocache")
}
proc, err := os.StartProcess(path, args, &os.ProcAttr{
Files: []*os.File{null, null, null},
})
if err != nil {
return err
}
// Kill the child process on parent exit.
atexit.Register(func() {
proc.Kill()
proc.Wait()
}, time.Second)
return nil
}