[PATCH] D38416: [RFC] Add a LIT-style Progress Bar to libSupport
Justin Bogner via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 29 11:02:17 PDT 2017
Jonathan Roelofs via Phabricator via llvm-commits
<llvm-commits at lists.llvm.org> writes:
> jroelofs created this revision.
> Herald added a subscriber: mgorny.
>
> I've built this for an out-of-tree use case where we have a tool runs
> for a very long time. The task itself happens to be reasonably
> predictable in the amount of work completed, much like the corpus of
> LIT tests. Since we've already got such a thing in-tree (in python),
> namely the LIT one, I figured I'd model it off of that.
>
> Usage is something like:
>
> llvm::ProgressBar PB(llvm::outs(), "Performing Tasks");
> for (int I = 0, E = 100; I != E; ++I) {
> PB.update(I / float(E), (I < 30 ? "Task A" : "Task B"));
> sleep(1);
> }
> PB.clear();
>
> And with that, you get a nice familiar progress bar that looks like:
>
> Performing Tasks
> 20% [=====--------------------] ETA 00:01:20
> Task A
>
> with different colors, to distinguish it from LIT's (sigh... bikeshed away).
>
> Is this something that would be useful upstream? I've thought about
> tying this into the pass manager, but I don't yet see a consistent way
> to estimate the total amount of work there. Even then, are there cases
> where Clang runs for long enough for this to matter?... I don't want
> this to be an admission of defeat when it comes to compile times. Are
> there other use cases?
I have an out of tree validation test runner type tool that could
probably use this, but I can't really think of any in-tree use cases.
I'll let others comment on whether this is worth having upstream.
> Suggestions on how to write a UnitTest for it are most welcome. As-is,
> that's a little tricky because this depends on the width of the
> terminal, so printing it to a `raw_string_ostream` isn't going to give
> consistent results.
For unix, you can cheat by setting COLUMNS in the environment - we look
at that before doing any ioctl stuff. Pretty sure that won't work on
windows though.
> I've tested this on Darwin, but I have no idea whether it'll DTRT on
> other platforms. If it's not platform-agnostic enough, suggestions on
> how to fix that would also be quite welcome.
>
>
> https://reviews.llvm.org/D38416
>
> Files:
> include/llvm/Support/Progress.h
> lib/Support/CMakeLists.txt
> lib/Support/Progress.cpp
>
> Index: lib/Support/Progress.cpp
> ===================================================================
> --- /dev/null
> +++ lib/Support/Progress.cpp
> @@ -0,0 +1,107 @@
> +//===--- lib/Support/Progress.cpp - ANSI Terminal Progress Bar --*- C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This file defines an ANSI Terminal Progress Bar.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Support/Progress.h"
> +
> +#include "llvm/Support/Process.h"
> +#include "llvm/Support/raw_ostream.h"
> +#include "llvm/Support/Format.h"
> +
> +#include <cassert>
> +
> +using namespace llvm;
> +
> +void ProgressBar::update(float Percent, StringRef Msg) {
> + if (!OS.is_displayed())
> + return;
> +
> + std::string ETAStr;
> + if (ShowETA) {
> + if (!Visible)
> + Start = std::chrono::system_clock::now();
> + auto Elapsed = std::chrono::duration_cast<std::chrono::seconds>(
> + std::chrono::system_clock::now() - Start).count();
> + if (Percent > 0.0001 && Elapsed > 1) {
> + auto Total = Elapsed / Percent;
> + auto ETA = unsigned(Total - Elapsed);
> + unsigned HH = ETA / 3600;
> + unsigned MM = (ETA / 60) % 60;
> + unsigned SS = ETA % 60;
> + raw_string_ostream RSO(ETAStr);
> + RSO << format(" ETA %02d:%02d:%02d", HH, MM, SS);
> + ETAStr = RSO.str();
> + }
> + }
> +
> + clear();
> +
> + unsigned TotalWidth = sys::Process::StandardOutColumns();
> + unsigned PBWidth = TotalWidth - (1 + 4 + 2 + 1 + ETAStr.size() + 1);
> + unsigned PBDone = PBWidth * Percent;
> +
> + // Centered header
> + (OS.indent((TotalWidth - Header.size()) / 2)
> + .changeColor(HeaderColor, /*Bold=*/true)
> + << Header).resetColor() << "\n";
> +
> + // Progress bar
> + OS << format(" %3d%% ", unsigned(100 * Percent));
> + (OS.changeColor(BarColor) << "[").resetColor();
> + (OS.changeColor(BarColor, /*Bold=*/true)
> + << std::string(PBDone, '=') << std::string(PBWidth - PBDone, '-'))
> + .resetColor();
> + (OS.changeColor(BarColor) << "]").resetColor();
> + OS << ETAStr << "\n";
> +
> + // Footer messaage
> + if (Msg.size() < TotalWidth) {
> + OS << Msg;
> + Width = Msg.size();
> + } else {
> + OS << Msg.substr(0, TotalWidth - 4) << "... ";
> + Width = TotalWidth;
> + }
> +
> + // Hide the cursor
> + OS << "\033[?25l";
> +
> + OS.flush();
> + Visible = true;
> +}
> +
> +void ProgressBar::clear() {
> + if (!Visible)
> + return;
> +
> + if (!OS.is_displayed())
> + return;
> +
> + // Move to beginning of current the line
> + OS << "\033[" << (Width + 1) << "D"
> +
> + // Clear it
> + << "\033[2K"
> +
> + // Move up a line and clear it
> + << "\033[1A\033[2K"
> +
> + // Move up a line and clear it
> + << "\033[1A\033[2K"
> +
> + // Unhide the cursor
> + << "\033[?25h";
> +
> + OS.flush();
> + Visible = false;
> +}
> +
> Index: lib/Support/CMakeLists.txt
> ===================================================================
> --- lib/Support/CMakeLists.txt
> +++ lib/Support/CMakeLists.txt
> @@ -86,6 +86,7 @@
> Parallel.cpp
> PluginLoader.cpp
> PrettyStackTrace.cpp
> + Progress.cpp
> RandomNumberGenerator.cpp
> Regex.cpp
> ScaledNumber.cpp
> Index: include/llvm/Support/Progress.h
> ===================================================================
> --- /dev/null
> +++ include/llvm/Support/Progress.h
> @@ -0,0 +1,82 @@
> +//===--- Progress.h - ANSI Terminal Progress Bar ----------------*- C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This file declares an ANSI Terminal Progress Bar.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_SUPPORT_PROGRESS_H
> +#define LLVM_SUPPORT_PROGRESS_H
> +
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/Support/raw_ostream.h"
> +
> +#include <string>
> +#include <chrono>
> +
> +namespace llvm {
> +/// A 3-line progress bar, which looks like:
> +///
> +/// \verbatim
> +///
> +/// Header
> +/// 20% [===========----------------------------------]
> +/// progress message
> +///
> +/// \endverbatim
> +///
> +/// This is modeled heavily off of the one in LIT.
> +class ProgressBar {
> + // Colors purposefully chosen to be *different* than LIT's so as not to be
> + // confusing. For just about everything else, we try to match the behavior
> + // pretty closely.
> + static const auto HeaderColor = llvm::raw_ostream::YELLOW;
> + static const auto BarColor = llvm::raw_ostream::MAGENTA;
> +public:
> + ProgressBar(llvm::raw_ostream &OS, llvm::StringRef Header, bool ETA=true)
> + : OS(OS), Header(Header), ShowETA(ETA) {}
> +
> + /// Update the amount of progress.
> + ///
> + /// Causes the bar to be Visible if it wasn't already.
> + ///
> + /// \param Percent A value in the range [0.0f, 1.0f] indicating the
> + /// amount of progress to display.
> + /// \param Msg The message to display underneath the bar.
> + void update(float Percent, llvm::StringRef Msg);
> +
> + /// Remove the progress bar from the terminal.
> + void clear();
> +
> +private:
> +
> + /// The stream being written to
> + llvm::raw_ostream &OS;
> +
> + /// Header text to print centered at the top of the bar
> + std::string Header;
> +
> + /// Whether to make a guess at estimated completion
> + bool ShowETA;
> +
> + /// If `ShowETA && Visible`, contains the time when the progress bar was
> + /// first displayed
> + std::chrono::time_point<std::chrono::system_clock> Start;
> +
> + /// Is the progress bar being displayed?
> + bool Visible = false;
> +
> + /// Total number of characters written out on the last line
> + unsigned Width = 0;
> +};
> +
> +} // namespace llvm
> +
> +#endif
> +
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list