diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/builds_cmd.cpp | 206 | ||||
| -rw-r--r-- | src/zen/cmds/builds_cmd.h | 1 | ||||
| -rw-r--r-- | src/zen/cmds/projectstore_cmd.cpp | 6 | ||||
| -rw-r--r-- | src/zen/cmds/wipe_cmd.cpp | 26 | ||||
| -rw-r--r-- | src/zen/zen.cpp | 181 | ||||
| -rw-r--r-- | src/zen/zen.h | 25 |
6 files changed, 350 insertions, 95 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp index 15c635594..134cde0f8 100644 --- a/src/zen/cmds/builds_cmd.cpp +++ b/src/zen/cmds/builds_cmd.cpp @@ -145,8 +145,24 @@ namespace { const std::vector<std::string_view> DefaultExcludeFolders({UnsyncFolderName, ZenFolderName, UGSFolderName, LegacyZenTempFolderName}); const std::vector<std::string_view> DefaultExcludeExtensions({}); - static bool IsVerbose = false; - static bool UsePlainProgress = false; + static bool IsVerbose = false; + static ProgressBar::Mode ProgressMode = ProgressBar::Mode::Pretty; + + uint32_t GetUpdateDelayMS(ProgressBar::Mode InMode) + { + switch (InMode) + { + case ProgressBar::Mode::Plain: + return 5000; + case ProgressBar::Mode::Pretty: + return 200; + case ProgressBar::Mode::Log: + return 2000; + default: + ZEN_ASSERT(false); + return 0; + } + } #define ZEN_CONSOLE_VERBOSE(fmtstr, ...) \ if (IsVerbose) \ @@ -333,7 +349,7 @@ namespace { ZEN_TRACE_CPU("CleanDirectory"); Stopwatch Timer; - ProgressBar Progress(UsePlainProgress); + ProgressBar Progress(ProgressMode, "Clean Folder"); std::atomic<bool> CleanWipe = true; std::atomic<uint64_t> DiscoveredItemCount = 0; @@ -453,7 +469,7 @@ namespace { uint64_t LastUpdateTimeMs = Timer.GetElapsedTimeMs(); - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); LastUpdateTimeMs = Timer.GetElapsedTimeMs(); @@ -492,7 +508,7 @@ namespace { } uint64_t NowMs = Timer.GetElapsedTimeMs(); - if ((NowMs - LastUpdateTimeMs) >= (UsePlainProgress ? 5000 : 200)) + if ((NowMs - LastUpdateTimeMs) >= GetUpdateDelayMS(ProgressMode)) { LastUpdateTimeMs = NowMs; @@ -1814,15 +1830,34 @@ namespace { ValidateStatistics& ValidateStats, DownloadStatistics& DownloadStats) { + ZEN_TRACE_CPU("ValidateBuildPart"); + + ProgressBar::SetLogOperationName(ProgressMode, "Validate Part"); + + enum TaskSteps : uint32_t + { + FetchBuild, + FetchBuildPart, + ValidateBlobs, + Cleanup, + StepCount + }; + + auto EndProgress = + MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); }); + Stopwatch Timer; - auto _ = MakeGuard([&]() { + auto _ = MakeGuard([&]() { ZEN_CONSOLE("Validated build part {}/{} ('{}') in {}", BuildId, BuildPartId, BuildPartName, NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); - CbObject Build = Storage.GetBuild(BuildId); + + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::FetchBuild, TaskSteps::StepCount); + + CbObject Build = Storage.GetBuild(BuildId); if (!BuildPartName.empty()) { BuildPartId = Build["parts"sv].AsObjectView()[BuildPartName].AsObjectId(); @@ -1837,6 +1872,9 @@ namespace { { PreferredMultipartChunkSize = ChunkSize; } + + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::FetchBuildPart, TaskSteps::StepCount); + CbObject BuildPart = Storage.GetBuildPart(BuildId, BuildPartId); ValidateStats.BuildPartSize = BuildPart.GetSize(); ZEN_CONSOLE("Validating build part {}/{} ({})", BuildId, BuildPartId, NiceBytes(BuildPart.GetSize())); @@ -1876,7 +1914,9 @@ namespace { } }); - ProgressBar ProgressBar(UsePlainProgress); + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::ValidateBlobs, TaskSteps::StepCount); + + ProgressBar ProgressBar(ProgressMode, "Validate Blobs"); uint64_t AttachmentsToVerifyCount = ChunkAttachments.size() + BlockAttachments.size(); FilteredRate FilteredDownloadedBytesPerSecond; @@ -1990,7 +2030,7 @@ namespace { Work.DefaultErrorFunction()); } - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); const uint64_t DownloadedAttachmentCount = DownloadStats.DownloadedChunkCount + DownloadStats.DownloadedBlockCount; @@ -2020,6 +2060,8 @@ namespace { ProgressBar.Finish(); ValidateStats.ElapsedWallTimeUS = Timer.GetElapsedTimeUs(); + + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount); } void ArrangeChunksIntoBlocks(const ChunkedFolderContent& Content, @@ -2226,7 +2268,7 @@ namespace { const std::size_t NewBlockCount = NewBlockChunks.size(); if (NewBlockCount > 0) { - ProgressBar ProgressBar(UsePlainProgress); + ProgressBar ProgressBar(ProgressMode, "Generate Blocks"); OutBlocks.BlockDescriptions.resize(NewBlockCount); OutBlocks.BlockSizes.resize(NewBlockCount); @@ -2389,7 +2431,7 @@ namespace { Work.DefaultErrorFunction()); } - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); FilteredGeneratedBytesPerSecond.Update(GenerateBlocksStats.GeneratedBlockByteCount.load()); @@ -2439,7 +2481,7 @@ namespace { { ZEN_TRACE_CPU("UploadPartBlobs"); { - ProgressBar ProgressBar(UsePlainProgress); + ProgressBar ProgressBar(ProgressMode, "Upload Blobs"); WorkerThreadPool& ReadChunkPool = GetIOWorkerPool(); WorkerThreadPool& UploadChunkPool = GetNetworkPool(); @@ -2752,7 +2794,7 @@ namespace { Work.DefaultErrorFunction()); } - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); FilteredCompressedBytesPerSecond.Update(LooseChunksStats.CompressedChunkRawBytes.load()); FilteredGenerateBlockBytesPerSecond.Update(GeneratedBlockByteCount.load()); @@ -2971,6 +3013,23 @@ namespace { bool IgnoreExistingBlocks, bool PostUploadVerify) { + ZEN_TRACE_CPU("UploadFolder"); + + ProgressBar::SetLogOperationName(ProgressMode, "Upload Folder"); + + enum TaskSteps : uint32_t + { + PrepareBuild, + CalculateDelta, + Upload, + Validate, + Cleanup, + StepCount + }; + + auto EndProgress = + MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); }); + Stopwatch ProcessTimer; const std::filesystem::path ZenTempFolder = ZenTempFolderPath(ZenFolderPath); @@ -2986,6 +3045,8 @@ namespace { CreateDirectories(ZenTempBlockFolderPath(ZenFolderPath)); CreateDirectories(ZenTempChunkFolderPath(ZenFolderPath)); + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::PrepareBuild, TaskSteps::StepCount); + std::uint64_t TotalRawSize = 0; CbObject ChunkerParameters; @@ -3157,7 +3218,7 @@ namespace { return true; }, GetIOWorkerPool(), - UsePlainProgress ? 5000 : 200, + GetUpdateDelayMS(ProgressMode), [&](bool, std::ptrdiff_t) { ZEN_CONSOLE_VERBOSE("Found {} files in '{}'...", LocalFolderScanStats.AcceptedFileCount.load(), Path); }, @@ -3212,7 +3273,7 @@ namespace { TotalRawSize = std::accumulate(Content.RawSizes.begin(), Content.RawSizes.end(), std::uint64_t(0)); { - ProgressBar ProgressBar(UsePlainProgress); + ProgressBar ProgressBar(ProgressMode, "Scan Files"); FilteredRate FilteredBytesHashed; FilteredBytesHashed.Start(); LocalContent = ChunkFolderContent( @@ -3221,7 +3282,7 @@ namespace { Path, Content, *ChunkController, - UsePlainProgress ? 5000 : 200, + GetUpdateDelayMS(ProgressMode), [&](bool, std::ptrdiff_t) { FilteredBytesHashed.Update(ChunkingStats.BytesHashed.load()); std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found", @@ -3277,6 +3338,8 @@ namespace { PrepBuildResult.KnownBlocks.size(), NiceTimeSpanMs(PrepBuildResult.FindBlocksTimeMs))); + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CalculateDelta, TaskSteps::StepCount); + const std::uint64_t LargeAttachmentSize = AllowMultiparts ? PrepBuildResult.PreferredMultipartChunkSize * 4u : (std::uint64_t)-1; Stopwatch BlockArrangeTimer; @@ -3386,6 +3449,8 @@ namespace { UploadStatistics UploadStats; GeneratedBlocks NewBlocks; + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Upload, TaskSteps::StepCount); + if (!NewBlockChunks.empty()) { Stopwatch GenerateBuildBlocksTimer; @@ -3710,6 +3775,7 @@ namespace { DownloadStatistics ValidateDownloadStats; if (PostUploadVerify && !AbortFlag) { + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Validate, TaskSteps::StepCount); ValidateBuildPart(*Storage.BuildStorage, BuildId, BuildPartId, BuildPartName, ValidateStats, ValidateDownloadStats); } @@ -3961,6 +4027,8 @@ namespace { {"uploadedBytesPerSec", double(GetBytesPerSecond(UploadStats.ElapsedWallTimeUS, UploadStats.ChunksBytes + UploadStats.BlocksBytes))}, {"elapsedTimeSec", double(ProcessTimer.GetElapsedTimeMs() / 1000.0)}}); + + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount); } void VerifyFolder(const ChunkedFolderContent& Content, @@ -3972,7 +4040,7 @@ namespace { Stopwatch Timer; - ProgressBar ProgressBar(UsePlainProgress); + ProgressBar ProgressBar(ProgressMode, "Verify Files"); WorkerThreadPool& VerifyPool = GetIOWorkerPool(); @@ -4114,7 +4182,7 @@ namespace { }); } - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); std::string Details = fmt::format("Verified {}/{} ({}). Failed files: {}", VerifyFolderStats.FilesVerified.load(), @@ -5085,7 +5153,7 @@ namespace { Stopwatch Timer; auto _ = MakeGuard([&LocalFolderScanStats, &Timer]() { LocalFolderScanStats.ElapsedWallTimeUS = Timer.GetElapsedTimeUs(); }); - ProgressBar ProgressBar(UsePlainProgress); + ProgressBar ProgressBar(ProgressMode, "Check Files"); ParallellWork Work(AbortFlag); std::atomic<uint64_t> CompletedPathCount = 0; @@ -5751,7 +5819,7 @@ namespace { WorkerThreadPool& NetworkPool = GetNetworkPool(); WorkerThreadPool& WritePool = GetIOWorkerPool(); - ProgressBar WriteProgressBar(UsePlainProgress); + ProgressBar WriteProgressBar(ProgressMode, PrimeCacheOnly ? "Downloading" : "Writing"); ParallellWork Work(AbortFlag); struct LooseChunkHashWorkData @@ -7054,7 +7122,7 @@ namespace { { ZEN_TRACE_CPU("WriteChunks_Wait"); - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); uint64_t DownloadedBytes = DownloadStats.DownloadedChunkByteCount.load() + DownloadStats.DownloadedBlockByteCount.load() + @@ -7241,7 +7309,7 @@ namespace { WorkerThreadPool& WritePool = GetIOWorkerPool(); - ProgressBar CacheLocalProgressBar(UsePlainProgress); + ProgressBar CacheLocalProgressBar(ProgressMode, "Cache Local Data"); ParallellWork Work(AbortFlag); for (uint32_t LocalPathIndex : FilesToCache) @@ -7272,7 +7340,7 @@ namespace { { ZEN_TRACE_CPU("CacheLocal_Wait"); - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); const uint64_t WorkTotal = FilesToCache.size(); const uint64_t WorkComplete = CachedCount.load(); @@ -7329,7 +7397,7 @@ namespace { WorkerThreadPool& WritePool = GetIOWorkerPool(); - ProgressBar RebuildProgressBar(UsePlainProgress); + ProgressBar RebuildProgressBar(ProgressMode, "Rebuild State"); ParallellWork Work(AbortFlag); OutLocalFolderState.Paths.resize(RemoteContent.Paths.size()); @@ -7566,7 +7634,7 @@ namespace { { ZEN_TRACE_CPU("FinalizeTree_Wait"); - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, std::ptrdiff_t PendingWork) { + Work.Wait(GetUpdateDelayMS(ProgressMode), [&](bool IsAborted, std::ptrdiff_t PendingWork) { ZEN_UNUSED(IsAborted, PendingWork); const uint64_t WorkTotal = Targets.size() + RemoveLocalPathIndexes.size(); const uint64_t WorkComplete = TargetsComplete.load() + DeletedCount.load(); @@ -8164,7 +8232,7 @@ namespace { { ByteCountToScan += RawSize; } - ProgressBar ProgressBar(false); + ProgressBar ProgressBar(ProgressMode, "Scan Files"); FilteredRate FilteredBytesHashed; FilteredBytesHashed.Start(); ChunkedFolderContent UpdatedLocalContent = ChunkFolderContent( @@ -8173,7 +8241,7 @@ namespace { Path, UpdatedContent, ChunkController, - UsePlainProgress ? 5000 : 200, + GetUpdateDelayMS(ProgressMode), [&](bool, std::ptrdiff_t) { FilteredBytesHashed.Update(ChunkingStats.BytesHashed.load()); std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found", @@ -8232,7 +8300,7 @@ namespace { { ByteCountToScan += RawSize; } - ProgressBar ProgressBar(false); + ProgressBar ProgressBar(ProgressMode, "Scan Files"); FilteredRate FilteredBytesHashed; FilteredBytesHashed.Start(); LocalContent = ChunkFolderContent( @@ -8241,7 +8309,7 @@ namespace { Path, OutLocalFolderContent, ChunkController, - UsePlainProgress ? 5000 : 200, + GetUpdateDelayMS(ProgressMode), [&](bool, std::ptrdiff_t) { FilteredBytesHashed.Update(ChunkingStats.BytesHashed.load()); std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found", @@ -8289,7 +8357,7 @@ namespace { std::move(IsAcceptedFolder), std::move(IsAcceptedFile), GetIOWorkerPool(), - UsePlainProgress ? 5000 : 200, + GetUpdateDelayMS(ProgressMode), [](bool, std::ptrdiff_t) {}, AbortFlag); if (AbortFlag) @@ -8337,10 +8405,27 @@ namespace { { ZEN_TRACE_CPU("DownloadFolder"); + ProgressBar::SetLogOperationName(ProgressMode, "Download Folder"); + + enum TaskSteps : uint32_t + { + CheckState, + CompareState, + Download, + Verify, + Cleanup, + StepCount + }; + + auto EndProgress = + MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); }); + ZEN_ASSERT((!PrimeCacheOnly) || (PrimeCacheOnly && (!AllowPartialBlockRequests))); Stopwatch DownloadTimer; + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CheckState, TaskSteps::StepCount); + const std::filesystem::path ZenTempFolder = ZenTempFolderPath(ZenFolderPath); CreateDirectories(ZenTempFolder); @@ -8360,6 +8445,8 @@ namespace { std::vector<ChunkBlockDescription> BlockDescriptions; std::vector<IoHash> LooseChunkHashes; + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CompareState, TaskSteps::StepCount); + ChunkedFolderContent RemoteContent = GetRemoteContent(Storage, BuildId, AllBuildParts, ChunkController, PartContents, BlockDescriptions, LooseChunkHashes); @@ -8471,6 +8558,8 @@ namespace { RebuildFolderStateStatistics RebuildFolderStateStats; VerifyFolderStatistics VerifyFolderStats; + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Download, TaskSteps::StepCount); + UpdateFolder(SystemRootDir, Storage, BuildId, @@ -8497,6 +8586,8 @@ namespace { { if (!PrimeCacheOnly) { + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Verify, TaskSteps::StepCount); + VerifyFolder(RemoteContent, Path, PostDownloadVerify, VerifyFolderStats); Stopwatch WriteStateTimer; @@ -8564,6 +8655,9 @@ namespace { }); } } + + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount); + if (CleanDirectory(ZenTempFolder, {})) { RemoveDirWithRetry(ZenTempFolder); @@ -8572,6 +8666,22 @@ namespace { void DiffFolders(const std::filesystem::path& BasePath, const std::filesystem::path& ComparePath, bool OnlyChunked) { + ZEN_TRACE_CPU("DiffFolders"); + + ProgressBar::SetLogOperationName(ProgressMode, "Diff Folders"); + + enum TaskSteps : uint32_t + { + CheckBase, + CheckCompare, + Diff, + Cleanup, + StepCount + }; + + auto EndProgress = + MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); }); + ChunkedFolderContent BaseFolderContent; ChunkedFolderContent CompareFolderContent; @@ -8614,6 +8724,8 @@ namespace { return true; }; + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CheckBase, TaskSteps::StepCount); + GetFolderContentStatistics BaseGetFolderContentStats; ChunkingStatistics BaseChunkingStats; BaseFolderContent = ScanAndChunkFolder(BaseGetFolderContentStats, @@ -8627,6 +8739,8 @@ namespace { return; } + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::CheckCompare, TaskSteps::StepCount); + GetFolderContentStatistics CompareGetFolderContentStats; ChunkingStatistics CompareChunkingStats; CompareFolderContent = ScanAndChunkFolder(CompareGetFolderContentStats, @@ -8642,6 +8756,8 @@ namespace { } } + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Diff, TaskSteps::StepCount); + std::vector<IoHash> AddedHashes; std::vector<IoHash> RemovedHashes; uint64_t RemovedSize = 0; @@ -8728,6 +8844,8 @@ namespace { NewChunkCount, NiceBytes(NewChunkSize), NewPercent); + + ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Cleanup, TaskSteps::StepCount); } } // namespace @@ -8852,7 +8970,18 @@ BuildsCommand::BuildsCommand() }; auto AddOutputOptions = [this](cxxopts::Options& Ops) { - Ops.add_option("output", "", "plain-progress", "Show progress using plain output", cxxopts::value(m_PlainProgress), "<progress>"); + Ops.add_option("output", + "", + "plain-progress", + "Show progress using plain output", + cxxopts::value(m_PlainProgress), + "<plainprogress>"); + Ops.add_option("output", + "", + "log-progress", + "Write @progress style progress to output", + cxxopts::value(m_LogProgress), + "<logprogress>"); Ops.add_option("output", "", "verbose", "Enable verbose console output", cxxopts::value(m_Verbose), "<verbose>"); }; @@ -9335,8 +9464,19 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) }; auto ParseOutputOptions = [&]() { - IsVerbose = m_Verbose; - UsePlainProgress = IsVerbose || m_PlainProgress; + IsVerbose = m_Verbose; + if (m_LogProgress) + { + ProgressMode = ProgressBar::Mode::Log; + } + else if (m_Verbose || m_PlainProgress) + { + ProgressMode = ProgressBar::Mode::Plain; + } + else + { + ProgressMode = ProgressBar::Mode::Pretty; + } }; ParseOutputOptions(); diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h index 01d510e1b..9f6c1f6ec 100644 --- a/src/zen/cmds/builds_cmd.h +++ b/src/zen/cmds/builds_cmd.h @@ -28,6 +28,7 @@ private: std::filesystem::path m_SystemRootDir; bool m_PlainProgress = false; + bool m_LogProgress = false; bool m_Verbose = false; bool m_BoostWorkerThreads = false; diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp index c73842b89..540c4c9ac 100644 --- a/src/zen/cmds/projectstore_cmd.cpp +++ b/src/zen/cmds/projectstore_cmd.cpp @@ -168,7 +168,7 @@ namespace { throw std::runtime_error(fmt::format("invalid job id returned, received '{}'", JobIdText)); } - ProgressBar ProgressBar(PlainProgress); + ProgressBar ProgressBar(PlainProgress ? ProgressBar::Mode::Plain : ProgressBar::Mode::Pretty, ""sv); auto OuputMessages = [&](CbObjectView StatusObject) { CbArrayView Messages = StatusObject["Messages"sv].AsArrayView(); @@ -2091,7 +2091,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg { std::unique_ptr<ProgressBar> EmitProgressBar; { - ProgressBar ParseProgressBar(false); + ProgressBar ParseProgressBar(ProgressBar::Mode::Pretty, ""); CbArrayView Entries = ResponseObject["entries"sv].AsArrayView(); uint64_t Remaining = Entries.Num(); for (auto EntryIter : Entries) @@ -2110,7 +2110,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg } if (!EmitProgressBar) { - EmitProgressBar = std::make_unique<ProgressBar>(false); + EmitProgressBar = std::make_unique<ProgressBar>(ProgressBar::Mode::Pretty, ""sv); WriteStopWatch.Reset(); } diff --git a/src/zen/cmds/wipe_cmd.cpp b/src/zen/cmds/wipe_cmd.cpp index 2b4e9ab3c..9bf0c6f9e 100644 --- a/src/zen/cmds/wipe_cmd.cpp +++ b/src/zen/cmds/wipe_cmd.cpp @@ -35,10 +35,26 @@ namespace { static std::atomic<bool> AbortFlag = false; static bool IsVerbose = false; static bool Quiet = false; - static bool UsePlainProgress = false; + static ProgressBar::Mode ProgressMode = ProgressBar::Mode::Pretty; const bool SingleThreaded = false; bool BoostWorkerThreads = true; + uint32_t GetUpdateDelayMS(ProgressBar::Mode InMode) + { + switch (InMode) + { + case ProgressBar::Mode::Plain: + return 5000; + case ProgressBar::Mode::Pretty: + return 200; + case ProgressBar::Mode::Log: + return 2000; + default: + ZEN_ASSERT(false); + return 0; + } + } + WorkerThreadPool& GetIOWorkerPool() { return SingleThreaded ? GetSyncWorkerPool() @@ -161,7 +177,7 @@ namespace { ZEN_TRACE_CPU("CleanDirectory"); Stopwatch Timer; - ProgressBar Progress(UsePlainProgress); + ProgressBar Progress(ProgressMode, "Clean Folder"); std::atomic<bool> CleanWipe = true; std::atomic<uint64_t> DiscoveredItemCount = 0; @@ -396,7 +412,7 @@ namespace { GetIOWorkerPool(), Work.PendingWork()); - Work.Wait(UsePlainProgress ? 5000 : 200, [&](bool IsAborted, ptrdiff_t PendingWork) { + Work.Wait(ProgressMode == ProgressBar::Mode::Pretty ? 200 : 5000, [&](bool IsAborted, ptrdiff_t PendingWork) { if (Quiet) { return; @@ -455,7 +471,7 @@ namespace { } uint64_t NowMs = Timer.GetElapsedTimeMs(); - if ((NowMs - LastUpdateTimeMs) >= (UsePlainProgress ? 5000 : 200)) + if ((NowMs - LastUpdateTimeMs) >= GetUpdateDelayMS(ProgressMode)) { LastUpdateTimeMs = NowMs; @@ -529,7 +545,7 @@ WipeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Quiet = m_Quiet; IsVerbose = m_Verbose; - UsePlainProgress = IsVerbose || m_PlainProgress; + ProgressMode = (IsVerbose || m_PlainProgress) ? ProgressBar::Mode::Plain : ProgressBar::Mode::Pretty; BoostWorkerThreads = m_BoostWorkerThreads; MakeSafeAbsolutePathÍnPlace(m_Directory); diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index e442f8a4b..399c43990 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -269,25 +269,120 @@ ZenCmdBase::ResolveTargetHostSpec(const std::string& InHostSpec) return ResolveTargetHostSpec(InHostSpec, /* out */ Dummy); } +#if ZEN_PLATFORM_WINDOWS +static HANDLE +GetConsoleHandle() +{ + static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + return hStdOut; +} +#endif + static bool -IsStdoutTty() +CheckStdoutTty() { #if ZEN_PLATFORM_WINDOWS - static HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode); + HANDLE hStdOut = GetConsoleHandle(); + DWORD dwMode = 0; + static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode); return IsConsole; #else return isatty(fileno(stdout)); #endif } -ProgressBar::ProgressBar(bool PlainProgress, bool ShowDetails) -: m_StdoutIsTty(IsStdoutTty()) -, m_PlainProgress(PlainProgress || !m_StdoutIsTty) -, m_ShowDetails(ShowDetails) +static bool +IsStdoutTty() +{ + static bool StdoutIsTty = CheckStdoutTty(); + return StdoutIsTty; +} + +static void +OutputToConsoleRaw(const char* String, size_t Length) +{ +#if ZEN_PLATFORM_WINDOWS + HANDLE hStdOut = GetConsoleHandle(); +#endif + +#if ZEN_PLATFORM_WINDOWS + if (IsStdoutTty()) + { + WriteConsoleA(hStdOut, String, (DWORD)Length, 0, 0); + } + else + { + ::WriteFile(hStdOut, (LPCVOID)String, (DWORD)Length, 0, 0); + } +#else + fwrite(String, 1, Length, stdout); +#endif +} + +static void +OutputToConsoleRaw(const std::string& String) +{ + OutputToConsoleRaw(String.c_str(), String.length()); +} + +static void +OutputToConsoleRaw(const StringBuilderBase& SB) +{ + OutputToConsoleRaw(SB.c_str(), SB.Size()); +} + +static uint32_t +GetConsoleColumns() +{ +#if ZEN_PLATFORM_WINDOWS + HANDLE hStdOut = GetConsoleHandle(); + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE) + { + return (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1); + } +#else + struct winsize w; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) + { + return (uint32_t)w.ws_col; + } +#endif + return 1024; +} + +void +ProgressBar::SetLogOperationName(Mode InMode, std::string_view Name) +{ + if (InMode == Mode::Log) + { + std::string String = fmt::format("@progress {}\n", Name); + OutputToConsoleRaw(String); + } +} + +void +ProgressBar::SetLogOperationProgress(Mode InMode, uint32_t StepIndex, uint32_t StepCount) +{ + if (InMode == Mode::Log) + { + const size_t PercentDone = StepCount > 0u ? gsl::narrow<uint8_t>((100 * StepIndex) / StepCount) : 0u; + + std::string String = fmt::format("@progress {}%\n", PercentDone); + OutputToConsoleRaw(String); + } +} + +ProgressBar::ProgressBar(Mode InMode, std::string_view InSubTask) +: m_Mode((!IsStdoutTty() && InMode == Mode::Pretty) ? Mode::Plain : InMode) , m_LastUpdateMS(m_SW.GetElapsedTimeMs() - 10000) +, m_SubTask(InSubTask) { + if (!m_SubTask.empty() && InMode == Mode::Log) + { + std::string String = fmt::format("@progress push {}\n", m_SubTask); + OutputToConsoleRaw(String); + } } ProgressBar::~ProgressBar() @@ -295,6 +390,11 @@ ProgressBar::~ProgressBar() try { ForceLinebreak(); + if (!m_SubTask.empty() && m_Mode == Mode::Log) + { + const std::string String("@progress pop\n"); + OutputToConsoleRaw(String); + } } catch (const std::exception& Ex) { @@ -319,15 +419,16 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) m_LastUpdateMS = ElapsedTimeMS; - size_t PercentDone = + const size_t PercentDone = NewState.TotalCount > 0u ? gsl::narrow<uint8_t>((100 * (NewState.TotalCount - NewState.RemainingCount)) / NewState.TotalCount) : 0u; - if (m_PlainProgress) + if (m_Mode == Mode::Plain) { - std::string Details = (m_ShowDetails && !NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : ""; - ZEN_CONSOLE("{} {}% ({}){}", NewState.Task, PercentDone, NiceTimeSpanMs(ElapsedTimeMS), Details); + const std::string Details = (!NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : ""; + const std::string Output = fmt::format("{} {}% ({}){}\n", NewState.Task, PercentDone, NiceTimeSpanMs(ElapsedTimeMS), Details); + OutputToConsoleRaw(Output); } - else + else if (m_Mode == Mode::Pretty) { size_t ProgressBarSize = 20; @@ -335,28 +436,7 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) uint64_t Completed = NewState.TotalCount - NewState.RemainingCount; uint64_t ETAMS = (PercentDone > 5) ? (ElapsedTimeMS * NewState.RemainingCount) / Completed : 0; - uint32_t ConsoleColumns = 1024; - -#if ZEN_PLATFORM_WINDOWS - static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); -#endif - - if (!m_PlainProgress) - { -#if ZEN_PLATFORM_WINDOWS - CONSOLE_SCREEN_BUFFER_INFO csbi; - if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE) - { - ConsoleColumns = (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1); - } -#else - struct winsize w; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) - { - ConsoleColumns = (uint32_t)w.ws_col; - } -#endif - } + uint32_t ConsoleColumns = GetConsoleColumns(); std::string_view TaskString = NewState.Task; @@ -428,21 +508,30 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) LineToPrint << "\n"; } -#if ZEN_PLATFORM_WINDOWS - if (m_StdoutIsTty) + OutputToConsoleRaw(LineToPrint); + + m_LastOutputLength = DoLinebreak ? 0 : Output.length(); + m_State = NewState; + } + else if (m_Mode == Mode::Log) + { + if (m_State.Task != NewState.Task || + m_State.Details != NewState.Details) // TODO: Should we output just because details change? Will this spam the log collector? { - WriteConsoleA(hStdOut, LineToPrint.c_str(), (DWORD)LineToPrint.Size(), 0, 0); + const std::string Details = (!NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : ""; + const std::string Message = fmt::format("@progress {} ({}){}\n", NewState.Task, NiceTimeSpanMs(ElapsedTimeMS), Details); + OutputToConsoleRaw(Message); } - else + + const size_t OldPercentDone = + m_State.TotalCount > 0u ? gsl::narrow<uint8_t>((100 * (m_State.TotalCount - m_State.RemainingCount)) / m_State.TotalCount) : 0u; + + if (OldPercentDone != PercentDone) { - ::WriteFile(hStdOut, (LPCVOID)LineToPrint.c_str(), (DWORD)LineToPrint.Size(), 0, 0); + const std::string Progress = fmt::format("@progress {}%\n", PercentDone); + OutputToConsoleRaw(Progress); } -#else - fwrite(LineToPrint.c_str(), 1, LineToPrint.Size(), stdout); -#endif - - m_LastOutputLength = DoLinebreak ? 0 : Output.length(); - m_State = NewState; + m_State = NewState; } } diff --git a/src/zen/zen.h b/src/zen/zen.h index dd0fd44b3..9ca228e37 100644 --- a/src/zen/zen.h +++ b/src/zen/zen.h @@ -81,7 +81,17 @@ public: uint64_t RemainingCount = 0; }; - explicit ProgressBar(bool PlainProgress, bool ShowDetails = true); + enum class Mode + { + Plain, + Pretty, + Log + }; + + static void SetLogOperationName(Mode InMode, std::string_view Name); + static void SetLogOperationProgress(Mode InMode, uint32_t StepIndex, uint32_t StepCount); + + explicit ProgressBar(Mode InMode, std::string_view InSubTask); ~ProgressBar(); void UpdateState(const State& NewState, bool DoLinebreak); @@ -91,13 +101,12 @@ public: bool HasActiveTask() const; private: - const bool m_StdoutIsTty = true; - const bool m_PlainProgress; - const bool m_ShowDetails; - Stopwatch m_SW; - uint64_t m_LastUpdateMS; - State m_State; - size_t m_LastOutputLength = 0; + const Mode m_Mode; + Stopwatch m_SW; + uint64_t m_LastUpdateMS; + State m_State; + const std::string m_SubTask; + size_t m_LastOutputLength = 0; }; } // namespace zen |