Skip to content

Commit

Permalink
Add support for taking Perfetto trace to GAPIS.
Browse files Browse the repository at this point in the history
  • Loading branch information
pmuetschard committed Apr 5, 2019
1 parent 63c7ba9 commit b3feb8b
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 3 deletions.
1 change: 1 addition & 0 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<classpathentry kind="lib" path="bazel-bin/core/log/log_pb/liblog_pb_proto-speed.jar" sourcepath="bazel-genfiles/core/log/log_pb/log_pb_proto-speed-src.jar"/>
<classpathentry kind="lib" path="bazel-bin/core/os/device/libdevice_proto-speed.jar" sourcepath="bazel-genfiles/core/os/device/device_proto-speed-src.jar"/>
<classpathentry kind="lib" path="bazel-bin/core/stream/libstream_proto-speed.jar" sourcepath="bazel-genfiles/core/stream/stream_proto-speed-src.jar"/>
<classpathentry kind="lib" path="bazel-bin/external/perfetto/libconfig_proto-speed.jar" sourcepath="bazel-genfiles/external/perfetto/config_proto-speed-src.jar"/>
<classpathentry kind="lib" path="bazel-bin/external/com_google_protobuf/libprotobuf_java.jar"/>
<classpathentry kind="lib" path="bazel-bin/gapidapk/pkginfo/libpkginfo_proto-speed.jar" sourcepath="bazel-genfiles/gapidapk/pkginfo/pkginfo_proto-speed-src.jar"/>
<classpathentry kind="lib" path="bazel-bin/gapis/api/libapi_proto-speed.jar" sourcepath="bazel-genfiles/gapis/api/api_proto-speed-src.jar"/>
Expand Down
2 changes: 2 additions & 0 deletions core/os/android/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//core/app:go_default_library",
"//core/event/task:go_default_library",
"//core/log:go_default_library",
"//core/os/device:go_default_library",
"//core/os/device/bind:go_default_library",
"@perfetto//:go_default_library",
],
)

Expand Down
3 changes: 3 additions & 0 deletions core/os/android/adb/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ go_library(
"inputs.go",
"installed_package.go",
"logcat.go",
"perfetto.go",
"screen.go",
],
importpath = "github.com/google/gapid/core/os/android/adb",
Expand All @@ -44,6 +45,8 @@ go_library(
"//core/os/device/bind:go_default_library",
"//core/os/file:go_default_library",
"//core/os/shell:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@perfetto//:go_default_library",
],
)

Expand Down
95 changes: 95 additions & 0 deletions core/os/android/adb/perfetto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (C) 2019 Google Inc.
//
// 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 adb

import (
"bufio"
"bytes"
"context"
"io"
"strings"

"perfetto_pb"

"github.com/golang/protobuf/proto"
"github.com/google/gapid/core/app/crash"
"github.com/google/gapid/core/event/task"
"github.com/google/gapid/core/log"
)

