Skip to content

Commit

Permalink
WIP replay from archive on Android
Browse files Browse the repository at this point in the history
  • Loading branch information
hevrard committed Mar 26, 2019
1 parent 06e6639 commit 7823bcc
Show file tree
Hide file tree
Showing 13 changed files with 741 additions and 82 deletions.
216 changes: 141 additions & 75 deletions cmd/gapir/cc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#if TARGET_OS == GAPID_OS_ANDROID
#include <sys/stat.h>
#include "android_native_app_glue.h"
#include "gapir/cc/android/asset_replay_service.h"
#include "gapir/cc/android/asset_resource_cache.h"
#elif TARGET_OS == GAPID_OS_LINUX || TARGET_OS == GAPID_OS_OSX
#include <dirent.h>
#include <ftw.h>
Expand All @@ -55,6 +57,13 @@ using namespace gapir;

namespace {

enum ReplayMode {
kUnknown = 0, // Can't determine replay type from arguments yet.
kConflict, // Impossible combination of command line arguments.
kReplayServer, // Run gapir as a server.
kReplayArchive, // Replay an exported archive.
};

std::vector<uint32_t> memorySizes {
// If we are on desktop, we can try more memory
#if TARGET_OS != GAPID_OS_ANDROID
Expand Down Expand Up @@ -264,12 +273,44 @@ std::unique_ptr<Server> Setup(const char* uri, const char* authToken,
});
}

static int replayArchive(core::CrashHandler* crashHandler,
std::unique_ptr<ResourceCache> resourceCache,
gapir::ReplayService* replayArchiveService) {
// The directory consists an archive(resources.{index,data}) and payload.bin.
MemoryManager memoryManager(memorySizes);
std::unique_ptr<ResourceLoader> resLoader =
CachedResourceLoader::create(resourceCache.get(), nullptr);

std::unique_ptr<Context> context = Context::create(
replayArchiveService, *crashHandler, resLoader.get(), &memoryManager);

if (context->initialize("payload")) {
GAPID_DEBUG("Replay context initialized successfully");
} else {
GAPID_ERROR("Replay context initialization failed");
return EXIT_FAILURE;
}

GAPID_INFO("Replay started");
bool ok = context->interpret();
replayArchiveService->sendReplayFinished();
if (!context->cleanup()) {
GAPID_ERROR("Replay cleanup failed");
return EXIT_FAILURE;
}
GAPID_INFO("Replay %s", ok ? "finished successfully" : "failed");

return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}

} // anonymous namespace

#if TARGET_OS == GAPID_OS_ANDROID

