Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add export_replay for standalone replay with gapir. #2159

Merged
merged 2 commits into from
Aug 31, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
24 changes: 23 additions & 1 deletion cmd/gapir/cc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "gapir/cc/context.h"
#include "gapir/cc/crash_uploader.h"
#include "gapir/cc/memory_manager.h"
#include "gapir/cc/replay_archive.h"
#include "gapir/cc/replay_connection.h"
#include "gapir/cc/resource_disk_cache.h"
#include "gapir/cc/resource_in_memory_cache.h"
Expand Down Expand Up @@ -210,9 +211,12 @@ int main(int argc, const char* argv[]) {
const char* portArgStr = "0";
const char* authTokenFile = nullptr;
int idleTimeoutSec = 0;
const char* replayArchive = nullptr;

for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--auth-token-file") == 0) {
if (strcmp(argv[i], "--replay-archive") == 0) {
replayArchive = argv[++i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (i + 1 >= argc) {
GAPID_FATAL(....
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I think 'using archive' and 'connecting to gapis' are mutual exclusive, using both at the same time might be invalid (at least for this CL, and same to options like: port, idle-timeout-sec), so probably we should document this somewhere.

} else if (strcmp(argv[i], "--auth-token-file") == 0) {
if (i + 1 >= argc) {
GAPID_FATAL("Usage: --auth-token-file <token-string>");
}
Expand Down Expand Up @@ -283,6 +287,24 @@ int main(int argc, const char* argv[]) {
core::Debugger::waitForAttach();
}

if (replayArchive) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have a big mode-switch at the start of the replay, I would split this out into 2 methods. One that starts an "archived replay" and one that opens a connection.

It is a bit weird to have a "start the program in an entirely different way" early-out in the middle of main().

core::CrashHandler crashHandler;
GAPID_LOGGER_INIT(logLevel, "gapir", logPath);
MemoryManager memoryManager(memorySizes);
std::string payloadPath = std::string(replayArchive) + "/payload.bin";
gapir::ReplayArchive conn(payloadPath);
std::unique_ptr<ResourceProvider> resourceProvider =
ResourceDiskCache::create(nullptr, replayArchive);
std::unique_ptr<Context> context = Context::create(
&conn, crashHandler, resourceProvider.get(), &memoryManager);

GAPID_INFO("Replay started");
bool ok = context->interpret();
GAPID_INFO("Replay %s", ok ? "finished successfully" : "failed");

return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}

core::CrashHandler crashHandler;

GAPID_LOGGER_INIT(logLevel, "gapir", logPath);
Expand Down
1 change: 1 addition & 0 deletions cmd/gapit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ go_library(
"dump.go",
"dump_pipeline.go",
"dump_shaders.go",
"export_replay.go",
"flags.go",
"inputs.go",
"main.go",
Expand Down
68 changes: 68 additions & 0 deletions cmd/gapit/export_replay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (C) 2017 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 main

import (
"context"
"flag"
"path/filepath"

"github.com/google/gapid/core/app"
"github.com/google/gapid/core/log"
)

type exportReplayVerb struct{ ExportReplayFlags }

func init() {
verb := &exportReplayVerb{}
app.AddVerb(&app.Verb{
Name: "export_replay",
ShortHelp: "Export replay vm instruction and assets.",
Action: verb,
})
}

func (verb *exportReplayVerb) Run(ctx context.Context, flags flag.FlagSet) error {
if flags.NArg() != 1 {
app.Usage(ctx, "Exactly one gfx trace file expected, got %d", flags.NArg())
return nil
}

capture, err := filepath.Abs(flags.Arg(0))
if err != nil {
log.Errf(ctx, err, "Could not find capture file: %v", flags.Arg(0))
}

client, err := getGapis(ctx, verb.Gapis, verb.Gapir)
if err != nil {
return log.Err(ctx, err, "Failed to connect to the GAPIS server")
}
defer client.Close()

capturePath, err := client.LoadCapture(ctx, capture)
if err != nil {
return log.Err(ctx, err, "Failed to load the capture file")
}

device, err := getDevice(ctx, client, capturePath, verb.Gapir)
if err != nil {
return err
}

if err := client.ExportReplay(ctx, capturePath, device, verb.Out); err != nil {
return log.Err(ctx, err, "Failed to export replay")
}
return nil
}
5 changes: 5 additions & 0 deletions cmd/gapit/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ type (
DisplayToSurface bool `help:"display the frames rendered in the replay back to the surface"`
CommandFilterFlags
}
ExportReplayFlags struct {
Gapis GapisFlags
Gapir GapirFlags
Out string `help:"output directory for commands and assets"`
}
VideoFlags struct {
Gapis GapisFlags
Gapir GapirFlags
Expand Down
25 changes: 25 additions & 0 deletions core/archive/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (C) 2018 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.

load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["archive.go"],
cdeps = ["//core/cc"],
cgo = True,
clinkopts = [], # keep
importpath = "github.com/google/gapid/core/archive",
visibility = ["//visibility:public"],
)
45 changes: 45 additions & 0 deletions core/archive/archive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2018 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 archive

/*
#include <stdlib.h>
#include "core/cc/archive.h"
*/
import "C"
import "unsafe"

// Archive contains assets.
type Archive = *C.archive

// New creates an archive.
func New(name string) Archive {
cstr := C.CString(name)
defer C.free(unsafe.Pointer(cstr))
return C.archive_create(cstr)
}

// Dispose flush and close the underlying archive.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flushes and closes

func (a Archive) Dispose() {
C.archive_destroy(a)
}

// Write writes key-value pair to the archive.
func (a Archive) Write(key string, value []byte) bool {
cstr := C.CString(key)
defer C.free(unsafe.Pointer(cstr))
csize := C.size_t(len(value))
return C.archive_write(a, cstr, unsafe.Pointer(&value[0]), csize) != 0
}
14 changes: 14 additions & 0 deletions core/cc/archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,17 @@ bool Archive::write(const std::string& id, const void* buffer, uint32_t size) {
}

} // namespace core

extern "C" {

archive* archive_create(const char* archiveName) {
return reinterpret_cast<archive*>(new core::Archive(archiveName));
}

void archive_destroy(archive* a) { delete reinterpret_cast<core::Archive*>(a); }

int archive_write(archive* a, const char* id, const void* buffer, size_t size) {
return reinterpret_cast<core::Archive*>(a)->write(id, buffer, size) ? 1 : 0;
}

} // extern "C"
12 changes: 12 additions & 0 deletions core/cc/archive.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef CORE_ARCHIVE_H
#define CORE_ARCHIVE_H

#ifdef __cplusplus

#include "id.h"

#include <string>
Expand Down Expand Up @@ -57,4 +59,14 @@ class Archive {

} // namespace core

extern "C" {
#endif
typedef struct archive_t archive;
archive* archive_create(const char* archiveName);
void archive_destroy(archive* a);
int archive_write(archive* a, const char* id, const void* buffer, size_t size);
#ifdef __cplusplus
} // extern "C"
#endif

#endif // CORE_ARCHIVE_H
50 changes: 50 additions & 0 deletions gapir/cc/replay_archive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018 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.
*/

#include "replay_archive.h"

#include <grpc++/grpc++.h>
#include <fstream>
#include <memory>

#include "gapir/replay_service/service.grpc.pb.h"

namespace gapir {

std::unique_ptr<ReplayArchive::Payload> ReplayArchive::getPayload() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReplayConnection::Payload? Not sure which one is better.

std::fstream input(mFileprefix, std::ios::in | std::ios::binary);
std::unique_ptr<replay_service::Payload> payload(new replay_service::Payload);
payload->ParseFromIstream(&input);
return std::unique_ptr<Payload>(new Payload(std::move(payload)));
}

std::unique_ptr<ReplayArchive::Resources> ReplayArchive::getResources(
std::unique_ptr<ResourceRequest> req) {
return nullptr;
}
bool ReplayArchive::sendReplayFinished() { return true; }
bool ReplayArchive::sendCrashDump(const std::string& filepath,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be writing out the crash dump here. (And logging it's location).

const void* crash_data, uint32_t crash_size) {
return true;
}
bool ReplayArchive::sendPostData(std::unique_ptr<Posts> posts) { return true; }
bool ReplayArchive::sendNotification(uint64_t id, uint32_t severity,
uint32_t api_index, uint64_t label,
const std::string& msg, const void* data,
uint32_t data_size) {
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should log any notifications that we got here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GAPIR already write those to log.

}
} // namespace gapir
51 changes: 51 additions & 0 deletions gapir/cc/replay_archive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2018 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.
*/

#ifndef GAPIR_REPLAY_ARCHIVE_H
#define GAPIR_REPLAY_ARCHIVE_H

#include "core/cc/archive.h"
#include "replay_connection.h"
namespace gapir {

// ReplayArchive implements ReplayConnection for exported replays.
class ReplayArchive : public ReplayConnection {
public:
ReplayArchive(const std::string& fileprefix)
: ReplayConnection(nullptr), mFileprefix(fileprefix) {}
// Read payload from disk.
virtual std::unique_ptr<Payload> getPayload() override;

// We are reading from disk, so the following methods are not implemented.
virtual std::unique_ptr<Resources> getResources(
std::unique_ptr<ResourceRequest> req) override;
virtual bool sendReplayFinished() override;
virtual bool sendCrashDump(const std::string& filepath,
const void* crash_data,
uint32_t crash_size) override;
virtual bool sendPostData(std::unique_ptr<Posts> posts) override;
virtual bool sendNotification(uint64_t id, uint32_t severity,
uint32_t api_index, uint64_t label,
const std::string& msg, const void* data,
uint32_t data_size) override;

private:
std::string mFileprefix;
};

} // namespace gapir

#endif // GAPIR_REPLAY_ARCHIVE_H
3 changes: 3 additions & 0 deletions gapir/cc/resource_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ bool ResourceCache::Batch::flush(ResourceCache& cache, ReplayConnection* conn) {
if (count == 0) {
return true;
}
if (!cache.mFallbackProvider) {
return false;
}
if (!cache.mFallbackProvider->get(mResources.data(), count, conn, ptr,
mSize)) {
return false;
Expand Down
5 changes: 5 additions & 0 deletions gapis/api/vulkan/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,3 +781,8 @@ func (a API) QueryIssues(
}
return res.([]replay.Issue), nil
}

// ExportReplayRequest returns request type for standalone replay.
func (a API) ExportReplayRequest() replay.Request {
return issuesRequest{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really should have a new type of request, instead of using issuesRequest, eventually. And using issuesRequest won't have any framebuffer data postback, so can be hard to tell if the replay is correct or not.

}
15 changes: 15 additions & 0 deletions gapis/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,21 @@ func (c *client) SaveCapture(ctx context.Context, capture *path.Capture, path st
return nil
}

func (c *client) ExportReplay(ctx context.Context, capture *path.Capture, device *path.Device, path string) error {
res, err := c.client.ExportReplay(ctx, &service.ExportReplayRequest{
Capture: capture,
Path: path,
Device: device,
})
if err != nil {
return err
}
if err := res.GetError(); err != nil {
return err.Get()
}
return nil
}

func (c *client) GetDevices(ctx context.Context) ([]*path.Device, error) {
res, err := c.client.GetDevices(ctx, &service.GetDevicesRequest{})
if err != nil {
Expand Down
Loading