blob: 864a91e77e1037006a8db6a3042d6f2521b83577 [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 executor contains the Execute function for sending a replay to a device.
package executor
import (
"bytes"
"fmt"
"io"
"android.googlesource.com/platform/tools/gpu/binary"
"android.googlesource.com/platform/tools/gpu/binary/endian"
"android.googlesource.com/platform/tools/gpu/binary/flat"
"android.googlesource.com/platform/tools/gpu/database"
"android.googlesource.com/platform/tools/gpu/device"
"android.googlesource.com/platform/tools/gpu/log"
"android.googlesource.com/platform/tools/gpu/replay/builder"
"android.googlesource.com/platform/tools/gpu/replay/protocol"
)
type executor struct {
payload protocol.Payload
decoder builder.ResponseDecoder
connection io.ReadWriteCloser
database database.Database
logger log.Logger
architecture device.Architecture
}
// Execute sends the replay payload for execution on the target replay device
// communicating on connection.
// decoder will be used for decoding all postback reponses. Once a postback
// response is decoded, the corresponding handler in the handlers map will be
// called.
func Execute(
payload protocol.Payload,
decoder builder.ResponseDecoder,
connection io.ReadWriteCloser,
database database.Database,
logger log.Logger,
architecture device.Architecture) error {
return executor{
payload: payload,
decoder: decoder,
connection: connection,
database: database,
logger: logger,
architecture: architecture,
}.execute()
}
func (r executor) execute() error {
// Encode the payload
buf := &bytes.Buffer{}
e := flat.Encoder(endian.Writer(buf, r.architecture.ByteOrder))
if err := e.Value(&r.payload); err != nil {
return err
}
data := buf.Bytes()
// Store the payload to the database
id, err := database.Store(data, r.database, r.logger)
if err != nil {
return err
}
// Kick the communication handler
responseR, responseW := io.Pipe()
comErr := make(chan error)
go func() {
err := r.handleReplayCommunication(id, uint32(len(data)), responseW)
if err != nil {
log.Warningf(r.logger, "Replay communication failed with error: %v", err)
if closeErr := responseW.CloseWithError(err); closeErr != nil {
log.Warningf(r.logger, "Replay execute pipe writer CloseWithError failed: %v after error %v", closeErr, err)
}
} else {
if closeErr := responseW.Close(); closeErr != nil {
log.Warningf(r.logger, "Replay execute pipe writer Close failed: %v", closeErr)
}
}
comErr <- err
}()
// Decode and handle postbacks as they are received
r.decoder(responseR, nil)
err = <-comErr
if closeErr := responseR.Close(); closeErr != nil {
log.Warningf(r.logger, "Replay execute pipe reader Close failed: %v", closeErr)
}
return err
}
func (r executor) handleReplayCommunication(replayID binary.ID, replaySize uint32, postbacks io.WriteCloser) error {
connection := r.connection
defer connection.Close()
e := flat.Encoder(endian.Writer(connection, r.architecture.ByteOrder))
d := flat.Decoder(endian.Reader(connection, r.architecture.ByteOrder))
if err := e.Uint8(uint8(protocol.ConnectionTypeReplay)); err != nil {
return err
}
if err := e.String(replayID.String()); err != nil {
return err
}
if err := e.Uint32(replaySize); err != nil {
return err
}
for {
msg, err := d.Uint8()
switch {
case err == io.EOF:
return nil
case err != nil:
return err
}
switch protocol.MessageType(msg) {
case protocol.MessageTypeGet:
if err := r.handleGetData(); err != nil {
return fmt.Errorf("Failed to read replay postback data: %v", err)
}
case protocol.MessageTypePost:
if err := r.handleDataResponse(postbacks); err != nil {
return fmt.Errorf("Failed to send replay resource data: %v", err)
}
default:
return fmt.Errorf("Unknown message type: %v\n", msg)
}
}
}
func (r executor) handleDataResponse(postbacks io.Writer) error {
d := flat.Decoder(endian.Reader(r.connection, r.architecture.ByteOrder))
n, err := d.Uint32()
if err != nil {
return err
}
c, err := io.CopyN(postbacks, r.connection, int64(n))
if c != int64(n) {
return err
}
return nil
}
func (r executor) handleGetData() error {
logger := log.Enter(r.logger, "handleGetData")
d := flat.Decoder(endian.Reader(r.connection, r.architecture.ByteOrder))
resourceCount, err := d.Uint32()
if err != nil {
return err
}
resourceIDs := make([]binary.ID, resourceCount)
for i := range resourceIDs {
idString, err := d.String()
if err != nil {
return err
}
resourceIDs[i], err = binary.ParseID(idString)
if err != nil {
return err
}
}
for _, rid := range resourceIDs {
data, err := database.Resolve(rid, r.database, logger)
if err != nil {
return err
}
if _, err := r.connection.Write(data.([]byte)); err != nil {
return err
}
}
return nil
}