namespace {

const char* kReplayAssetToDetect = "replay_export/resources.index";

template <typename... Args>
jobject jni_call_o(JNIEnv* env, jobject obj, const char* name, const char* sig,
Args&&... args) {
Expand All @@ -287,6 +328,7 @@ int jni_call_i(JNIEnv* env, jobject obj, const char* name, const char* sig,
struct Options {
int idleTimeoutSec = 0;
std::string authToken = "";
ReplayMode mode = kUnknown;

static Options Parse(struct android_app* app) {
Options opts;
Expand All @@ -308,6 +350,20 @@ struct Options {
env->ReleaseStringUTFChars((jstring)token, tmp);
}

opts.mode = kReplayServer;

// Select replay archive mode if replay assets are detected
jobject j_asset_manager =
jni_call_o(env, app->activity->clazz, "getAssets",
"()Landroid/content/res/AssetManager;");
AAssetManager* asset_manager = AAssetManager_fromJava(env, j_asset_manager);
AAsset* asset = AAssetManager_open(asset_manager, kReplayAssetToDetect,
AASSET_MODE_UNKNOWN);
if (asset != nullptr) {
opts.mode = kReplayArchive;
AAsset_close(asset);
}

app->activity->vm->DetachCurrentThread();
return opts;
}
Expand Down Expand Up @@ -341,35 +397,76 @@ void android_process(struct android_app* app, int32_t cmd) {

// Main function for android
void android_main(struct android_app* app) {
MemoryManager memoryManager(memorySizes);
CrashHandler crashHandler;

// Attach main thread to the JVM, needed for sane JNI calls
// JNIEnv* env;
// app->activity->vm->AttachCurrentThread(&env, 0);

std::thread waiting_thread;
std::atomic<bool> thread_is_done(false);

// Get the path of the file system socket.
const char* pipe = pipeName();
std::string internal_data_path = std::string(app->activity->internalDataPath);
std::string socket_file_path = internal_data_path + "/" + std::string(pipe);
std::string uri = std::string("unix://") + socket_file_path;

GAPID_INFO(
"Started Graphics API Replay daemon.\n"
"Listening on unix socket '%s'\n"
"Supported ABIs: %s\n",
uri.c_str(), core::supportedABIs());

auto opts = Options::Parse(app);
std::unique_ptr<Server> server = nullptr;
MemoryManager memoryManager(memorySizes);
auto cache = InMemoryResourceCache::create(memoryManager.getTopAddress());
std::mutex lock;
PrewarmData data;
std::unique_ptr<Server> server =
Setup(uri.c_str(), opts.authToken.c_str(), cache.get(),
opts.idleTimeoutSec, &crashHandler, &memoryManager, &data, &lock);
std::atomic<bool> serverIsDone(false);
std::thread waiting_thread([&]() {
server.get()->wait();
serverIsDone = true;
});
if (chmod(socket_file_path.c_str(), S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH)) {
GAPID_ERROR("Chmod failed!");

auto opts = Options::Parse(app);

if (opts.mode == kReplayArchive) {
GAPID_INFO("Started Graphics API Replay from archive.");

waiting_thread = std::thread([&]() {
// It's important to use a different JNIEnv as it is a separate thread
JNIEnv* env;
app->activity->vm->AttachCurrentThread(&env, 0);

// Hugues: maybe we need to open assets in the main thread?
jobject j_asset_manager =
jni_call_o(env, app->activity->clazz, "getAssets",
"()Landroid/content/res/AssetManager;");
AAssetManager* asset_manager =
AAssetManager_fromJava(env, j_asset_manager);

std::unique_ptr<ResourceCache> assetResourceCache =
AssetResourceCache::create(asset_manager);
gapir::AssetReplayService assetReplayService(asset_manager);

const char* postbackDirectory = "";
replayArchive(&crashHandler, std::move(assetResourceCache),
&assetReplayService);

app->activity->vm->DetachCurrentThread();

thread_is_done = true;
});

} else if (opts.mode == kReplayServer) {
GAPID_INFO(
"Started Graphics API Replay daemon.\n"
"Listening on unix socket '%s'\n"
"Supported ABIs: %s\n",
uri.c_str(), core::supportedABIs());

server =
Setup(uri.c_str(), opts.authToken.c_str(), cache.get(),
opts.idleTimeoutSec, &crashHandler, &memoryManager, &data, &lock);
waiting_thread = std::thread([&]() {
server.get()->wait();
thread_is_done = true;
});
if (chmod(socket_file_path.c_str(),
S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH)) {
GAPID_ERROR("Chmod failed!");
}
} else {
GAPID_ERROR("Invalid replay mode");
}

app->onAppCmd = android_process;
Expand All @@ -389,13 +486,15 @@ void android_main(struct android_app* app) {
}
if (app->destroyRequested) {
// Clean up and exit the main loop
server->shutdown();
if (opts.mode == kReplayServer) {
server->shutdown();
}
alive = false;
break;
}
}

if (serverIsDone && !finishing) {
if (thread_is_done && !finishing) {
// Start termination of the app
ANativeActivity_finish(app->activity);

Expand All @@ -411,7 +510,9 @@ void android_main(struct android_app* app) {

// Final clean up
waiting_thread.join();
unlink(socket_file_path.c_str());
if (opts.mode == kReplayServer) {
unlink(socket_file_path.c_str());
}
GAPID_INFO("End of Graphics API Replay");
return;
}
Expand All @@ -429,13 +530,6 @@ struct Options {

int logLevel = LOG_LEVEL;
const char* logPath = "logs/gapir.log";

enum ReplayMode {
kUnknown = 0, // Can't determine replay type from arguments yet.
kConflict, // Impossible combination of command line arguments.
kReplayServer, // Run gapir as a server.
kReplayArchive, // Replay an exported archive.
};
ReplayMode mode = kUnknown;
bool waitForDebugger = false;
const char* cachePath = nullptr;
Expand Down Expand Up @@ -669,47 +763,7 @@ std::unique_ptr<ResourceCache> createCache(
}
} // namespace

static int replayArchive(Options opts) {
// The directory consists an archive(resources.{index,data}) and payload.bin.
core::CrashHandler crashHandler;
GAPID_LOGGER_INIT(opts.logLevel, "gapir", opts.logPath);
MemoryManager memoryManager(memorySizes);
std::string payloadPath = std::string(opts.replayArchive) + "/payload.bin";
gapir::ArchiveReplayService replayArchive(payloadPath,
opts.postbackDirectory);
// All the resource data must be in the archive file, no fallback resource
// loader to fetch uncached resources data.
auto onDiskCache = OnDiskResourceCache::create(opts.replayArchive, false);
std::unique_ptr<ResourceLoader> resLoader =
CachedResourceLoader::create(onDiskCache.get(), nullptr);

std::unique_ptr<Context> context = Context::create(
&replayArchive, crashHandler, resLoader.get(), &memoryManager);

if (context->initialize("payload")) {
GAPID_DEBUG("Replay context initialized successfully");
} else {
GAPID_ERROR("Replay context initialization failed");
return EXIT_FAILURE;
}

GAPID_INFO("Replay started");
bool ok = context->interpret();
replayArchive.sendReplayFinished();
if (!context->cleanup()) {
GAPID_ERROR("Replay cleanup failed");
return EXIT_FAILURE;
}
GAPID_INFO("Replay %s", ok ? "finished successfully" : "failed");

return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}

static int startServer(Options opts) {
core::CrashHandler crashHandler;

GAPID_LOGGER_INIT(opts.logLevel, "gapir", opts.logPath);

static int startServer(core::CrashHandler* crashHandler, Options opts) {
// Read the auth-token.
// Note: This must come before the socket is created as the auth token
// file is deleted by GAPIS as soon as the port is written to stdout.
Expand Down Expand Up @@ -754,7 +808,7 @@ static int startServer(Options opts) {
PrewarmData data;
std::unique_ptr<Server> server =
Setup(uri.c_str(), (authToken.size() > 0) ? authToken.data() : nullptr,
cache.get(), opts.idleTimeoutSec, &crashHandler, &memoryManager,
cache.get(), opts.idleTimeoutSec, crashHandler, &memoryManager,
&data, &lock);
// The following message is parsed by launchers to detect the selected port.
// DO NOT CHANGE!
Expand Down Expand Up @@ -786,13 +840,25 @@ int main(int argc, const char* argv[]) {
} else if (opts.version) {
printf("GAPIR version " GAPID_VERSION_AND_BUILD "\n");
return EXIT_SUCCESS;
} else if (opts.mode == Options::kConflict) {
} else if (opts.mode == kConflict) {
GAPID_ERROR("Argument conflicts.");
return EXIT_FAILURE;
} else if (opts.mode == Options::kReplayArchive) {
return replayArchive(opts);
}

core::CrashHandler crashHandler;
GAPID_LOGGER_INIT(opts.logLevel, "gapir", opts.logPath);

if (opts.mode == kReplayArchive) {
std::string payloadPath = std::string(opts.replayArchive) + "/payload.bin";
gapir::ArchiveReplayService replayArchiveService(payloadPath,
opts.postbackDirectory);
// All the resource data must be in the archive file, no fallback resource
// loader to fetch uncached resources data.
auto onDiskCache = OnDiskResourceCache::create(opts.replayArchive, false);
return replayArchive(&crashHandler, std::move(onDiskCache),
&replayArchiveService);
} else {
return startServer(opts);
return startServer(&crashHandler, opts);
}
}

Expand Down
2 changes: 2 additions & 0 deletions cmd/gapit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ go_library(
"stresstest.go",
"sxs_video.go",
"trace.go",
"trace2apk.go",
"trim.go",
"unpack.go",
"video.go",
Expand All @@ -55,6 +56,7 @@ go_library(
"//core/app/auth:go_default_library",
"//core/app/crash:go_default_library",
"//core/app/flags:go_default_library",
"//core/app/layout:go_default_library",
"//core/app/status:go_default_library",
"//core/data/endian:go_default_library",
"//core/data/id:go_default_library",
Expand Down
7 changes: 7 additions & 0 deletions cmd/gapit/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,11 @@ type (

SmokeTestsFlags struct {
}

Trace2apkFlags struct {
Gapis GapisFlags
Gapir GapirFlags
CommandFilterFlags
CaptureFileFlags
}
)
Loading

0 comments on commit 7823bcc

Please sign in to comment.