Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 35 additions & 26 deletions cmd/gapit/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,34 @@ func (verb *traceVerb) Run(ctx context.Context, flags flag.FlagSet) error {
}
}

ctx, start := verb.inputHandler(ctx, options.Flags&client.DeferStart != 0)

if verb.Local.Port != 0 {
return verb.captureLocal(ctx, flags, verb.Local.Port, options)
return verb.captureLocal(ctx, flags, verb.Local.Port, start, options)
}

return verb.captureADB(ctx, flags, options)
return verb.captureADB(ctx, flags, start, options)
}

func (verb *traceVerb) inputHandler(ctx context.Context, deferStart bool) (context.Context, task.Signal) {
if verb.For > 0 {
return ctx, task.FiredSignal
}
startSignal, start := task.NewSignal()
var cancel task.CancelFunc
ctx, cancel = task.WithCancel(ctx)
go func() {
reader := bufio.NewReader(os.Stdin)
if deferStart {
println("Press enter to start capturing...")
_, _ = reader.ReadString('\n')
start(ctx)
}
println("Press enter to stop capturing...")
_, _ = reader.ReadString('\n')
cancel()
}()
return ctx, startSignal
}

func (verb *traceVerb) startLocalApp(ctx context.Context) (func(), error) {
Expand Down Expand Up @@ -138,15 +161,16 @@ func (verb *traceVerb) startLocalApp(ctx context.Context) (func(), error) {
return func() { cancel(); cleanup() }, nil
}

func (verb *traceVerb) captureLocal(ctx context.Context, flags flag.FlagSet, port int, options client.Options) error {
func (verb *traceVerb) captureLocal(ctx context.Context, flags flag.FlagSet, port int, start task.Signal, options client.Options) error {
output := verb.Out
if output == "" {
output = "capture.gfxtrace"
}
return doCapture(ctx, options, port, output, verb.For)
process := &client.Process{Port: port, Options: options}
return doCapture(ctx, process, output, start, verb.For)
}

func (verb *traceVerb) captureADB(ctx context.Context, flags flag.FlagSet, options client.Options) error {
func (verb *traceVerb) captureADB(ctx context.Context, flags flag.FlagSet, start task.Signal, options client.Options) error {
d, err := getADBDevice(ctx, verb.Gapii.Device)
if err != nil {
return err
Expand Down Expand Up @@ -265,11 +289,10 @@ func (verb *traceVerb) captureADB(ctx context.Context, flags flag.FlagSet, optio
defer d.TurnScreenOff(ctx) // Think green!
}

port, cleanup, err := client.StartOrAttach(ctx, pkg, a)
process, err := client.StartOrAttach(ctx, pkg, a, options)
if err != nil {
return err
}
defer cleanup(ctx)

ctx, stop := task.WithCancel(ctx)
if verb.Record.Inputs {
Expand All @@ -286,10 +309,10 @@ func (verb *traceVerb) captureADB(ctx context.Context, flags flag.FlagSet, optio
}
}

return doCapture(ctx, options, int(port), output, verb.For)
return doCapture(ctx, process, output, start, verb.For)
}

func doCapture(ctx context.Context, options client.Options, port int, out string, duration time.Duration) error {
func doCapture(ctx context.Context, process *client.Process, out string, start task.Signal, duration time.Duration) error {
log.I(ctx, "Creating file '%v'", out)
os.MkdirAll(filepath.Dir(out), 0755)
file, err := os.Create(out)
Expand All @@ -298,25 +321,11 @@ func doCapture(ctx context.Context, options client.Options, port int, out string
}
defer file.Close()

signal, fireSignal := task.NewSignal()
if duration == 0 {
var cancel task.CancelFunc
ctx, cancel = task.WithCancel(ctx)
go func() {
reader := bufio.NewReader(os.Stdin)
if (options.Flags & client.DeferStart) != 0 {
println("Press enter to start capturing...")
_, _ = reader.ReadString('\n')
fireSignal(ctx)
}
println("Press enter to stop capturing...")
_, _ = reader.ReadString('\n')
cancel()
}()
} else {
if duration > 0 {
ctx, _ = task.WithTimeout(ctx, duration)
}
_, err = client.Capture(ctx, port, signal, file, options)

_, err = process.Capture(ctx, start, file)
if err != nil {
return err
}
Expand Down
25 changes: 25 additions & 0 deletions core/event/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package task
import (
"context"
"sync"
"time"
)

// Task is the unit of work used in the task system.
Expand All @@ -33,3 +34,27 @@ func Once(task Task) Task {
return err
}
}

// Retry repeatedly calls task until task returns a nil error, the number
// attempts reaches maxAttempts or the context is cancelled. Retry will sleep
// for retryDelay between retry attempts.
// if maxAttempts <= 0, then there is no maximum limit to the number of times
// task will be called.
func Retry(ctx context.Context, maxAttempts int, retryDelay time.Duration, task Task) error {
var count int
for {
err := task(ctx)
if err == nil {
return nil
}
count++
if maxAttempts > 0 && count >= maxAttempts {
return err
}
select {
case <-ShouldStop(ctx):
return StopReason(ctx)
case <-time.After(retryDelay):
}
}
}
32 changes: 9 additions & 23 deletions gapii/cc/connection_header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,35 +49,21 @@ bool ConnectionHeader::read(core::StreamReader* reader) {
return false;
}

const int kMinSupportedVersion = 2;
const int kMaxSupportedVersion = 5;
const int kMinSupportedVersion = 1;
const int kMaxSupportedVersion = 1;

if (mVersion < kMinSupportedVersion || mVersion > kMaxSupportedVersion) {
GAPID_WARNING("Unsupported ConnectionHeader version %d. Only understand [%d to %d].",
mVersion, kMinSupportedVersion, kMaxSupportedVersion);
return false;
}
if (mVersion >= 2) {
if (!reader->read(mObserveFrameFrequency) ||
!reader->read(mObserveDrawFrequency)) {
return false;
}
}
if (mVersion >= 4) {
if (!reader->read(mStartFrame) ||
!reader->read(mNumFrames)) {
return false;
}
}
if (mVersion >= 5) {
if (!reader->read(mAPIs)) {
return false;
}
}
if (mVersion >= 3) {
if (!reader->read(mFlags)) {
return false;
}
if (!reader->read(mObserveFrameFrequency) ||
!reader->read(mObserveDrawFrequency) ||
!reader->read(mStartFrame) ||
!reader->read(mNumFrames) ||
!reader->read(mAPIs) ||
!reader->read(mFlags)) {
return false;
}

// Insert new version handling here. Don't forget to bump kMaxSupportedVersion!
Expand Down
14 changes: 7 additions & 7 deletions gapii/cc/connection_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ class ConnectionHeader {
bool read(core::StreamReader* reader);

uint8_t mMagic[4]; // 's', 'p', 'y', '0'
uint32_t mVersion; // 2 or 3
uint32_t mObserveFrameFrequency; // non-zero == enabled. Version: 2+
uint32_t mObserveDrawFrequency; // non-zero == enabled. Version: 2+
uint32_t mStartFrame; // non-zero == Frame to start at. version 4+
uint32_t mNumFrames; // non-zero == Number of frames to capture. version 4+
uint32_t mAPIs; // Bitset of APIS to enable. version 5+
uint32_t mFlags; // Combination of FLAG_XX bits. Version: 3+
uint32_t mVersion; // 1
uint32_t mObserveFrameFrequency; // non-zero == enabled.
uint32_t mObserveDrawFrequency; // non-zero == enabled.
uint32_t mStartFrame; // non-zero == Frame to start at.
uint32_t mNumFrames; // non-zero == Number of frames to capture.
uint32_t mAPIs; // Bitset of APIS to enable.
uint32_t mFlags; // Combination of FLAG_XX bits.
};

} // namespace gapii
Expand Down
1 change: 1 addition & 0 deletions gapii/cc/spy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ extern "C"
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
GAPID_INFO("JNI_OnLoad() was called. vm = %p", vm);
gJavaVM = vm;
gapii::Spy::get(); // Construct the spy.
return JNI_VERSION_1_6;
}
void* queryPlatformData() {
Expand Down
65 changes: 42 additions & 23 deletions gapii/client/adb.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ package client

import (
"context"
"net"
"time"

"github.com/google/gapid/core/event/task"
"github.com/google/gapid/core/app"
"github.com/google/gapid/core/context/keys"
"github.com/google/gapid/core/log"
"github.com/google/gapid/core/os/android"
"github.com/google/gapid/core/os/android/adb"
Expand All @@ -33,11 +35,23 @@ const (
getPidRetries = 7
)

// Process represents a running process to capture.
type Process struct {
// The local host port used to connect to GAPII.
Port int

// The options used for the capture.
Options Options

// The connection
conn net.Conn
}

// StartOrAttach launches an activity on an android device with the GAPII interceptor
// enabled using the gapid.apk built for the ABI matching the specified action and device.
// If there is no activity provided, it will try to attach to any already running one.
// GAPII will attempt to connect back on the returned host port to write the trace.
func StartOrAttach(ctx context.Context, p *android.InstalledPackage, a *android.ActivityAction) (port adb.TCPPort, cleanup task.Task, err error) {
func StartOrAttach(ctx context.Context, p *android.InstalledPackage, a *android.ActivityAction, o Options) (*Process, error) {
ctx = log.Enter(ctx, "start")
if a != nil {
ctx = log.V{"activity": a.Activity}.Bind(ctx)
Expand All @@ -53,7 +67,7 @@ func StartOrAttach(ctx context.Context, p *android.InstalledPackage, a *android.

log.I(ctx, "Turning device screen on")
if err := d.TurnScreenOn(ctx); err != nil {
return 0, nil, log.Err(ctx, err, "Couldn't turn device screen on")
return nil, log.Err(ctx, err, "Couldn't turn device screen on")
}

log.I(ctx, "Checking for lockscreen")
Expand All @@ -62,49 +76,50 @@ func StartOrAttach(ctx context.Context, p *android.InstalledPackage, a *android.
log.W(ctx, "Couldn't determine lockscreen state: %v", err)
}
if locked {
return 0, nil, log.Err(ctx, nil, "Cannot trace app on locked device")
return nil, log.Err(ctx, nil, "Cannot trace app on locked device")
}

port, err = adb.LocalFreeTCPPort()
port, err := adb.LocalFreeTCPPort()
if err != nil {
return 0, nil, log.Err(ctx, err, "Finding free port")
return nil, log.Err(ctx, err, "Finding free port")
}

log.I(ctx, "Checking gapid.apk is installed")
apk, err := gapidapk.EnsureInstalled(ctx, d, abi)
if err != nil {
return 0, nil, log.Err(ctx, err, "Installing gapid.apk")
return nil, log.Err(ctx, err, "Installing gapid.apk")
}

ctx = log.V{"port": port}.Bind(ctx)

log.I(ctx, "Forwarding")
if err := d.Forward(ctx, adb.TCPPort(port), adb.NamedAbstractSocket("gapii")); err != nil {
return 0, nil, log.Err(ctx, err, "Setting up port forwarding")
return nil, log.Err(ctx, err, "Setting up port forwarding")
}

removeForward := func(ctx context.Context) error {
// Clone context to ignore cancellation.
ctx = keys.Clone(context.Background(), ctx)
return d.RemoveForward(ctx, port)
}

// FileDir may fail here. This happens if/when the app is non-debuggable.
// Don't set up vulkan tracing here, since the loader will not try and load the layer
// if we aren't debuggable regardless.
if err := d.Command("shell", "setprop", "debug.vulkan.layers", "VkGraphicsSpy").Run(ctx); err != nil {
d.RemoveForward(ctx, adb.TCPPort(port))
return 0, nil, log.Err(ctx, err, "Setting up vulkan layer")
removeForward(ctx)
return nil, log.Err(ctx, err, "Setting up vulkan layer")
}

doCleanup := func(ctx context.Context) error {
app.AddCleanup(ctx, func() {
d.Command("shell", "setprop", "debug.vulkan.layers", "\"\"").Run(ctx)
return d.RemoveForward(ctx, adb.TCPPort(port))
}
defer func() {
if err != nil {
doCleanup(ctx)
}
}()
removeForward(ctx)
})

if a != nil {
log.I(ctx, "Starting activity in debug mode")
if err := d.StartActivityForDebug(ctx, *a); err != nil {
return 0, nil, log.Err(ctx, err, "Starting activity in debug mode")
return nil, log.Err(ctx, err, "Starting activity in debug mode")
}
} else {
log.I(ctx, "No start activity selected - trying to attach...")
Expand All @@ -117,13 +132,17 @@ func StartOrAttach(ctx context.Context, p *android.InstalledPackage, a *android.
pid, err = p.Pid(ctx)
}
if err != nil {
return 0, nil, log.Err(ctx, err, "Getting pid")
return nil, log.Err(ctx, err, "Getting pid")
}
ctx = log.V{"pid": pid}.Bind(ctx)

if err := loadLibrariesViaJDWP(ctx, apk, pid, d); err != nil {
return 0, nil, err
process := &Process{
Port: int(port),
Options: o,
}
if err := process.loadAndConnectViaJDWP(ctx, apk, pid, d); err != nil {
return nil, err
}

return port, doCleanup, nil
return process, nil
}
Loading