From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 44CA23858D1E for ; Wed, 9 Feb 2022 02:26:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 44CA23858D1E Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-447-xBL6yloBOXasHUmJ9umpVw-1; Tue, 08 Feb 2022 21:26:12 -0500 X-MC-Unique: xBL6yloBOXasHUmJ9umpVw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6870A100C673; Wed, 9 Feb 2022 02:26:11 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.19.149]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0C31212E21; Wed, 9 Feb 2022 02:26:07 +0000 (UTC) From: Aaron Merey To: gdb-patches@sourceware.org Cc: patrick@monnerat.net, tom@tromey.com, Aaron Merey Subject: [PATCH v3] gdb: Improve debuginfod progress updates Date: Tue, 8 Feb 2022 21:25:48 -0500 Message-Id: <20220209022548.343785-1-amerey@redhat.com> In-Reply-To: <20220126005817.56356-1-amerey@redhat.com> References: <20220126005817.56356-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-13.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 09 Feb 2022 02:26:20 -0000 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: - +/* 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: + - 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 + (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 #include 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 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 m_meters; + /* Stack of progress info. */ + std::vector 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 /* 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 meter; + std::string & fname; + gdb::optional progress; }; /* Deleter for a debuginfod_client. */ @@ -98,6 +100,149 @@ struct debuginfod_client_deleter using debuginfod_client_up = std::unique_ptr; +/* 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 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 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: + + - 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 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