#include "completer.h"
#include "readline/readline.h"
#include "cli/cli-style.h"
+#include "top.h"
/* These are the CLI output functions */
m_streams.pop_back ();
}
-/* The cli_ui_out::do_progress_* functions result in the following:
- - printed for tty, SHOULD_PRINT == true:
- <NAME
- [##### ]\r>
- - printed for tty, SHOULD_PRINT == false:
- <>
+/* Initialize a progress update to be displayed with
+ cli_ui_out::do_progress_notify. */
+
+void
+cli_ui_out::do_progress_start ()
+{
+ m_progress_info.emplace_back ();
+}
+
+#define MIN_CHARS_PER_LINE 50
+#define MAX_CHARS_PER_LINE 4096
+
+/* Print a progress update. MSG is a string to be printed on the line above
+ the progress bar. TOTAL is the size of the download whose progress is
+ being displayed. UNIT should be the unit of TOTAL (ex. "K"). If HOWMUCH
+ is between 0.0 and 1.0, a progress bar is displayed indicating the percentage
+ of completion and the download size. If HOWMUCH is negative, a progress
+ indicator will tick across the screen. If the output stream is not a tty
+ then only MSG is printed.
+
+ - printed for tty, HOWMUCH between 0.0 and 1.0:
+ <MSG
+ [######### ] HOWMUCH*100% (TOTAL UNIT)\r>
+ - printed for tty, HOWMUCH < 0.0:
+ <MSG
+ [ ### ]\r>
- printed for not-a-tty:
- <NAME...
- >
+ <MSG...\n>
*/
void
-cli_ui_out::do_progress_start (const std::string &name, bool should_print)
+cli_ui_out::do_progress_notify (const std::string &msg,
+ const char *unit,
+ double howmuch, double total)
{
+ int chars_per_line = get_chars_per_line ();
struct ui_file *stream = m_streams.back ();
- cli_progress_info meter;
+ cli_progress_info &info (m_progress_info.back ());
+
+ if (chars_per_line > MAX_CHARS_PER_LINE)
+ chars_per_line = MAX_CHARS_PER_LINE;
+
+ if (info.state == progress_update::START)
+ {
+ if (stream->isatty ()
+ && current_ui->input_interactive_p ()
+ && chars_per_line >= MIN_CHARS_PER_LINE)
+ {
+ gdb_printf (stream, "%s\n", msg.c_str ());
+ info.state = progress_update::BAR;
+ }
+ else
+ {
+ gdb_printf (stream, "%s...\n", msg.c_str ());
+ info.state = progress_update::WORKING;
+ }
+ }
+
+ if (info.state != progress_update::BAR
+ || chars_per_line < MIN_CHARS_PER_LINE)
+ return;
- meter.last_value = 0;
- meter.name = name;
- if (!stream->isatty ())
+ if (total > 0 && howmuch >= 0 && howmuch <= 1.0)
{
- gdb_printf (stream, "%s...", meter.name.c_str ());
+ std::string progress = string_printf (" %3.f%% (%.2f %s)",
+ howmuch * 100, total,
+ unit);
+ int width = chars_per_line - progress.size () - 4;
+ int max = width * howmuch;
+
+ std::string display = "\r[";
+
+ for (int i = 0; i < width; ++i)
+ if (i < max)
+ display += "#";
+ else
+ display += " ";
+
+ display += "]" + progress;
+ gdb_printf (stream, "%s", display.c_str ());
gdb_flush (stream);
- meter.printing = 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. */
- meter.printing = should_print ? START : NO_PRINT;
+ using namespace std::chrono;
+ milliseconds diff = duration_cast<milliseconds>
+ (steady_clock::now () - info.last_update);
+
+ /* Advance the progress indicator at a rate of 1 tick every
+ every 0.5 seconds. */
+ if (diff.count () >= 500)
+ {
+ int width = chars_per_line - 4;
+
+ gdb_printf (stream, "\r[");
+ for (int i = 0; i < width; ++i)
+ {
+ if (i == info.pos % width
+ || i == (info.pos + 1) % width
+ || i == (info.pos + 2) % width)
+ gdb_printf (stream, "#");
+ else
+ gdb_printf (stream, " ");
+ }
+
+ gdb_printf (stream, "]");
+ gdb_flush (stream);
+ info.last_update = steady_clock::now ();
+ info.pos++;
+ }
}
- m_meters.push_back (std::move (meter));
+ return;
}
+/* Clear the current line of the most recent progress update. Overwrites
+ the current line with whitespace. */
+
void
-cli_ui_out::do_progress_notify (double howmuch)
+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 (meter.printing == NO_PRINT)
+ if (!stream->isatty ()
+ || !current_ui->input_interactive_p ()
+ || chars_per_line < MIN_CHARS_PER_LINE)
return;
- if (meter.printing == START)
- {
- gdb_printf (stream, "%s\n", meter.name.c_str ());
- gdb_flush (stream);
- meter.printing = WORKING;
- }
-
- if (meter.printing == WORKING && howmuch >= 1.0)
- return;
+ if (chars_per_line > MAX_CHARS_PER_LINE)
+ chars_per_line = MAX_CHARS_PER_LINE;
- if (!stream->isatty ())
- return;
+ gdb_printf (stream, "\r");
+ for (int i = 0; i < chars_per_line; ++i)
+ gdb_printf (stream, " ");
+ gdb_printf (stream, "\r");
- int chars_per_line = get_chars_per_line ();
- if (chars_per_line > 0)
- {
- int i, max;
- int width = chars_per_line - 3;
-
- max = width * howmuch;
- gdb_printf (stream, "\r[");
- for (i = 0; i < width; ++i)
- gdb_printf (stream, i < max ? "#" : " ");
- gdb_printf (stream, "]");
- gdb_flush (stream);
- meter.printing = PROGRESS;
- }
+ gdb_flush (stream);
}
+/* Remove the most recent progress update from the progress_info stack
+ and overwrite the current line with whitespace. */
+
void
cli_ui_out::do_progress_end ()
{
struct ui_file *stream = m_streams.back ();
- cli_progress_info &meter = m_meters.back ();
-
- if (!stream->isatty ())
- {
- gdb_printf (stream, "\n");
- gdb_flush (stream);
- }
- else if (meter.printing == PROGRESS)
- {
- int i;
- int width = get_chars_per_line () - 3;
-
- gdb_printf (stream, "\r");
- for (i = 0; i < width + 2; ++i)
- gdb_printf (stream, " ");
- gdb_printf (stream, "\r");
- gdb_flush (stream);
- }
+ m_progress_info.pop_back ();
- m_meters.pop_back ();
+ if (stream->isatty ())
+ clear_current_line ();
}
/* local functions */
#define CLI_OUT_H
#include "ui-out.h"
+#include <chrono>
#include <vector>
class cli_ui_out : public ui_out
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) override;
+ virtual void do_progress_start () override;
+ virtual void do_progress_notify (const std::string &, const char *,
+ double, double) override;
virtual void do_progress_end () override;
bool suppress_output ()
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
{
+ /* Position of the progress indicator. */
+ int pos;
/* The current state. */
- enum meter_state printing;
- /* The name to print. */
- std::string name;
- /* The last notification value. */
- double last_value;
+ progress_update::state state;
+ /* Progress indicator's time of last update. */
+ std::chrono::steady_clock::time_point last_update;
+
+ cli_progress_info ()
+ : pos (0), state (progress_update::START)
+ {}
};
- /* 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 void cli_display_match_list (char **matches, int len, int max);
#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. */
struct user_data
{
user_data (const char *desc, const char *fname)
- : desc (desc), fname (fname), has_printed (false)
+ : desc (desc), fname (fname)
{ }
const char * const desc;
const char * const fname;
- bool has_printed;
+ ui_out::progress_update progress;
};
/* Deleter for a debuginfod_client. */
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, MB, GB
+ or remain unchanged. UNIT will be set to "B", "KB", "MB" or "GB"
+ accordingly. */
+
+static const char *
+get_size_and_unit (double &size)
+{
+ if (size < 1024)
+ /* If size is less than 1 KB then set unit to B. */
+ return "B";
+
+ size /= 1024;
+ if (size < 1024)
+ /* If size is less than 1 MB then set unit to KB. */
+ return "K";
+
+ size /= 1024;
+ if (size < 1024)
+ /* If size is less than 1 GB then set unit to MB. */
+ return "M";
+
+ size /= 1024;
+ return "G";
+}
+
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);
+
if (check_quit_flag ())
{
- gdb_printf ("Cancelling download of %s %ps...\n",
- data->desc,
- styled_string (file_name_style.style (), data->fname));
+ gdb_printf ("Cancelling download of %s %s...\n",
+ data->desc, styled_fname.c_str ());
return 1;
}
- if (!data->has_printed)
+ if (debuginfod_verbose == 0)
+ return 0;
+
+ /* Print progress update. Include the transfer size if available. */
+ if (total > 0)
{
- /* Include the transfer size, if available. */
- if (total > 0)
+ /* Transfer size is known. */
+ double howmuch = (double) cur / (double) total;
+
+ if (howmuch >= 0.0 && howmuch <= 1.0)
{
- float size = 1.0f * total / 1024;
- const char *unit = "KB";
-
- /* If size is greater than 0.01 MB, set unit to MB. */
- if (size > 10.24)
- {
- size /= 1024;
- unit = "MB";
- }
-
- gdb_printf ("Downloading %.2f %s %s %ps...\n",
- size, unit, data->desc,
- styled_string (file_name_style.style (),
- data->fname));
+ double d_total = (double) total;
+ const char *unit = get_size_and_unit (d_total);
+ std::string msg = string_printf ("Downloading %0.2f %s %s %s",
+ d_total, unit, data->desc,
+ styled_fname.c_str ());
+ data->progress.update_progress (msg, unit, howmuch, d_total);
+ return 0;
}
- else
- gdb_printf ("Downloading %s %ps...\n", data->desc,
- styled_string (file_name_style.style (), data->fname));
-
- data->has_printed = true;
}
+ std::string msg = string_printf ("Downloading %s %s",
+ data->desc, styled_fname.c_str ());
+ data->progress.update_progress (msg);
return 0;
}
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. */
+ current_uiout->do_progress_end ();
+
+ if (fd < 0 && fd != -ENOENT)
+ gdb_printf (_("Download failed: %s. Continuing without %s %ps.\n"),
+ safe_strerror (-fd),
+ data.desc,
+ styled_string (file_name_style.style (), data.fname));
+}
+
/* See debuginfod-support.h */
scoped_fd
srcpath,
&dname));
debuginfod_set_user_data (c, nullptr);
-
- if (fd.get () < 0 && fd.get () != -ENOENT)
- gdb_printf (_("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->reset (dname);
scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
&dname));
debuginfod_set_user_data (c, nullptr);
-
- if (fd.get () < 0 && fd.get () != -ENOENT)
- gdb_printf (_("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);
scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
debuginfod_set_user_data (c, nullptr);
-
- if (fd.get () < 0 && fd.get () != -ENOENT)
- gdb_printf (_("Download failed: %s. " \
- "Continuing without executable 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);
return (string_file *) m_streams.back ();
}
+/* Initialize a progress update to be displayed with
+ mi_ui_out::do_progress_notify. */
+
+void
+mi_ui_out::do_progress_start ()
+{
+ m_progress_info.emplace_back ();
+}
+
+/* Indicate that a task described by MSG is in progress. */
+
+void
+mi_ui_out::do_progress_notify (const std::string &msg, const char *unit,
+ double cur, double total)
+{
+ mi_progress_info &info (m_progress_info.back ());
+
+ if (info.state == progress_update::START)
+ {
+ gdb_printf ("%s...\n", msg.c_str ());
+ info.state = progress_update::WORKING;
+ }
+}
+
+/* Remove the most recent progress update from the progress_info stack. */
+
+void
+mi_ui_out::do_progress_end ()
+{
+ m_progress_info.pop_back ();
+}
+
/* Clear the buffer. */
void
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 () override;
+ virtual void do_progress_notify (const std::string &, const char *,
+ double, double) override;
- virtual void do_progress_notify (double) override
- {
- }
-
- virtual void do_progress_end () override
- {
- }
+ virtual void do_progress_end () override;
private:
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;
+
+ mi_progress_info ()
+ : state (progress_update::START)
+ {}
+ };
+
+ /* 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. */
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
+ };
+
/* SHOULD_PRINT indicates whether something should be printed for a tty. */
- progress_meter (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_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;
+ progress_update (const progress_update &) = delete;
+ progress_update &operator= (const progress_update &) = delete;
- /* Emit some progress for this progress meter. HOWMUCH may range
- from 0.0 to 1.0. */
- void progress (double howmuch)
+ /* Emit some progress for this progress meter. Includes current
+ amount of progress made and total amount in the display. */
+ void update_progress (const std::string& msg, const char *unit,
+ double cur, double total)
{
- m_uiout->do_progress_notify (howmuch);
+ m_uiout->do_progress_notify (msg, unit, cur, total);
}
+ /* Emit some progress for this progress meter. */
+ void update_progress (const std::string& msg)
+ {
+ m_uiout->do_progress_notify (msg, "", -1, -1);
+ }
private:
struct ui_out *m_uiout;
};
+ virtual void do_progress_end () = 0;
+
protected:
virtual void do_table_begin (int nbrofcols, int nr_rows, const char *tblid)
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) = 0;
- virtual void do_progress_end () = 0;
+ virtual void do_progress_start () = 0;
+ virtual void do_progress_notify (const std::string &, const char *,
+ double, double) = 0;
/* Set as not MI-like by default. It is overridden in subclasses if
necessary. */