diff --git a/gdb/cli-out.c b/gdb/cli-out.c index 5ff645b4a83..8cfa8f24c42 100644 --- a/gdb/cli-out.c +++ b/gdb/cli-out.c @@ -280,15 +280,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...", info.name.c_str ()); gdb_flush (stream); - meter.printing = WORKING; + info.state = progress_report::WORKING; } else { @@ -296,74 +295,120 @@ 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_report::START + : progress_report::NO_PRINT; } - m_meters.push_back (std::move (meter)); + m_progress_info.push_back (std::move (info)); } void cli_ui_out::do_progress_notify (double howmuch) { 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_report::NO_PRINT) return; - if (meter.printing == START) + int chars_per_line = get_chars_per_line (); + if (info.state == progress_report::START && chars_per_line <= 0) { - fprintf_unfiltered (stream, "%s\n", meter.name.c_str ()); + fprintf_unfiltered (stream, "%s\n", info.name.c_str ()); gdb_flush (stream); - meter.printing = WORKING; + info.state = progress_report::WORKING; } - if (meter.printing == WORKING && howmuch >= 1.0) + if (info.state == progress_report::WORKING && howmuch >= 1.0) return; if (!stream->isatty ()) return; - int chars_per_line = get_chars_per_line (); - if (chars_per_line > 0) + if (chars_per_line <= 0) + return; + + if (howmuch <= 0.0) { + using namespace std::chrono; + seconds diff = duration_cast + (steady_clock::now () - info.last_update); + + if (diff.count () >= 1.0) + { + static int spin = 0; + + fprintf_unfiltered (stream, "\r%s %c\b", info.name.c_str (), + "-\\|/"[spin++ % 4]); + gdb_flush (stream); + info.last_update = steady_clock::now (); + } + info.state = progress_report::SPIN; + } + else + { + /* Pick a reasonable limit for the progress bar length. */ + if (chars_per_line > 3840) + chars_per_line = 3840; + int i, max; int width = chars_per_line - 3; - max = width * howmuch; + + if (info.state == progress_report::SPIN) + { + /* Ensure the progress bar prints on its own line so that + progress updates don't erase 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_report::BAR; } } +void +cli_ui_out::update_progress_name (const std::string &name) +{ + cli_progress_info &info = m_progress_info.back (); + info.name = name; +} + +cli_ui_out::progress_report::state +cli_ui_out::get_progress_state () +{ + cli_progress_info &info = m_progress_info.back (); + return info.state; +} + void cli_ui_out::do_progress_end () { struct ui_file *stream = m_streams.back (); - cli_progress_info &meter = m_meters.back (); + cli_progress_info &info = m_progress_info.back (); - if (!stream->isatty ()) - { - fprintf_unfiltered (stream, "\n"); - gdb_flush (stream); - } - else if (meter.printing == PROGRESS) + if (!stream->isatty () || info.state == progress_report::SPIN) + fprintf_unfiltered (stream, " \n"); + else if (info.state == progress_report::BAR) { int i; int width = get_chars_per_line () - 3; + this->do_progress_notify (1.0); fprintf_unfiltered (stream, "\r"); for (i = 0; i < width + 2; ++i) fprintf_unfiltered (stream, " "); fprintf_unfiltered (stream, "\r"); - gdb_flush (stream); } - m_meters.pop_back (); + gdb_flush (stream); + m_progress_info.pop_back (); } /* local functions */ diff --git a/gdb/cli-out.h b/gdb/cli-out.h index 4af5524495a..1947b5b3354 100644 --- a/gdb/cli-out.h +++ b/gdb/cli-out.h @@ -21,6 +21,7 @@ #define CLI_OUT_H #include "ui-out.h" +#include #include class cli_ui_out : public ui_out @@ -74,6 +75,8 @@ protected: virtual void do_progress_start (const std::string &, bool) override; virtual void do_progress_notify (double) override; virtual void do_progress_end () override; + virtual void update_progress_name (const std::string &) override; + virtual progress_report::state get_progress_state () override; bool suppress_output () { return m_suppress_output; } @@ -85,32 +88,19 @@ private: std::vector 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 report. */ struct cli_progress_info { /* The current state. */ - enum meter_state printing; + progress_report::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 m_meters; + /* Stack of progress info. */ + std::vector m_progress_info; }; extern cli_ui_out *cli_out_new (struct ui_file *stream); diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c index c91f45675c3..a511e818628 100644 --- a/gdb/debuginfod-support.c +++ b/gdb/debuginfod-support.c @@ -23,6 +23,7 @@ #include "gdbsupport/gdb_optional.h" #include "cli/cli-cmds.h" #include "cli/cli-style.h" +#include "cli-out.h" #include "target.h" /* Set/show debuginfod commands. */ @@ -82,8 +83,7 @@ struct user_data const char * const desc; const char * const fname; - gdb::optional meter; - bool printed_spin = false; + gdb::optional report; }; /* Deleter for a debuginfod_client. */ @@ -102,12 +102,14 @@ using debuginfod_client_up static int progressfn (debuginfod_client *c, long cur, long total) { - static int spin = 0; user_data *data = static_cast (debuginfod_get_user_data (c)); gdb_assert (data != nullptr); if (check_quit_flag ()) { + if (data->report.has_value ()) + data->report.reset (); + printf_filtered ("Cancelling download of %s %ps...\n", data->desc, styled_string (file_name_style.style (), data->fname)); @@ -122,41 +124,44 @@ progressfn (debuginfod_client *c, long cur, long total) if (total > 0) { - if (!data->meter.has_value ()) + /* Transfer size is known, so print it along with the rest of the + progress update. */ + if (!data->report.has_value () + || data->report->get_state () == ui_out::progress_report::SPIN) { double size = 1.0f * total / 1024; std::string unit = "KB"; - /* Switch to MB if size greater than 0.01 MB. */ + /* Switch unit to MB if size is greater than 0.01 MB. */ if (size > 10.24) { size /= 1024; unit = "MB"; } - /* Overwrite an existing progress update line with no download - size information. */ - if (data->printed_spin) - { - current_uiout->message ("\r"); - data->printed_spin = false; - } - std::string message = string_printf ("Downloading %.2f %s %s %s", size, unit.c_str (), data->desc, styled_filename.c_str ()); - data->meter.emplace (current_uiout, message, 1); + + if (!data->report.has_value ()) + data->report.emplace (current_uiout, message, 1); + else + data->report->update_name (message); } - current_uiout->progress ((double)cur / (double)total); + current_uiout->update_progress ((double)cur / (double)total); } else { - current_uiout->message (_("\rDownloading %s %s %c"), data->desc, - styled_filename.c_str (), "-/|\\"[spin++ % 4]); - current_uiout->flush (); - data->printed_spin = true; + if (!data->report.has_value ()) + { + std::string message = string_printf ("Downloading %s %s", data->desc, + styled_filename.c_str ()); + data->report.emplace (current_uiout, message, 1); + } + + current_uiout->update_progress (-1); } return 0; @@ -230,6 +235,7 @@ debuginfod_source_query (const unsigned char *build_id, if (c == nullptr) return scoped_fd (-ENOMEM); + char *dname = nullptr; user_data data ("source file", srcpath); debuginfod_set_user_data (c, &data); @@ -244,9 +250,7 @@ debuginfod_source_query (const unsigned char *build_id, build_id, build_id_len, srcpath, - nullptr)); - if (data.printed_spin) - current_uiout->message ("\b \n"); + &dname)); debuginfod_set_user_data (c, nullptr); @@ -256,7 +260,7 @@ debuginfod_source_query (const unsigned char *build_id, styled_string (file_name_style.style (), srcpath)); if (fd.get () >= 0) - *destname = make_unique_xstrdup (srcpath); + destname->reset (dname); return fd; } @@ -290,9 +294,6 @@ debuginfod_debuginfo_query (const unsigned char *build_id, scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len, &dname)); - if (data.printed_spin) - current_uiout->message ("\b \n"); - debuginfod_set_user_data (c, nullptr); if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT) diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c index 20c6f0f9194..e9d8a18f79e 100644 --- a/gdb/mi/mi-out.c +++ b/gdb/mi/mi-out.c @@ -258,6 +258,25 @@ mi_ui_out::main_stream () return (string_file *) m_streams.back (); } +void +mi_ui_out::do_progress_start (const std::string &name, bool should_print) +{ + struct ui_file *stream = gdb_stdout; + mi_progress_info info; + + fprintf_unfiltered (stream, "%s...\n", name.c_str ()); + gdb_flush (stream); + info.state = progress_report::WORKING; + m_progress_info.push_back (std::move (info)); +} + +mi_ui_out::progress_report::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..97e7e6d053b 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,9 +82,8 @@ protected: 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_report::state get_progress_state () override; virtual void do_progress_notify (double) override { @@ -95,12 +93,26 @@ protected: { } + virtual void update_progress_name (const std::string &) override + { + } + private: void field_separator (); void open (const char *name, ui_out_type type); void close (ui_out_type type); + /* The state of a recent progress_report. */ + struct mi_progress_info + { + /* The current state. */ + progress_report::state state; + }; + + /* Stack of progress info. */ + std::vector 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..17858b238ec 100644 --- a/gdb/ui-out.h +++ b/gdb/ui-out.h @@ -280,26 +280,50 @@ 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 progress reporting. */ + class progress_report { public: + /* Represents the printing state of a progress report. */ + enum state + { + /* Printing will start with the next output. */ + START, + /* Printing has already started. */ + WORKING, + /* Progress bar printing has already started. */ + BAR, + /* Spinner printing has already started. */ + SPIN, + /* 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_report (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_report () { - 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_report (const progress_report &) = delete; + progress_report &operator= (const progress_report &) = delete; private: @@ -307,8 +331,8 @@ 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_report object. */ + void update_progress (double howmuch) { do_progress_notify (howmuch); } @@ -350,6 +374,8 @@ class ui_out virtual void do_progress_start (const std::string &, bool) = 0; virtual void do_progress_notify (double) = 0; virtual void do_progress_end () = 0; + virtual void update_progress_name (const std::string &) = 0; + virtual progress_report::state get_progress_state () = 0; /* Set as not MI-like by default. It is overridden in subclasses if necessary. */