blob: 0f7cd4fb2729e0ab953ea991e4defba9c6f15440 [file] [log] [blame]
// Copyright 2017 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/snapshot/Loader.h"
#include <errno.h>
#include "android/base/files/FileShareOpen.h"
#include "android/base/files/PathUtils.h"
#include "android/base/files/StdioStream.h"
#include "android/snapshot/TextureLoader.h"
#include "android/utils/path.h"
#include "android/utils/file_io.h"
using android::base::PathUtils;
using android::base::StdioStream;
using android::base::System;
namespace android {
namespace snapshot {
Loader::Loader(const Snapshot& snapshot, int error)
: mStatus(OperationStatus::Error), mSnapshot(snapshot) {
if (error) {
mSnapshot.saveFailure(errnoToFailure(error));
return;
}
if (!path_is_dir(base::c_str(mSnapshot.dataDir()))) {
return;
}
if (!mSnapshot.preload()) {
return;
}
mMemUsage = System::get()->getMemUsage();
mDiskKind = System::get()->pathDiskKind(mSnapshot.dataDir());
{
const auto ram = android::base::fsopen(
PathUtils::join(mSnapshot.dataDir(), kRamFileName).c_str(),
"rb", android::base::FileShare::Read);
if (!ram) {
mSnapshot.saveFailure(FailureReason::NoRamFile);
return;
}
// TODO: RamLoader constructor can just have the first argument if we
// can figure out a way to get it to compile for windows. It currently
// doesn't like {} being put as an argument for the ram block structure
// directly.
RamLoader::RamBlockStructure emptyRamBlockStructure = {};
mRamLoader.emplace(StdioStream(ram, StdioStream::kOwner),
RamLoader::Flags::OnDemandAllowed,
emptyRamBlockStructure);
}
{
const auto textures = android::base::fsopen(
PathUtils::join(mSnapshot.dataDir(), "textures.bin").c_str(),
"rb", android::base::FileShare::Read);
if (!textures) {
mSnapshot.saveFailure(FailureReason::NoTexturesFile);
mRamLoader.clear();
return;
}
mTextureLoader = std::make_shared<TextureLoader>(
StdioStream(textures, StdioStream::kOwner));
}
mStatus = OperationStatus::NotStarted;
}
void Loader::prepare() {
// TODO: run asynchronous index loading here.
}
void Loader::start() {
if (!mSnapshot.load()) {
mStatus = OperationStatus::Error;
}
}
void Loader::reportSuccessful() {
mSnapshot.incrementSuccessfulLoads();
}
void Loader::reportInvalid() {
mSnapshot.incrementInvalidLoads();
}
Loader::~Loader() {
// Wait for textureLoader to finish loading textures
if (mRamLoader && !mRamLoader->hasError()) {
mRamLoader->join();
}
if (mTextureLoader) {
mTextureLoader->join();
}
}
void Loader::interrupt() {
if (mRamLoader && !mRamLoader->hasError()) {
mRamLoader->interrupt();
}
if (mTextureLoader && !mTextureLoader->hasError()) {
mTextureLoader->interrupt();
}
}
ITextureLoaderPtr Loader::textureLoader() const {
return mTextureLoader;
}
void Loader::complete(bool succeeded) {
mStatus = OperationStatus::Error;
if (!succeeded) {
if (!mSnapshot.failureReason()) {
mSnapshot.saveFailure(FailureReason::EmulationEngineFailed);
}
return;
}
if (!mRamLoader || mRamLoader->hasError()) {
if (!mSnapshot.failureReason()) {
mSnapshot.saveFailure(FailureReason::RamFailed);
}
return;
}
if (!mTextureLoader || mTextureLoader->hasError()) {
if (!mSnapshot.failureReason()) {
mSnapshot.saveFailure(FailureReason::TexturesFailed);
}
return;
}
mStatus = OperationStatus::Ok;
}
// Don't do heavy operations like interrupting the loader
// here since this could be in a crash handler.
void Loader::onInvalidSnapshotLoad() {
mSnapshot.incrementInvalidLoads();
if (mSnapshot.shouldInvalidate()) {
mSnapshot.saveFailure(FailureReason::CorruptedData);
} else {
mSnapshot.saveFailure(FailureReason::InternalError);
}
}
void Loader::join() {
if (mRamLoader) {
mRamLoader->touchAllPages();
}
if (mTextureLoader) {
mTextureLoader->join();
}
}
void Loader::synchronize(bool isOnExit) {
if (mTextureLoader) {
mTextureLoader->join();
}
// Prepare ram loader for incremental save.
//
// It is important that the gap tracker of the ram loader is in a good
// state.
//
// a. If this is on exit, we can simply interrupt reading and use the ram
// loader's current gap tracker, if any, because a precondition of this
// function running is that we are saving the same snapshot as we have
// loaded.
//
// b. If this is not on exit, then we first need to make the loader finish
// whatever it was doing, because the emulator will need valid ram after
// the save. Then, we also need to invalidate the loader's gap tracker
// because it's unknown whether the particular version of this loader's
// gaps corresponds properly to the gaps in the |kRamFileName| file on disk
// (e.g., we might have saved more than once after a load).
if (mRamLoader && !mRamLoader->hasError()) {
if (isOnExit) {
mRamLoader->interrupt();
} else {
mRamLoader->join();
mRamLoader->invalidateGaps();
}
// If we transitioned from file backed to non-file-backed, we will
// need to rewrite the index and cannot use a previous index.
// Otherwise, there will be a lot of confusing cases to handle.
if (mRamLoader->didSwitchFileBacking()) {
mRamLoader.clear();
return;
}
if (!mRamLoader->hasGaps()) {
const auto ram = ::android_fopen(
PathUtils::join(mSnapshot.dataDir(), kRamFileName).c_str(), "rb");
if (!ram) return;
mRamLoader.emplace(
StdioStream(ram, StdioStream::kOwner),
RamLoader::Flags::LoadIndexOnly,
mRamLoader->getRamBlockStructure());
}
}
}
} // namespace snapshot
} // namespace android