[PATCH v3] gdb: Improve debuginfod progress updates
Aaron Merey
amerey@redhat.com
Wed Feb 9 02:25:48 GMT 2022
v2 can be found here:
https://sourceware.org/pipermail/gdb-patches/2022-January/185432.html
v3 adds truncation of progress update messages when printed to a
terminal that is not wide enough to fit the message on one line.
The new function build_message will try to fit as much of the
untruncated message as possible. For example if the original
message is:
Downloading XX MB separate debug info for /aa/bb/cc/dd/ee
Then possible truncated messages are:
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
---
gdb/cli-out.c | 170 ++++++++++++++++++------
gdb/cli-out.h | 31 ++---
gdb/debuginfod-support.c | 276 ++++++++++++++++++++++++++++++++++-----
gdb/mi/mi-out.c | 34 +++++
gdb/mi/mi-out.h | 20 ++-
gdb/ui-out.h | 62 +++++++--
6 files changed, 486 insertions(+), 107 deletions(-)
diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index 5ff645b4a83..3523b074e75 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -265,10 +265,17 @@ cli_ui_out::do_redirect (ui_file *outstream)
m_streams.pop_back ();
}
-/* The cli_ui_out::do_progress_* functions result in the following:
- - printed for tty, SHOULD_PRINT == true:
- <NAME
- [##### ]\r>
+/* 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:
@@ -280,15 +287,14 @@ void
cli_ui_out::do_progress_start (const std::string &name, bool should_print)
{
struct ui_file *stream = m_streams.back ();
- cli_progress_info meter;
+ cli_progress_info info;
- meter.last_value = 0;
- meter.name = name;
+ info.name = name;
if (!stream->isatty ())
{
- fprintf_unfiltered (stream, "%s...", meter.name.c_str ());
+ fprintf_unfiltered (stream, "%s\n", info.name.c_str ());
gdb_flush (stream);
- meter.printing = WORKING;
+ info.state = progress_update::WORKING;
}
else
{
@@ -296,74 +302,158 @@ cli_ui_out::do_progress_start (const std::string &name, bool should_print)
of progress. This makes it so a second progress message can
be started before the first one has been notified, without
messy output. */
- meter.printing = should_print ? START : NO_PRINT;
+ info.state = should_print ? progress_update::START
+ : progress_update::NO_PRINT;
}
- m_meters.push_back (std::move (meter));
+ m_progress_info.push_back (std::move (info));
}
+/* Pick a reasonable limit for the progress update length. */
+#define MAX_CHARS_PER_LINE 4096
+
void
-cli_ui_out::do_progress_notify (double howmuch)
+cli_ui_out::do_progress_notify (double howmuch,
+ progress_update::state next_state)
{
struct ui_file *stream = m_streams.back ();
- cli_progress_info &meter (m_meters.back ());
+ cli_progress_info &info (m_progress_info.back ());
- if (meter.printing == NO_PRINT)
+ if (info.state == progress_update::NO_PRINT)
return;
- if (meter.printing == START)
+ 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\n", meter.name.c_str ());
+ fprintf_unfiltered (stream, "%s", info.name.c_str ());
+ if (chars_per_line <= 0)
+ fprintf_unfiltered (stream, "\n");
gdb_flush (stream);
- meter.printing = WORKING;
+ info.state = progress_update::WORKING;
}
- if (meter.printing == WORKING && howmuch >= 1.0)
+ if (chars_per_line <= 0)
+ return;
+
+ if (info.state == progress_update::WORKING && howmuch >= 1.0)
return;
if (!stream->isatty ())
return;
- int chars_per_line = get_chars_per_line ();
- if (chars_per_line > 0)
+ if (next_state == progress_update::PERCENT)
+ {
+ fprintf_unfiltered (stream, "\r(%2.0f%%) %s",
+ howmuch * 100, info.name.c_str ());
+ gdb_flush (stream);
+ info.state = progress_update::PERCENT;
+ }
+ else if (next_state == progress_update::SPIN)
+ {
+ using namespace std::chrono;
+ seconds diff = duration_cast<seconds>
+ (steady_clock::now () - info.last_update);
+
+ /* Advance the spinner no faster than 1 tick per second. */
+ if (diff.count () >= 1.0)
+ {
+ static int spin = 0;
+
+ fprintf_unfiltered (stream, "\r%c %s", "-\\|/"[spin++ % 4],
+ info.name.c_str ());
+ gdb_flush (stream);
+ info.last_update = steady_clock::now ();
+ }
+ 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);
- meter.printing = PROGRESS;
+ info.state = progress_update::BAR;
}
+
+ return;
}
+/* Clear the current line of the most recent progress update. Overwrites
+ the current line with whitespace. */
+
void
-cli_ui_out::do_progress_end ()
+cli_ui_out::clear_current_line ()
{
struct ui_file *stream = m_streams.back ();
- cli_progress_info &meter = m_meters.back ();
+ int chars_per_line = get_chars_per_line ();
- if (!stream->isatty ())
- {
- fprintf_unfiltered (stream, "\n");
- gdb_flush (stream);
- }
- else if (meter.printing == PROGRESS)
- {
- int i;
- int width = get_chars_per_line () - 3;
+ if (chars_per_line <= 0
+ || chars_per_line > MAX_CHARS_PER_LINE)
+ chars_per_line = MAX_CHARS_PER_LINE;
- fprintf_unfiltered (stream, "\r");
- for (i = 0; i < width + 2; ++i)
- fprintf_unfiltered (stream, " ");
- fprintf_unfiltered (stream, "\r");
- gdb_flush (stream);
- }
+ int i;
+ int width = chars_per_line;
+
+ fprintf_unfiltered (stream, "\r");
+ for (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. */
+
+void
+cli_ui_out::do_progress_end ()
+{
+ struct ui_file *stream = m_streams.back ();
+ m_progress_info.pop_back ();
- m_meters.pop_back ();
+ if (stream->isatty ())
+ clear_current_line ();
}
/* local functions */
diff --git a/gdb/cli-out.h b/gdb/cli-out.h
index 4af5524495a..2afd5b32706 100644
--- a/gdb/cli-out.h
+++ b/gdb/cli-out.h
@@ -21,6 +21,7 @@
#define CLI_OUT_H
#include "ui-out.h"
+#include <chrono>
#include <vector>
class cli_ui_out : public ui_out
@@ -72,8 +73,10 @@ class cli_ui_out : public ui_out
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) override;
+ virtual void do_progress_notify (double, progress_update::state) 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; }
@@ -85,32 +88,20 @@ class cli_ui_out : public ui_out
std::vector<ui_file *> m_streams;
bool m_suppress_output;
- /* Represents the printing state of a progress meter. */
- enum meter_state
- {
- /* Printing will start with the next output. */
- START,
- /* Printing has already started. */
- WORKING,
- /* Progress printing has already started. */
- PROGRESS,
- /* Printing should not be done. */
- NO_PRINT
- };
-
- /* The state of a recent progress meter. */
+ /* The state of a recent progress update. */
struct cli_progress_info
{
/* The current state. */
- enum meter_state printing;
+ progress_update::state state;
/* The name to print. */
std::string name;
- /* The last notification value. */
- double last_value;
+ /* Time of last spinner update. */
+ std::chrono::steady_clock::time_point last_update;
};
- /* Stack of progress meters. */
- std::vector<cli_progress_info> m_meters;
+ /* Stack of progress info. */
+ std::vector<cli_progress_info> m_progress_info;
+ void clear_current_line ();
};
extern cli_ui_out *cli_out_new (struct ui_file *stream);
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 56d8e7781c5..34a72618663 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -23,7 +23,9 @@
#include "gdbsupport/gdb_optional.h"
#include "cli/cli-cmds.h"
#include "cli/cli-style.h"
+#include "cli-out.h"
#include "target.h"
+#include <sstream>
/* Set/show debuginfod commands. */
static cmd_list_element *set_debuginfod_prefix_list;
@@ -76,13 +78,13 @@ debuginfod_debuginfo_query (const unsigned char *build_id,
struct user_data
{
- user_data (const char *desc, const char *fname)
+ user_data (const char *desc, std::string &fname)
: desc (desc), fname (fname)
{ }
const char * const desc;
- const char * const fname;
- gdb::optional<ui_out::progress_meter> meter;
+ std::string & fname;
+ gdb::optional<ui_out::progress_update> progress;
};
/* Deleter for a debuginfod_client. */
@@ -98,6 +100,149 @@ struct debuginfod_client_deleter
using debuginfod_client_up
= std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
+/* Convert SIZE into a unit suitable for use with progress updates.
+ SIZE should in given in bytes and will be converted into KB or MB.
+ UNIT will be set to "KB" or "MB" accordingly. */
+
+static void
+get_size_and_unit (double *size, const char **unit)
+{
+ *size /= 1024;
+
+ /* If size is greater than 0.01 MB then set unit to MB. */
+ if (*size > 10.24)
+ {
+ *size /= 1024;
+ *unit = "MB";
+ }
+ else
+ *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)
{
@@ -106,31 +251,68 @@ progressfn (debuginfod_client *c, long cur, long total)
if (check_quit_flag ())
{
- printf_filtered ("Cancelling download of %s %ps...\n",
- data->desc,
- styled_string (file_name_style.style (), data->fname));
+ 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 ());
+
+ printf_filtered ("Cancelled download of %s %s\n",
+ data->desc, styled_fname.c_str ());
return 1;
}
- if (total == 0)
+ if (debuginfod_verbose == 0
+ || (data->progress.has_value ()
+ && data->progress->get_state () == ui_out::progress_update::WORKING))
return 0;
- if (!data->meter.has_value ())
+ /* Print progress update. Include the transfer size if available. */
+ if (total > 0)
{
- float size_in_mb = 1.0f * total / (1024 * 1024);
- string_file styled_filename (current_uiout->can_emit_style_escape ());
- fprintf_styled (&styled_filename,
- file_name_style.style (),
- "%s",
- data->fname);
- std::string message
- = string_printf ("Downloading %.2f MB %s %s", size_in_mb, data->desc,
- styled_filename.c_str());
- data->meter.emplace (current_uiout, message, 1);
+ /* Transfer size is known. */
+ double percent = (double)cur / (double)total;
+
+ if (percent >= 0.0 && percent <= 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;
+ }
}
- current_uiout->progress ((double)cur / (double)total);
+ if (!data->progress.has_value ()
+ || data->progress->get_state () != ui_out::progress_update::SPIN)
+ {
+ std::string message = build_message ("", "", data->desc, data->fname);
+
+ if (!data->progress.has_value ())
+ data->progress.emplace (current_uiout, message, 1);
+ else
+ data->progress->update_name (message);
+ }
+ current_uiout->update_progress_spin ();
return 0;
}
@@ -186,6 +368,44 @@ debuginfod_is_enabled ()
return true;
}
+/* Print the result of the most recent attempted download. */
+
+static void
+print_outcome (user_data &data, int fd)
+{
+ /* Clears the current line of progress output. */
+ 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 ());
+
+ if (debuginfod_verbose > 1 && fd >= 0)
+ {
+ struct stat s;
+
+ if (fstat (fd, &s) == 0)
+ {
+ double size = (double)s.st_size;
+ const char *unit = "";
+
+ get_size_and_unit (&size, &unit);
+ printf_filtered (_("Retrieved %.02f %s %s %s\n"), size, unit,
+ data.desc, styled_fname.c_str ());
+ }
+ else
+ warning (_("Retrieved %s %s but size cannot be read: %s\n"),
+ data.desc, styled_fname.c_str (),
+ safe_strerror (errno));
+ }
+ else if (debuginfod_verbose > 0 && fd < 0 && fd != -ENOENT)
+ printf_filtered (_("Download failed: %s. " \
+ "Continuing without %s %s.\n"),
+ safe_strerror (-fd), data.desc,
+ styled_fname.c_str ());
+}
+
/* See debuginfod-support.h */
scoped_fd
@@ -202,7 +422,8 @@ debuginfod_source_query (const unsigned char *build_id,
if (c == nullptr)
return scoped_fd (-ENOMEM);
- user_data data ("source file", srcpath);
+ std::string fname = srcpath;
+ user_data data ("source file", fname);
debuginfod_set_user_data (c, &data);
gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
@@ -218,11 +439,7 @@ debuginfod_source_query (const unsigned char *build_id,
srcpath,
nullptr));
debuginfod_set_user_data (c, nullptr);
-
- if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
- printf_filtered (_("Download failed: %s. Continuing without source file %ps.\n"),
- safe_strerror (-fd.get ()),
- styled_string (file_name_style.style (), srcpath));
+ print_outcome (data, fd.get ());
if (fd.get () >= 0)
*destname = make_unique_xstrdup (srcpath);
@@ -247,7 +464,8 @@ debuginfod_debuginfo_query (const unsigned char *build_id,
return scoped_fd (-ENOMEM);
char *dname = nullptr;
- user_data data ("separate debug info for", filename);
+ std::string fname = filename;
+ user_data data ("separate debug info for", fname);
debuginfod_set_user_data (c, &data);
gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
@@ -260,11 +478,7 @@ debuginfod_debuginfo_query (const unsigned char *build_id,
scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
&dname));
debuginfod_set_user_data (c, nullptr);
-
- if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
- printf_filtered (_("Download failed: %s. Continuing without debug info for %ps.\n"),
- safe_strerror (-fd.get ()),
- styled_string (file_name_style.style (), filename));
+ print_outcome (data, fd.get ());
if (fd.get () >= 0)
destname->reset (dname);
diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
index 20c6f0f9194..a7f697b4f28 100644
--- a/gdb/mi/mi-out.c
+++ b/gdb/mi/mi-out.c
@@ -258,6 +258,40 @@ mi_ui_out::main_stream ()
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)
+{
+ 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;
+ m_progress_info.push_back (std::move (info));
+}
+
+/* Get the state of the most recent progress update. */
+
+mi_ui_out::progress_update::state
+mi_ui_out::get_progress_state ()
+{
+ mi_progress_info &info = m_progress_info.back ();
+ return info.state;
+}
+
/* Clear the buffer. */
void
diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h
index 1b7fa96a182..d915b35f633 100644
--- a/gdb/mi/mi-out.h
+++ b/gdb/mi/mi-out.h
@@ -25,7 +25,6 @@
struct ui_out;
struct ui_file;
-
class mi_ui_out : public ui_out
{
public:
@@ -83,15 +82,18 @@ class mi_ui_out : public ui_out
virtual bool do_is_mi_like_p () const override
{ return true; }
- virtual void do_progress_start (const std::string &, bool) override
+ 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_notify (double) override
+ virtual void do_progress_end () override
{
}
- virtual void do_progress_end () override
+ virtual void update_progress_name (const std::string &) override
{
}
@@ -101,6 +103,16 @@ class mi_ui_out : public ui_out
void open (const char *name, ui_out_type type);
void close (ui_out_type type);
+ /* The state of a recent progress_update. */
+ struct mi_progress_info
+ {
+ /* The current state. */
+ progress_update::state state;
+ };
+
+ /* Stack of progress info. */
+ std::vector<mi_progress_info> m_progress_info;
+
/* Convenience method that returns the MI out's string stream cast
to its appropriate type. Assumes/asserts that output was not
redirected. */
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index 05312150c21..4c490c11b82 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -280,26 +280,52 @@ class ui_out
escapes. */
virtual bool can_emit_style_escape () const = 0;
- /* An object that starts and finishes a progress meter. */
- class progress_meter
+ /* An object that starts and finishes displaying progress updates. */
+ class progress_update
{
public:
+ /* Represents the printing state of a progress update. */
+ enum state
+ {
+ /* Printing will start with the next update. */
+ START,
+ /* Printing has already started. */
+ 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_meter (struct ui_out *uiout, const std::string &name,
- bool should_print)
+ progress_update (struct ui_out *uiout, const std::string &name,
+ bool should_print)
: m_uiout (uiout)
{
m_uiout->do_progress_start (name, should_print);
}
- ~progress_meter ()
+ ~progress_update ()
{
- m_uiout->do_progress_notify (1.0);
m_uiout->do_progress_end ();
}
- progress_meter (const progress_meter &) = delete;
- progress_meter &operator= (const progress_meter &) = delete;
+ 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;
+ progress_update &operator= (const progress_update &) = delete;
private:
@@ -307,10 +333,20 @@ class ui_out
};
/* Emit some progress corresponding to the most recently created
- progress meter. HOWMUCH may range from 0.0 to 1.0. */
- void progress (double howmuch)
+ progress_update object. */
+ void update_progress_bar (double howmuch)
+ {
+ do_progress_notify (howmuch, progress_update::BAR);
+ }
+
+ void update_progress_percent (double howmuch)
+ {
+ do_progress_notify (howmuch, progress_update::PERCENT);
+ }
+
+ void update_progress_spin ()
{
- do_progress_notify (howmuch);
+ do_progress_notify (0, progress_update::SPIN);
}
protected:
@@ -348,8 +384,10 @@ class ui_out
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) = 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;
/* Set as not MI-like by default. It is overridden in subclasses if
necessary. */
--
2.34.1
More information about the Gdb-patches
mailing list