uint32 FBuildPatchInstaller::Run() { // Make sure this function can never be parallelized static FCriticalSection SingletonFunctionLockCS; FScopeLock SingletonFunctionLock(&SingletonFunctionLockCS); FBuildPatchInstallError::Reset(); SetRunning(true); SetInited(true); SetDownloadSpeed(-1); UpdateDownloadProgressInfo(true); // Register the current manifest with the installation info, to make sure we pull from it if (CurrentBuildManifest.IsValid()) { InstallationInfo.RegisterAppInstallation(CurrentBuildManifest.ToSharedRef(), InstallDirectory); } // Keep track of files that failed verify TArray<FString> CorruptFiles; // Init prereqs progress value const bool bInstallPrereqs = !CurrentBuildManifest.IsValid() && !NewBuildManifest->GetPrereqPath().IsEmpty(); // Get the start time double StartTime = FPlatformTime::Seconds(); double CleanUpTime = 0; // Keep retrying the install while it is not canceled, or caused by download error bool bProcessSuccess = false; bool bCanRetry = true; int32 InstallRetries = 5; while (!bProcessSuccess && bCanRetry) { // Run the install bool bInstallSuccess = RunInstallation(CorruptFiles); BuildProgress.SetStateProgress(EBuildPatchProgress::PrerequisitesInstall, bInstallPrereqs ? 0.0f : 1.0f); if (bInstallSuccess) { BuildProgress.SetStateProgress(EBuildPatchProgress::Downloading, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::Installing, 1.0f); } // Backup local changes then move generated files bInstallSuccess = bInstallSuccess && RunBackupAndMove(); // Run Verification CorruptFiles.Empty(); BuildProgress.SetStateProgress(EBuildPatchProgress::Initializing, 1.0f); bProcessSuccess = bInstallSuccess && RunVerification(CorruptFiles); // Clean staging if INSTALL success if (bInstallSuccess) { GLog->Logf(TEXT("BuildPatchServices: Deleting staging area")); CleanUpTime = FPlatformTime::Seconds(); IFileManager::Get().DeleteDirectory(*StagingDirectory, false, true); CleanUpTime = FPlatformTime::Seconds() - CleanUpTime; } BuildProgress.SetStateProgress(EBuildPatchProgress::CleanUp, 1.0f); // Set if we can retry --InstallRetries; bCanRetry = InstallRetries > 0 && !FBuildPatchInstallError::IsInstallationCancelled() && !FBuildPatchInstallError::IsNoRetryError(); // If successful or we will retry, remove the moved files marker if (bProcessSuccess || bCanRetry) { GLog->Logf(TEXT("BuildPatchServices: Reset MM")); IFileManager::Get().Delete(*PreviousMoveMarker, false, true); } } if (bProcessSuccess) { // Run the prerequisites installer if this is our first install and the manifest has prerequisites info if (bInstallPrereqs) { // @TODO: We also want to trigger prereq install if this is an update and the prereq installer differs in the update bProcessSuccess &= RunPrereqInstaller(); } } // Set final stat values and log out results { FScopeLock Lock(&ThreadLock); bSuccess = bProcessSuccess; BuildStats.ProcessSuccess = bProcessSuccess; BuildStats.ProcessExecuteTime = (FPlatformTime::Seconds() - StartTime) - BuildStats.ProcessPausedTime; BuildStats.FailureReason = FBuildPatchInstallError::GetErrorString(); BuildStats.FailureReasonText = FBuildPatchInstallError::GetErrorText(); BuildStats.CleanUpTime = CleanUpTime; // Log stats GLog->Logf(TEXT("BuildPatchServices: Build Stat: AppName: %s"), *BuildStats.AppName); GLog->Logf(TEXT("BuildPatchServices: Build Stat: AppInstalledVersion: %s"), *BuildStats.AppInstalledVersion); GLog->Logf(TEXT("BuildPatchServices: Build Stat: AppPatchVersion: %s"), *BuildStats.AppPatchVersion); GLog->Logf(TEXT("BuildPatchServices: Build Stat: CloudDirectory: %s"), *BuildStats.CloudDirectory); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumFilesInBuild: %u"), BuildStats.NumFilesInBuild); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumFilesOutdated: %u"), BuildStats.NumFilesOutdated); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumFilesToRemove: %u"), BuildStats.NumFilesToRemove); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumChunksRequired: %u"), BuildStats.NumChunksRequired); GLog->Logf(TEXT("BuildPatchServices: Build Stat: ChunksQueuedForDownload: %u"), BuildStats.ChunksQueuedForDownload); GLog->Logf(TEXT("BuildPatchServices: Build Stat: ChunksLocallyAvailable: %u"), BuildStats.ChunksLocallyAvailable); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumChunksDownloaded: %u"), BuildStats.NumChunksDownloaded); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumChunksRecycled: %u"), BuildStats.NumChunksRecycled); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumChunksCacheBooted: %u"), BuildStats.NumChunksCacheBooted); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumDriveCacheChunkLoads: %u"), BuildStats.NumDriveCacheChunkLoads); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumRecycleFailures: %u"), BuildStats.NumRecycleFailures); GLog->Logf(TEXT("BuildPatchServices: Build Stat: NumDriveCacheLoadFailures: %u"), BuildStats.NumDriveCacheLoadFailures); GLog->Logf(TEXT("BuildPatchServices: Build Stat: TotalDownloadedData: %lld"), BuildStats.TotalDownloadedData); GLog->Logf(TEXT("BuildPatchServices: Build Stat: AverageDownloadSpeed: %.3f MB/sec"), BuildStats.AverageDownloadSpeed / 1024.0 / 1024.0); GLog->Logf(TEXT("BuildPatchServices: Build Stat: TheoreticalDownloadTime: %s"), *FPlatformTime::PrettyTime(BuildStats.TheoreticalDownloadTime)); GLog->Logf(TEXT("BuildPatchServices: Build Stat: VerifyTime: %s"), *FPlatformTime::PrettyTime(BuildStats.VerifyTime)); GLog->Logf(TEXT("BuildPatchServices: Build Stat: CleanUpTime: %s"), *FPlatformTime::PrettyTime(BuildStats.CleanUpTime)); GLog->Logf(TEXT("BuildPatchServices: Build Stat: ProcessExecuteTime: %s"), *FPlatformTime::PrettyTime(BuildStats.ProcessExecuteTime)); GLog->Logf(TEXT("BuildPatchServices: Build Stat: ProcessPausedTime: %.1f sec"), BuildStats.ProcessPausedTime); GLog->Logf(TEXT("BuildPatchServices: Build Stat: ProcessSuccess: %s"), BuildStats.ProcessSuccess ? TEXT("TRUE") : TEXT("FALSE")); GLog->Logf(TEXT("BuildPatchServices: Build Stat: FailureReason: %s"), *BuildStats.FailureReason); GLog->Logf(TEXT("BuildPatchServices: Build Stat: FailureReasonText: %s"), *BuildStats.FailureReasonText.BuildSourceString()); } // Mark that we are done SetRunning(false); return bSuccess ? 0 : 1; }
bool FBuildPatchInstaller::RunInstallation(TArray<FString>& CorruptFiles) { GLog->Logf(TEXT("BuildPatchServices: Starting Installation")); // Save the staging directories FPaths::NormalizeDirectoryName(DataStagingDir); FPaths::NormalizeDirectoryName(InstallStagingDir); // Make sure staging directories exist IFileManager::Get().MakeDirectory(*DataStagingDir, true); IFileManager::Get().MakeDirectory(*InstallStagingDir, true); // Reset any error from a previous install FBuildPatchInstallError::Reset(); FBuildPatchAnalytics::ResetCounters(); BuildProgress.Reset(); BuildProgress.SetStateProgress(EBuildPatchProgress::Initializing, 0.01f); BuildProgress.SetStateProgress(EBuildPatchProgress::CleanUp, 0.0f); // Remove any inventory FBuildPatchFileConstructor::PurgeFileDataInventory(); // Check if we should skip out of this process bool bPreviousStagingCompleted = FPaths::FileExists(PreviousMoveMarker); if (bPreviousStagingCompleted) { GLog->Logf(TEXT("BuildPatchServices: Detected previous staging completed")); // Set weights for verify only BuildProgress.SetStateWeight(EBuildPatchProgress::Downloading, 0.0f); BuildProgress.SetStateWeight(EBuildPatchProgress::Installing, 0.0f); BuildProgress.SetStateWeight(EBuildPatchProgress::MovingToInstall, 0.0f); BuildProgress.SetStateWeight(EBuildPatchProgress::BuildVerification, 1.0f); // Mark all installation steps complete BuildProgress.SetStateProgress(EBuildPatchProgress::Initializing, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::Resuming, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::Downloading, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::Installing, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::MovingToInstall, 1.0f); return true; } // Get the list of files needing construction TArray< FString > FilesToConstruct; if (CorruptFiles.Num() > 0) { FilesToConstruct.Append(CorruptFiles); } else { FBuildPatchAppManifest::GetOutdatedFiles(CurrentBuildManifest, NewBuildManifest, InstallDirectory, FilesToConstruct); } GLog->Logf(TEXT("BuildPatchServices: Requiring %d files"), FilesToConstruct.Num()); // Create the downloader FBuildPatchDownloader::Create(DataStagingDir, NewBuildManifest, &BuildProgress); // Create chunk cache if (bIsChunkData) { FBuildPatchChunkCache::Init(NewBuildManifest, CurrentBuildManifest, DataStagingDir, InstallDirectory, &BuildProgress, FilesToConstruct, InstallationInfo); } // Hold the file constructor thread FBuildPatchFileConstructor* FileConstructor = NULL; // Store some totals const uint32 NumFilesInBuild = NewBuildManifest->GetNumFiles(); // Stats for build const uint32 NumFilesToConstruct = bIsFileData ? NumFilesInBuild : FBuildPatchChunkCache::Get().GetStatNumFilesToConstruct(); const uint32 NumRequiredChunks = bIsFileData ? NumFilesInBuild : FBuildPatchChunkCache::Get().GetStatNumRequiredChunks(); const uint32 NumChunksToDownload = bIsFileData ? NumFilesInBuild : FBuildPatchChunkCache::Get().GetStatNumChunksToDownload(); const uint32 NumChunksToConstruct = bIsFileData ? 0 : FBuildPatchChunkCache::Get().GetStatNumChunksToRecycle(); TotalInitialDownloadSize = bIsFileData ? NewBuildManifest->GetFileSize(FilesToConstruct) : FBuildPatchChunkCache::Get().GetStatTotalChunkDownloadSize(); // Save stats { FScopeLock Lock(&ThreadLock); BuildStats.AppName = NewBuildManifest->GetAppName(); BuildStats.AppPatchVersion = NewBuildManifest->GetVersionString(); BuildStats.AppInstalledVersion = CurrentBuildManifest.IsValid() ? CurrentBuildManifest->GetVersionString() : TEXT("NONE"); BuildStats.CloudDirectory = FBuildPatchServicesModule::GetCloudDirectory(); BuildStats.NumFilesInBuild = NumFilesInBuild; BuildStats.NumFilesOutdated = NumFilesToConstruct; BuildStats.NumChunksRequired = NumRequiredChunks; BuildStats.ChunksQueuedForDownload = NumChunksToDownload; BuildStats.ChunksLocallyAvailable = NumChunksToConstruct; } // Save initial counts as float for use with progress updates InitialNumChunkDownloads = NumChunksToDownload; InitialNumChunkConstructions = NumChunksToConstruct; // Setup some weightings for the progress tracking const float NumRequiredChunksFloat = NumRequiredChunks; BuildProgress.SetStateWeight(EBuildPatchProgress::Downloading, NumRequiredChunksFloat > 0.0f ? InitialNumChunkDownloads / NumRequiredChunksFloat : 0.0f); BuildProgress.SetStateWeight(EBuildPatchProgress::Installing, NumRequiredChunksFloat > 0.0f ? 0.1f + (InitialNumChunkConstructions / NumRequiredChunksFloat) : 0.0f); BuildProgress.SetStateWeight(EBuildPatchProgress::MovingToInstall, NumFilesToConstruct > 0 ? 0.05f : 0.0f); // A verify weight of 1 / 9 will make it 10% of the total progress BuildProgress.SetStateWeight(EBuildPatchProgress::BuildVerification, 1.1f / 9.0f); // If this is a repair operation, start off with install and download complete if (bIsRepairing) { GLog->Logf(TEXT("BuildPatchServices: Performing a repair operation")); BuildProgress.SetStateProgress(EBuildPatchProgress::Downloading, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::Installing, 1.0f); BuildProgress.SetStateProgress(EBuildPatchProgress::MovingToInstall, 1.0f); } // Start the file constructor GLog->Logf(TEXT("BuildPatchServices: Starting file contruction worker")); FileConstructor = new FBuildPatchFileConstructor(CurrentBuildManifest, NewBuildManifest, InstallDirectory, InstallStagingDir, FilesToConstruct, &BuildProgress); // Initializing is now complete if we are constructing files BuildProgress.SetStateProgress(EBuildPatchProgress::Initializing, NumFilesToConstruct > 0 ? 1.0f : 0.0f); // If this is file data, queue the download list if (bIsFileData) { TArray< FGuid > RequiredFileData; NewBuildManifest->GetChunksRequiredForFiles(FilesToConstruct, RequiredFileData); FBuildPatchDownloader::Get().AddChunksToDownload(RequiredFileData); } // Wait for the file constructor to complete while (FileConstructor->IsComplete() == false) { UpdateDownloadProgressInfo(); FPlatformProcess::Sleep(0.1f); } FileConstructor->Wait(); delete FileConstructor; FileConstructor = NULL; GLog->Logf(TEXT("BuildPatchServices: File construction complete")); // Wait for downloader to complete FBuildPatchDownloader::Get().NotifyNoMoreChunksToAdd(); while (FBuildPatchDownloader::Get().IsComplete() == false) { UpdateDownloadProgressInfo(); FPlatformProcess::Sleep(0.0f); } TArray< FBuildPatchDownloadRecord > AllChunkDownloads = FBuildPatchDownloader::Get().GetDownloadRecordings(); SetDownloadSpeed(-1); // Calculate the average download speed from the recordings // NB: Because we are threading several downloads at once this is not simply averaging every download. We have to know about // how much data is being received simultaneously too. We also need to ignore download pauses. int64 TotalDownloadedBytes = 0; double TotalTimeDownloading = 0; double RecoredEndTime = 0; if (AllChunkDownloads.Num() > 0) { // Sort by start time AllChunkDownloads.Sort(); // Start with first record TotalTimeDownloading = AllChunkDownloads[0].EndTime - AllChunkDownloads[0].StartTime; TotalDownloadedBytes = AllChunkDownloads[0].DownloadSize; RecoredEndTime = AllChunkDownloads[0].EndTime; // For every other record.. for (int32 RecordIdx = 1; RecordIdx < AllChunkDownloads.Num(); ++RecordIdx) { // Do we have some time to count if (RecoredEndTime < AllChunkDownloads[RecordIdx].EndTime) { // Was there a break in downloading if (AllChunkDownloads[RecordIdx].StartTime > RecoredEndTime) { TotalTimeDownloading += AllChunkDownloads[RecordIdx].EndTime - AllChunkDownloads[RecordIdx].StartTime; } // Otherwise don't count time overlap else { TotalTimeDownloading += AllChunkDownloads[RecordIdx].EndTime - RecoredEndTime; } RecoredEndTime = AllChunkDownloads[RecordIdx].EndTime; } // Count all bytes TotalDownloadedBytes += AllChunkDownloads[RecordIdx].DownloadSize; } } // Set final stats { FScopeLock Lock(&ThreadLock); BuildStats.TotalDownloadedData = TotalDownloadedBytes; BuildStats.NumChunksDownloaded = AllChunkDownloads.Num(); const double TotalDownloadedBytesDouble = TotalDownloadedBytes; BuildStats.AverageDownloadSpeed = TotalTimeDownloading > 0 ? TotalDownloadedBytesDouble / TotalTimeDownloading : 0; BuildStats.TheoreticalDownloadTime = TotalTimeDownloading; BuildStats.NumChunksRecycled = bIsFileData ? 0 : FBuildPatchChunkCache::Get().GetCounterChunksRecycled(); BuildStats.NumChunksCacheBooted = bIsFileData ? 0 : FBuildPatchChunkCache::Get().GetCounterChunksCacheBooted(); BuildStats.NumDriveCacheChunkLoads = bIsFileData ? 0 : FBuildPatchChunkCache::Get().GetCounterDriveCacheChunkLoads(); BuildStats.NumRecycleFailures = bIsFileData ? 0 : FBuildPatchChunkCache::Get().GetCounterRecycleFailures(); BuildStats.NumDriveCacheLoadFailures = bIsFileData ? 0 : FBuildPatchChunkCache::Get().GetCounterDriveCacheLoadFailures(); } // Perform static cleanup if (bIsChunkData) { FBuildPatchChunkCache::Shutdown(); } FBuildPatchDownloader::Shutdown(); FBuildPatchFileConstructor::PurgeFileDataInventory(); GLog->Logf(TEXT("BuildPatchServices: Staged install complete")); return !FBuildPatchInstallError::HasFatalError(); }
void FBuildPatchInstaller::UpdateDownloadProgressInfo( bool bReset ) { // Static variables for persistent values static double LastTime = FPlatformTime::Seconds(); static double NowTime = 0; static double DeltaTime = 0; static double LastReadingTime = 0; static double LastDataReadings[NUM_DOWNLOAD_READINGS] = { 0 }; static double LastTimeReadings[NUM_DOWNLOAD_READINGS] = { 0 }; static uint32 ReadingIdx = 0; static bool bProgressIsDownload = true; static double AverageDownloadSpeed = 0; // Reset internals? if( bReset ) { LastTime = FPlatformTime::Seconds(); LastReadingTime = LastTime; NowTime = 0; DeltaTime = 0; for (int32 i = 0; i < NUM_DOWNLOAD_READINGS; ++i) { LastDataReadings[i] = 0; LastTimeReadings[i] = 0; } ReadingIdx = 0; bProgressIsDownload = true; AverageDownloadSpeed = 0; return; } // Return if not downloading yet if( !NewBuildManifest->IsFileDataManifest() && !FBuildPatchChunkCache::Get().HaveDownloadsStarted() ) { return; } // Calculate percentage complete based on number of chunks const int64 DownloadNumBytesLeft = FBuildPatchDownloader::Get().GetNumBytesLeft(); const float DownloadSizeFloat = TotalInitialDownloadSize; const float DownloadBytesLeftFloat = DownloadNumBytesLeft; const float DownloadProgress = 1.0f - (TotalInitialDownloadSize > 0 ? DownloadBytesLeftFloat / DownloadSizeFloat : 0.0f); BuildProgress.SetStateProgress( EBuildPatchProgress::Downloading, DownloadProgress ); // Calculate the average download speed NowTime = FPlatformTime::Seconds(); DeltaTime += NowTime - LastTime; if( DeltaTime > TIME_PER_READING ) { const double BytesDownloaded = FBuildPatchDownloader::Get().GetByteDownloadCountReset(); const double TimeSinceLastReading = NowTime - LastReadingTime; LastReadingTime = NowTime; LastDataReadings[ReadingIdx] = BytesDownloaded; LastTimeReadings[ReadingIdx] = TimeSinceLastReading; ReadingIdx = (ReadingIdx + 1) % NUM_DOWNLOAD_READINGS; DeltaTime = 0; double TotalData = 0; double TotalTime = 0; for (uint32 i = 0; i < NUM_DOWNLOAD_READINGS; ++i) { TotalData += LastDataReadings[i]; TotalTime += LastTimeReadings[i]; } AverageDownloadSpeed = TotalData / TotalTime; } // Set download values SetDownloadSpeed( DownloadProgress < 1.0f ? AverageDownloadSpeed : -1.0f ); SetDownloadBytesLeft( DownloadNumBytesLeft ); // Set last time LastTime = NowTime; }
void c_main(char *blockBase, u32 blockSize) { int numRead = 0; char commandline[128]; blobStatus status; int i; int retval = 0; /* We really want to be able to communicate, so initialise the * serial port at 9k6 (which works good for terminals) */ SerialInit(baud9k6); TimerInit(); /* initialise status */ status.kernelSize = 0; status.kernelType = fromFlash; status.ramdiskSize = 0; status.ramdiskType = fromFlash; status.blockSize = blockSize; status.downloadSpeed = baud115k2; /* Load kernel and ramdisk from flash to RAM */ Reload("kernel", &status); Reload("ramdisk", &status); /* Print the required GPL string */ SerialOutputString("\r" PACKAGE " version " VERSION "\r" "Copyright (C) 1999 2000 " "Jan-Derk Bakker and Erik Mouw\r" "Copyright (C) 2000 " "Johan Pouwelse.\r"); SerialOutputString(PACKAGE " comes with ABSOLUTELY NO WARRANTY; " "read the GNU GPL for details.\r"); SerialOutputString("This is free software, and you are welcome " "to redistribute it\r"); SerialOutputString("under certain conditions; " "read the GNU GPL for details.\r\r"); /* and some information */ #ifdef BLOB_DEBUG SerialOutputString("Running from "); if(RunningFromInternal()) SerialOutputString("internal flash\r"); else SerialOutputString("external flash\r"); SerialOutputString("blockBase = 0x"); SerialOutputHex((int) blockBase); SerialOutputString(", blockSize = 0x"); SerialOutputHex(blockSize); SerialOutputByte('\r'); #endif /* wait 10 seconds before starting autoboot */ SerialOutputString("Autoboot in progress, press any key to stop "); for(i = 0; i < 10; i++) { SerialOutputByte('.'); retval = SerialInputBlock(commandline, 1, 1); if(retval > 0) break; } /* no key was pressed, so proceed booting the kernel */ if(retval == 0) { commandline[0] = '\0'; BootKernel(commandline); } SerialOutputString("\rAutoboot aborted\r"); SerialOutputString("Type \"help\" to get a list of commands\r"); /* the command loop. endless, of course */ for(;;) { DisplayPrompt(NULL); /* wait an hour to get a command */ numRead = GetCommand(commandline, 128, 3600); if(numRead > 0) { if(MyStrNCmp(commandline, "boot", 4) == 0) { BootKernel(commandline + 4); } else if(MyStrNCmp(commandline, "clock", 5) == 0) { SetClock(commandline + 5); } else if(MyStrNCmp(commandline, "download ", 9) == 0) { Download(commandline + 9, &status); } else if(MyStrNCmp(commandline, "flash ", 6) == 0) { Flash(commandline + 6, &status); } else if(MyStrNCmp(commandline, "help", 4) == 0) { PrintHelp(); } else if(MyStrNCmp(commandline, "reload ", 7) == 0) { Reload(commandline + 7, &status); } else if(MyStrNCmp(commandline, "reset", 5) == 0) { ResetTerminal(); } else if(MyStrNCmp(commandline, "speed ", 6) == 0) { SetDownloadSpeed(commandline + 6, &status); } else if(MyStrNCmp(commandline, "status", 6) == 0) { PrintStatus(&status); } else { SerialOutputString("*** Unknown command: "); SerialOutputString(commandline); SerialOutputByte('\r'); } } } } /* c_main */