// StartPerfettoTrace starts a perfetto trace on this device.
func (b *binding) StartPerfettoTrace(ctx context.Context, config *perfetto_pb.TraceConfig, out string, stop task.Signal) error {
reader, stdout := io.Pipe()
data, err := proto.Marshal(config)
if err != nil {
return err
}

fail := make(chan error, 1)
crash.Go(func() {
buf := bufio.NewReader(reader)
for {
line, e := buf.ReadString('\n')
switch e {
default:
log.E(ctx, "[perfetto] Read error %v", e)
fail <- e
return
case io.EOF:
fail <- nil
return
case nil:
log.I(ctx, "[perfetto] %s", strings.TrimSuffix(line, "\n"))
}
}
})

process, err := b.Shell("perfetto", "-c", "-", "-o", out).
Read(bytes.NewReader(data)).
Capture(stdout, stdout).
Start(ctx)
if err != nil {
stdout.Close()
return err
}

wait := make(chan error, 1)
crash.Go(func() {
wait <- process.Wait(ctx)
})

select {
case err = <-fail:
return err
case err = <-wait:
// Do nothing.
case <-stop:
// TODO: figure out why "killall -2 perfetto" doesn't work.
var pid string
if pid, err = b.Shell("pidof perfetto").Call(ctx); err != nil {
break
}
if err = b.Shell("kill -2 " + pid).Run(ctx); err != nil {
break
}
err = <-wait
}

stdout.Close()
if err != nil {
return err
}
return <-fail
}
5 changes: 5 additions & 0 deletions core/os/android/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import (
"fmt"
"time"

"perfetto_pb"

"github.com/google/gapid/core/event/task"
"github.com/google/gapid/core/log"
"github.com/google/gapid/core/os/device"
"github.com/google/gapid/core/os/device/bind"
Expand Down Expand Up @@ -91,6 +94,8 @@ type Device interface {
// DeleteSystemSetting removes the system setting with with the given
// namespaced key.
DeleteSystemSetting(ctx context.Context, namespace, key string) error
// StartPerfettoTrace starts a perfetto trace.
StartPerfettoTrace(ctx context.Context, config *perfetto_pb.TraceConfig, out string, stop task.Signal) error
}

// LogcatMessage represents a single logcat message.
Expand Down
5 changes: 5 additions & 0 deletions gapis/perfetto/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ go_library(
srcs = [
"doc.go",
"processor.go",
"trace.go",
],
cdeps = ["//gapis/perfetto/cc:cc"],
cgo = True,
Expand All @@ -27,7 +28,11 @@ go_library(
deps = [
"//core/event/task:go_default_library",
"//core/log:go_default_library",
"//core/os/android:go_default_library",
"//core/os/android/adb:go_default_library",
"//core/os/file:go_default_library",
"//gapis/service:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@perfetto//:go_default_library",
],
)
113 changes: 113 additions & 0 deletions gapis/perfetto/trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (C) 2019 Google Inc.
//
// 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 perfetto

import (
"context"
"fmt"
"io"
"os"
"sync/atomic"

"perfetto_pb"

"github.com/google/gapid/core/event/task"
"github.com/google/gapid/core/log"
"github.com/google/gapid/core/os/android"
"github.com/google/gapid/core/os/android/adb"
"github.com/google/gapid/core/os/file"
"github.com/google/gapid/gapis/service"
)

const (
// perfettoTraceFile is the location on the device where we'll ask Perfetto
// to store the trace data while tracing.
perfettoTraceFile = "/data/misc/perfetto-traces/gapis-trace"
)

// Process represents a running Perfetto capture.
type Process struct {
device adb.Device
config *perfetto_pb.TraceConfig
deferred bool
}

// Start optional starts an app and sets up a Perfetto trace
func Start(ctx context.Context, d adb.Device, a *android.ActivityAction, opts *service.TraceOptions) (*Process, error) {
ctx = log.Enter(ctx, "start")
if a != nil {
ctx = log.V{
"package": a.Package.Name,
"activity": a.Activity,
}.Bind(ctx)
}

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

log.I(ctx, "Checking for lockscreen")
locked, err := d.IsShowingLockscreen(ctx)
if err != nil {
log.W(ctx, "Couldn't determine lockscreen state: %v", err)
}
if locked {
return nil, log.Err(ctx, nil, "Cannot trace app on locked device")
}

if a != nil {
if err := d.StartActivity(ctx, *a); err != nil {
return nil, log.Err(ctx, err, "Starting the activity")
}
}

return &Process{
device: d,
config: opts.PerfettoConfig,
deferred: opts.DeferStart,
}, nil
}

// Capture starts the perfetto capture.
func (p *Process) Capture(ctx context.Context, start task.Signal, stop task.Signal, w io.Writer, written *int64) (int64, error) {
tmp, err := file.Temp()
if err != nil {
return 0, log.Err(ctx, err, "Failed to create a temp file")
}

// Signal that we are ready to start.
atomic.StoreInt64(written, 1)

if p.deferred && !start.Wait(ctx) {
return 0, log.Err(ctx, nil, "Cancelled")
}

if err := p.device.StartPerfettoTrace(ctx, p.config, perfettoTraceFile, stop); err != nil {
return 0, err
}

if err := p.device.Pull(ctx, perfettoTraceFile, tmp.System()); err != nil {
return 0, err
}

size := tmp.Info().Size()
atomic.StoreInt64(written, size)
fh, err := os.Open(tmp.System())
if err != nil {
return 0, log.Err(ctx, err, fmt.Sprintf("Failed to open %s", tmp))
}
return io.Copy(w, fh)
}
1 change: 1 addition & 0 deletions gapis/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ func (r *traceHandler) Event(ctx context.Context, req service.TraceEvent) (*serv
}

if r.started {
// TODO: this is not a good way to signal that we are ready to capture.
if bytes == 0 {
status = service.TraceStatus_Initializing
} else {
Expand Down
2 changes: 2 additions & 0 deletions gapis/service/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ proto_library(
"//gapis/service/path:path_proto",
"//gapis/service/severity:severity_proto",
"//gapis/stringtable:stringtable_proto",
"@perfetto//:config_proto",
],
)

Expand All @@ -73,6 +74,7 @@ go_proto_library(
"//gapis/service/path:go_default_library",
"//gapis/service/severity:go_default_library",
"//gapis/stringtable:go_default_library",
"@perfetto//:go_default_library",
],
)

Expand Down
3 changes: 3 additions & 0 deletions gapis/service/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import "gapis/service/box/box.proto";
import "gapis/service/path/path.proto";
import "gapis/service/severity/severity.proto";
import "gapis/stringtable/stringtable.proto";
import "perfetto/config/perfetto_config.proto";

package service;
option java_package = "com.google.gapid.proto.service";
Expand Down Expand Up @@ -1163,6 +1164,8 @@ message TraceOptions {
string server_local_save_path = 21;
// Name of the pipe to connect/listen to.
string pipe_name = 22;
// The config to use if doing a Perfetto trace.
perfetto.protos.TraceConfig perfetto_config = 24;
}

enum TraceEvent {
Expand Down
1 change: 1 addition & 0 deletions gapis/trace/android/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ go_library(
"//gapidapk:go_default_library",
"//gapidapk/pkginfo:go_default_library",
"//gapii/client:go_default_library",
"//gapis/perfetto:go_default_library",
"//gapis/service:go_default_library",
"//gapis/trace/tracer:go_default_library",
],
Expand Down
14 changes: 11 additions & 3 deletions gapis/trace/android/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/google/gapid/gapidapk"
"github.com/google/gapid/gapidapk/pkginfo"
gapii "github.com/google/gapid/gapii/client"
"github.com/google/gapid/gapis/perfetto"
"github.com/google/gapid/gapis/service"
"github.com/google/gapid/gapis/trace/tracer"
)
Expand Down Expand Up @@ -474,10 +475,17 @@ func (t *androidTracer) SetupTrace(ctx context.Context, o *service.TraceOptions)
})
}

log.I(ctx, "Starting with options %+v", tracer.GapiiOptions(o))
process, gapiiCleanup, err := gapii.Start(ctx, pkg, a, tracer.GapiiOptions(o))
var process tracer.Process
if o.Type == service.TraceType_Perfetto {
process, err = perfetto.Start(ctx, t.b, a, o)
} else {
log.I(ctx, "Starting with options %+v", tracer.GapiiOptions(o))
var gapiiCleanup app.Cleanup
process, gapiiCleanup, err = gapii.Start(ctx, pkg, a, tracer.GapiiOptions(o))
cleanup = cleanup.Then(gapiiCleanup)
}
if err != nil {
return ret, cleanup.Invoke(ctx), err
}
return process, cleanup.Then(gapiiCleanup), nil
return process, cleanup, nil
}

0 comments on commit b3feb8b

Please sign in to comment.