m_streams.pop_back ();
}
-/* The cli_ui_out::do_progress_{start, notify} functions result in
- the following:
-
- - printed for tty, SHOULD_PRINT == true
- - next state == PERCENT:
- <(XX%) NAME\r>
- - next state == SPIN:
- <-\|/ NAME\r>
- - next state == BAR:
- <NAME
- [##### ]\r>
- - printed for tty, SHOULD_PRINT == false:
- <>
- - printed for not-a-tty:
- <NAME...
- >
-*/
-
void
-cli_ui_out::do_progress_start (const std::string &name, bool should_print)
+cli_ui_out::do_progress_start ()
{
- struct ui_file *stream = m_streams.back ();
cli_progress_info info;
- info.name = name;
- if (!stream->isatty ())
- {
- fprintf_unfiltered (stream, "%s\n", info.name.c_str ());
- gdb_flush (stream);
- info.state = progress_update::WORKING;
- }
- else
- {
- /* Don't actually emit anything until the first call notifies us
- of progress. This makes it so a second progress message can
- be started before the first one has been notified, without
- messy output. */
- info.state = should_print ? progress_update::START
- : progress_update::NO_PRINT;
- }
-
+ info.pos = 0;
+ info.state = progress_update::START;
m_progress_info.push_back (std::move (info));
}
/* Pick a reasonable limit for the progress update length. */
#define MAX_CHARS_PER_LINE 4096
+/* Print a progress update. MSG is a string to be printed before
+ If HOWMUCH is between 0.0 and 1.0, a progress bar is displayed
+ indicating the percentage of completion. If HOWMUCH is negative,
+ a progress indicator ticks across the screen. Multiple calls
+ to this function progressively update the display.
+
+ - printed for tty, HOWMUCH between 0.0 and 1.0:
+ <NAME
+ [######## ]\r>
+ - printed for tty, HOWMUCH < 0.0:
+ <NAME
+ [ ### ]\r>
+ - printed for not-a-tty:
+ <NAME...
+ >
+*/
+
void
-cli_ui_out::do_progress_notify (double howmuch,
- progress_update::state next_state)
+cli_ui_out::do_progress_notify (const std::string &msg, double howmuch)
{
struct ui_file *stream = m_streams.back ();
cli_progress_info &info (m_progress_info.back ());
- if (info.state == progress_update::NO_PRINT)
- return;
-
- int chars_per_line = get_chars_per_line ();
- if (chars_per_line > MAX_CHARS_PER_LINE)
- chars_per_line = MAX_CHARS_PER_LINE;
-
if (info.state == progress_update::START)
{
- fprintf_unfiltered (stream, "%s", info.name.c_str ());
- if (chars_per_line <= 0)
- fprintf_unfiltered (stream, "\n");
- gdb_flush (stream);
- info.state = progress_update::WORKING;
+ if (!stream->isatty ())
+ {
+ fprintf_unfiltered (stream, "%s...\n", msg.c_str ());
+ info.state = progress_update::WORKING;
+ }
+ else
+ {
+ fprintf_unfiltered (stream, "%s\n", msg.c_str ());
+ info.state = progress_update::BAR;
+ }
}
- if (chars_per_line <= 0)
- return;
-
- if (info.state == progress_update::WORKING && howmuch >= 1.0)
- return;
+ int chars_per_line = get_chars_per_line ();
+ if (chars_per_line > MAX_CHARS_PER_LINE)
+ chars_per_line = MAX_CHARS_PER_LINE;
- if (!stream->isatty ())
+ if (chars_per_line <= 0
+ || info.state == progress_update::WORKING
+ || !stream->isatty ())
return;
-
- if (next_state == progress_update::PERCENT)
+/*
+ if (howmuch >= 0)
{
- fprintf_unfiltered (stream, "\r(%2.0f%%) %s",
- howmuch * 100, info.name.c_str ());
+ int width = chars_per_line - 3;
+ int max = width * howmuch;
+
+ fprintf_unfiltered (stream, "\r[");
+ for (int i = 0; i < width; ++i)
+ fprintf_unfiltered (stream, i < max ? "#" : " ");
+ fprintf_unfiltered (stream, "]");
gdb_flush (stream);
- info.state = progress_update::PERCENT;
}
- else if (next_state == progress_update::SPIN)
+ else
{
using namespace std::chrono;
- seconds diff = duration_cast<seconds>
+ milliseconds diff = duration_cast<milliseconds>
(steady_clock::now () - info.last_update);
- /* Advance the spinner no faster than 1 tick per second. */
- if (diff.count () >= 1.0)
+ * Advance the progress indicator at a rate of 1 tick every
+ every 0.5 seconds. *
+ if (diff.count () >= 500)
{
- static int spin = 0;
+ int width = chars_per_line - 3;
- fprintf_unfiltered (stream, "\r%c %s", "-\\|/"[spin++ % 4],
- info.name.c_str ());
+ fprintf_unfiltered (stream, "\r[");
+
+ for (int i = 0; i < width; ++i)
+ {
+ if (i >= info.pos % width
+ && i < (info.pos + 3) % width)
+ fprintf_unfiltered (stream, "#");
+ else
+ fprintf_unfiltered (stream, " ");
+ }
+
+ fprintf_unfiltered (stream, "]");
gdb_flush (stream);
info.last_update = steady_clock::now ();
+ info.pos++;
}
- info.state = progress_update::SPIN;
- }
- else if (next_state == progress_update::BAR)
- {
- int i, max;
- int width = chars_per_line - 3;
- max = width * howmuch;
-
- if (info.state == progress_update::SPIN
- || info.state == progress_update::PERCENT)
- {
- /* Ensure the progress bar prints on its own line so that
- progress updates don't overwrite NAME. */
- fprintf_unfiltered (stream, "\r%s\n", info.name.c_str ());
- gdb_flush (stream);
- }
-
- fprintf_unfiltered (stream, "\r[");
-
- for (i = 0; i < width; ++i)
- fprintf_unfiltered (stream, i < max ? "#" : " ");
- fprintf_unfiltered (stream, "]");
- gdb_flush (stream);
- info.state = progress_update::BAR;
}
-
+*/
return;
}
|| chars_per_line > MAX_CHARS_PER_LINE)
chars_per_line = MAX_CHARS_PER_LINE;
- int i;
int width = chars_per_line;
fprintf_unfiltered (stream, "\r");
- for (i = 0; i < width; ++i)
+ for (int i = 0; i < width; ++i)
fprintf_unfiltered (stream, " ");
fprintf_unfiltered (stream, "\r");
gdb_flush (stream);
}
-/* Set NAME as the new description of the most recent progress update. */
-
-void
-cli_ui_out::update_progress_name (const std::string &name)
-{
- struct ui_file *stream = m_streams.back ();
- cli_progress_info &info = m_progress_info.back ();
- info.name = name;
-
- if (stream->isatty ())
- clear_current_line ();
-}
-
-/* Get the current state of the most recent progress update. */
-
-cli_ui_out::progress_update::state
-cli_ui_out::get_progress_state ()
-{
- cli_progress_info &info = m_progress_info.back ();
- return info.state;
-}
-
-
/* Remove the most recent progress update from the stack and
overwrite the current line with whitespace. */
virtual void do_flush () override;
virtual void do_redirect (struct ui_file *outstream) override;
- virtual void do_progress_start (const std::string &, bool) override;
- virtual void do_progress_notify (double, progress_update::state) override;
+ virtual void do_progress_start () override;
+ virtual void do_progress_notify (const std::string &, double) override;
virtual void do_progress_end () override;
- virtual void update_progress_name (const std::string &) override;
- virtual progress_update::state get_progress_state () override;
bool suppress_output ()
{ return m_suppress_output; }
/* The state of a recent progress update. */
struct cli_progress_info
{
+ /* Position of indicator. */
+ int pos;
/* The current state. */
progress_update::state state;
- /* The name to print. */
- std::string name;
/* Time of last spinner update. */
std::chrono::steady_clock::time_point last_update;
};
const char * const desc;
std::string & fname;
- gdb::optional<ui_out::progress_update> progress;
+ ui_out::progress_update progress;
};
/* Deleter for a debuginfod_client. */
*unit = "KB";
}
-/* Ensure the progress message can fit on a single line. Otherwise
- garbled output is possible with \r.
-
- An example of possible truncations, starting with the original message:
- "Downloading XX MB separate debug info for /aa/bb/cc/dd/ee"
- "Downloading XX MB separate debug info for /aa/bb/.../ee"
- "Downloading XX MB separate debug info for ee"
- "Downloading XX MB separate debug info"
- "Downloading XX MB"
- "Downloading"
- */
-
-static std::string
-build_message (std::string size, std::string unit, std::string desc, std::string fname)
-{
- int width = get_chars_per_line ();
- std::stringstream message;
-
- message << "Downloading";
- /* Leave room for spinner and percent indicator. */
- int message_size = message.str ().length () + 6;
-
- if (!size.empty () && !unit.empty ())
- {
- message_size += size.size () + unit.size () + 2;
- if (message_size > width)
- return message.str ();
-
- /* "Downloading XX MB" */
- message << " " << size << " " << unit;
- }
-
- /* If FNAME does not fit then message will end with DESC_END.
- In case DESC_END is "separate debug info for", remove " for". */
- std::string desc_end = desc;
- if (desc.substr (desc.size () - 4) == " for")
- desc_end = desc.substr (0, desc.size () - 4);
-
- if (message_size + desc_end.size () + 1 > width)
- return message.str ();
-
- string_file styled_fname (current_uiout->can_emit_style_escape ());
- if (message_size + desc.size () + fname.size () + 2 <= width)
- {
- /* Truncation is not necessary. Return untruncated message.
- "Downloading XX MB separate debug info for /usr/libxyz.so" */
- fprintf_styled (&styled_fname, file_name_style.style (), "%s",
- fname.c_str ());
-
- message << " " << desc << " " << styled_fname.c_str ();
- return message.str ();
- }
-
- while (fname.back () == '/')
- fname.pop_back ();
-
- /* Find path separators for the first, second and final components.
- If FNAME does not have path separators and it does not fit in the
- available space, do not include it in message. */
- size_t sep1 = fname.find ('/');
- if (sep1 == std::string::npos)
- {
- message << " " << desc_end;
- return message.str ();
- }
-
- size_t sep2 = fname.find ('/', sep1 + 1);
- size_t sep3;
- if (sep2 == std::string::npos)
- sep3 = std::string::npos;
- else
- sep3 = fname.find ('/', sep2 + 1);
- size_t seplast = fname.find_last_of ('/');
-
- /* If the first, second, and final path components are distinct, try to
- truncate FNAME so that it fits in the available space. Preserve the
- first, second and final path components. For example,
- "/aa/bb/cc/dd/ee" becomes "/aa/bb/.../ee" and
- "../aa/bb/cc/dd/" becomes "../aa/.../ee" */
- std::stringstream trunc;
- if (sep2 != sep3 && sep2 != seplast && sep2 != std::string::npos)
- {
- std::stringstream fnametmp;
-
- if (sep1 == 0 && sep3 != seplast && sep3 != std::string::npos)
- fnametmp << fname.substr (0, sep3 + 1)
- << "..." << fname.substr (seplast);
- else if (sep1 != 0)
- fnametmp << fname.substr (0, sep2 + 1)
- << "..." << fname.substr (seplast);
-
- if (!fnametmp.str ().empty ())
- {
- trunc << " " << desc << " ";
- if (message_size + trunc.str ().size () + fnametmp.str ().size () <= width)
- {
- fprintf_styled (&styled_fname, file_name_style.style (), "%s",
- fnametmp.str ().c_str ());
- message << trunc.str () << styled_fname.c_str ();
- return message.str ();
- }
- }
- }
-
- /* The first, second and final components are not distinct or
- "/aa/bb/.../ee" does not fit. Try "ee" instead. */
- trunc.str ("");
- trunc << " " << desc << " ";
- fname = fname.substr (seplast + 1);
- if (message_size + trunc.str ().size () + fname.size () <= width)
- {
- fprintf_styled (&styled_fname, file_name_style.style (), "%s",
- fname.c_str ());
- message << trunc.str () << styled_fname.c_str ();
- return message.str ();
- }
-
- /* We aren't able to fit anything from FNAME. End message with DESC_END
- since we already confirmed it will fit. */
- message << " " << desc_end;
- return message.str ();
-}
-
-
static int
progressfn (debuginfod_client *c, long cur, long total)
{
user_data *data = static_cast<user_data *> (debuginfod_get_user_data (c));
gdb_assert (data != nullptr);
+ string_file styled_fname (current_uiout->can_emit_style_escape ());
+ fprintf_styled (&styled_fname, file_name_style.style (), "%s",
+ data->fname.c_str ());
+
if (check_quit_flag ())
{
- if (data->progress.has_value ())
- data->progress.reset ();
-
- string_file styled_fname (current_uiout->can_emit_style_escape ());
- fprintf_styled (&styled_fname, file_name_style.style (), "%s",
- data->fname.c_str ());
+ current_uiout->do_progress_end (); ///?
printf_filtered ("Cancelled download of %s %s\n",
data->desc, styled_fname.c_str ());
return 1;
}
- if (debuginfod_verbose == 0
- || (data->progress.has_value ()
- && data->progress->get_state () == ui_out::progress_update::WORKING))
+ if (debuginfod_verbose == 0)
return 0;
/* Print progress update. Include the transfer size if available. */
if (total > 0)
{
/* Transfer size is known. */
- double percent = (double)cur / (double)total;
+ double howmuch = (double) cur / (double) total;
- if (percent >= 0.0 && percent <= 1.0)
+ if (howmuch >= 0.0 && howmuch <= 1.0)
{
- if (!data->progress.has_value ()
- || data->progress->get_state ()
- != ui_out::progress_update::PERCENT)
- {
- double size = (double)total;
- const char *unit = "";
-
- get_size_and_unit (&size, &unit);
- std::string fsize = string_printf ("%.2f", size);
- std::string message = build_message (fsize, unit, data->desc,
- data->fname);
- if (!data->progress.has_value ())
- data->progress.emplace (current_uiout, message, 1);
- else
- data->progress->update_name (message);
- }
-
- /* Ensure PERCENT doesn't require three digits to display. */
- if (percent > 0.99 && percent <= 1.0)
- percent = .99;
- current_uiout->update_progress_percent (percent);
- return 0;
- }
- }
-
- if (!data->progress.has_value ()
- || data->progress->get_state () != ui_out::progress_update::SPIN)
- {
- std::string message = build_message ("", "", data->desc, data->fname);
+ double size = (double) total;
+ const char *unit = "";
- if (!data->progress.has_value ())
- data->progress.emplace (current_uiout, message, 1);
- else
- data->progress->update_name (message);
+ get_size_and_unit (&size, &unit);
+ std::string msg = string_printf ("Downloading %0.2f %s %s %s\n",
+ size, unit, data->desc,
+ styled_fname.c_str ());
+ current_uiout->update_progress (msg, howmuch);
+ return 0;
+ }
}
- current_uiout->update_progress_spin ();
+ std::string msg = string_printf ("Downloading %s %s\n",
+ data->desc, styled_fname.c_str ());
+ current_uiout->update_progress (msg, -1);
return 0;
}
print_outcome (user_data &data, int fd)
{
/* Clears the current line of progress output. */
- if (data.progress.has_value ())
- data.progress.reset ();
+ current_uiout->do_progress_end ();
string_file styled_fname (current_uiout->can_emit_style_escape ());
fprintf_styled (&styled_fname, file_name_style.style (), "%s",
return (string_file *) m_streams.back ();
}
-/* Indicate that a task described by NAME is in progress:
-
- - SHOULD_PRINT == true:
- <NAME
- >
- - SHOULD_PRINT == false:
- <>
-*/
-
void
-mi_ui_out::do_progress_start (const std::string &name, bool should_print)
+mi_ui_out::do_progress_start ()
{
- struct ui_file *stream = gdb_stdout;
mi_progress_info info;
- if (should_print)
- {
- fprintf_unfiltered (stream, "%s\n", name.c_str ());
- gdb_flush (stream);
- }
-
- info.state = progress_update::WORKING;
+ info.state = progress_update::START;
m_progress_info.push_back (std::move (info));
}
-/* Get the state of the most recent progress update. */
+/* Indicate that a task described by NAME is in progress. */
-mi_ui_out::progress_update::state
-mi_ui_out::get_progress_state ()
+void
+mi_ui_out::do_progress_notify (const std::string &msg, double howmuch)
{
- mi_progress_info &info = m_progress_info.back ();
- return info.state;
+ mi_progress_info &info (m_progress_info.back ());
+
+ if (info.state == progress_update::START)
+ {
+ struct ui_file *stream = gdb_stdout;
+ fprintf_unfiltered (stream, "%s\n", msg.c_str ());
+ info.state = progress_update::WORKING;
+ }
}
/* Clear the buffer. */
virtual bool do_is_mi_like_p () const override
{ return true; }
- virtual void do_progress_start (const std::string &, bool) override;
- virtual progress_update::state get_progress_state () override;
-
- virtual void do_progress_notify (double, progress_update::state) override
- {
- }
+ virtual void do_progress_start () override;
+ virtual void do_progress_notify (const std::string &, double) override;
virtual void do_progress_end () override
{
}
- virtual void update_progress_name (const std::string &) override
- {
- }
-
private:
void field_separator ();
WORKING,
/* Progress bar printing has already started. */
BAR,
- /* Spinner printing has already started. */
- SPIN,
- /* Percent printing has already started. */
- PERCENT,
/* Printing should not be done. */
NO_PRINT
};
/* SHOULD_PRINT indicates whether something should be printed for a tty. */
- progress_update (struct ui_out *uiout, const std::string &name,
- bool should_print)
- : m_uiout (uiout)
+ progress_update ()
{
- m_uiout->do_progress_start (name, should_print);
+ m_uiout = current_uiout;
+ m_uiout->do_progress_start ();
}
~progress_update ()
{
- m_uiout->do_progress_end ();
- }
-
- void update_name (std::string &name)
- {
- m_uiout->update_progress_name (name);
- }
- state get_state ()
- {
- return m_uiout->get_progress_state ();
}
progress_update (const progress_update &) = delete;
/* Emit some progress corresponding to the most recently created
progress_update object. */
- void update_progress_bar (double howmuch)
- {
- do_progress_notify (howmuch, progress_update::BAR);
- }
-
- void update_progress_percent (double howmuch)
+ void update_progress (std::string &msg, double howmuch)
{
- do_progress_notify (howmuch, progress_update::PERCENT);
+ do_progress_notify (msg, howmuch);
}
- void update_progress_spin ()
- {
- do_progress_notify (0, progress_update::SPIN);
- }
+ virtual void do_progress_end () = 0;
protected:
virtual void do_flush () = 0;
virtual void do_redirect (struct ui_file *outstream) = 0;
- virtual void do_progress_start (const std::string &, bool) = 0;
- virtual void do_progress_notify (double, progress_update::state) = 0;
- virtual void do_progress_end () = 0;
- virtual void update_progress_name (const std::string &) = 0;
- virtual progress_update::state get_progress_state () = 0;
+ virtual void do_progress_start () = 0;
+ virtual void do_progress_notify (const std::string &, double) = 0;
/* Set as not MI-like by default. It is overridden in subclasses if
necessary. */