Compare commits

...

12 Commits

Author SHA1 Message Date
Aaron Merey
982ecf32ea Improve cur/total display, remove higher verbosity setting 2022-06-21 17:55:29 -04:00
Aaron Merey
d59d427f1f Add cur, total to progress bar 2022-05-19 19:08:08 -04:00
Aaron Merey
040720bbf7 Merge with master branch 2022-05-11 14:19:38 -04:00
Aaron Merey
b2e0ecaf06 change api, progress bar 2022-05-05 13:16:59 -04:00
Aaron Merey
6137e64926 Fix whitespace 2022-02-08 20:30:24 -05:00
Aaron Merey
1a626ac8bd Fix display when cur / total goes above 1.0 2022-02-04 19:29:53 -05:00
Aaron Merey
30e62ce67e Fix final char of line not overwriting 2022-02-04 15:08:51 -05:00
Aaron Merey
d43bc9260d Add progress message truncation 2022-02-03 14:43:21 -05:00
Aaron Merey
87538c92d9 Overwrite progress updates with whitespace upon completion 2022-01-26 17:13:20 -05:00
Aaron Merey
6347fd2103 Add percentage-complete indicator for progress updates 2022-01-21 17:54:08 -05:00
Aaron Merey
b7b7f1cef5 Move UI out of debuginfod-support and into ui-out and subclasses. 2022-01-19 12:43:30 -05:00
Aaron Merey
922e534d4d gdb: Improve debuginfod progress updates
Only print the size of the download if it is known.  If the size is
less than 0.01 MB then use KB instead.  If the size is unknown then
also print a throbber to indicate that the download is progressing.
2022-01-11 17:51:46 -05:00
6 changed files with 293 additions and 161 deletions

View File

@@ -262,105 +262,154 @@ 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>
- printed for tty, SHOULD_PRINT == false:
<>
void
cli_ui_out::do_progress_start ()
{
cli_progress_info info;
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_start (const std::string &name, bool should_print)
cli_ui_out::do_progress_notify (const std::string &msg,
const std::string &size,
double howmuch, double total)
{
struct ui_file *stream = m_streams.back ();
cli_progress_info meter;
cli_progress_info &info (m_progress_info.back ());
meter.last_value = 0;
meter.name = name;
if (!stream->isatty ())
if (info.state == progress_update::START)
{
gdb_printf (stream, "%s...", meter.name.c_str ());
if (stream->isatty ())
{
gdb_printf (stream, "%s", msg.c_str ());
info.state = progress_update::BAR;
}
else
{
gdb_printf (stream, "%s...\n", msg.c_str ());
info.state = progress_update::WORKING;
}
}
int chars_per_line = get_chars_per_line ();
if (chars_per_line > MAX_CHARS_PER_LINE)
chars_per_line = MAX_CHARS_PER_LINE;
if (chars_per_line <= 0
|| info.state == progress_update::WORKING
|| !stream->isatty ())
return;
if (total > 0 && howmuch >= 0 && howmuch <= 1.0)
{
std::string progress = string_printf (" %3.d%% (%.02f %s)",
(int)(howmuch * 100), total,
size.c_str ());
int width = chars_per_line - progress.size () - 3;
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 - 3;
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 ());
if (meter.printing == NO_PRINT)
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 (!stream->isatty ())
return;
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;
}
if (chars_per_line <= 0
|| chars_per_line > MAX_CHARS_PER_LINE)
chars_per_line = MAX_CHARS_PER_LINE;
int width = chars_per_line;
gdb_printf (stream, "\r");
for (int i = 0; i < width; ++i)
gdb_printf (stream, " ");
gdb_printf (stream, "\r");
gdb_flush (stream);
}
/* 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 ();
cli_progress_info &meter = m_meters.back ();
m_progress_info.pop_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_meters.pop_back ();
if (stream->isatty ())
clear_current_line ();
}
/* local functions */

View File

@@ -21,6 +21,7 @@
#define CLI_OUT_H
#include "ui-out.h"
#include <chrono>
#include <vector>
class cli_ui_out : public ui_out
@@ -71,8 +72,9 @@ protected:
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 std::string &,
double, double) override;
virtual void do_progress_end () override;
bool suppress_output ()
@@ -85,32 +87,20 @@ private:
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 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;
/* 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);

View File

@@ -24,7 +24,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;
@@ -87,12 +89,12 @@ debuginfod_exec_query (const unsigned char *build_id,
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;
const char * fname;
ui_out::progress_update progress;
};
/* Deleter for a debuginfod_client. */
@@ -108,47 +110,85 @@ 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, MB, GB
or remain unchanged. UNIT will be set to "B", "KB", "MB" or "GB"
accordingly. */
static void
get_size_and_unit (double &size, std::string &unit)
{
if (size < 1024)
{
/* If size is less than 1 KB then set unit to B. */
unit = "B";
return;
}
size /= 1024;
if (size < 1024)
{
/* If size is less than 1 MB then set unit to KB. */
unit = "KB";
return;
}
size /= 1024;
if (size < 1024)
{
/* If size is less than 1 GB then set unit to MB. */
unit = "MB";
return;
}
size /= 1024;
unit = "GB";
}
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";
double d_total = (double) total;
std::string unit = "";
/* 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));
get_size_and_unit (d_total, unit);
std::string msg = string_printf ("Downloading %0.2f %s %s %s\n",
d_total, unit.c_str (), 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\n",
data->desc, styled_fname.c_str ());
data->progress.update_progress (msg);
return 0;
}
@@ -233,6 +273,23 @@ 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. */
current_uiout->do_progress_end ();
string_file styled_fname (current_uiout->can_emit_style_escape ());
fprintf_styled (&styled_fname, file_name_style.style (), "%s",
data.fname);
if (fd < 0 && fd != -ENOENT)
gdb_printf (_("Download failed: %s. Continuing without %s %s.\n"),
safe_strerror (-fd), data.desc, styled_fname.c_str ());
}
/* See debuginfod-support.h */
scoped_fd
@@ -266,11 +323,7 @@ debuginfod_source_query (const unsigned char *build_id,
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);
@@ -308,11 +361,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 (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);
@@ -349,12 +398,7 @@ debuginfod_exec_query (const unsigned char *build_id,
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);

View File

@@ -258,6 +258,31 @@ mi_ui_out::main_stream ()
return (string_file *) m_streams.back ();
}
void
mi_ui_out::do_progress_start ()
{
mi_progress_info info;
info.state = progress_update::START;
m_progress_info.push_back (std::move (info));
}
/* Indicate that a task described by NAME is in progress. */
void
mi_ui_out::do_progress_notify (const std::string &msg, const std::string &unit,
double cur, double total)
{
mi_progress_info &info (m_progress_info.back ());
if (info.state == progress_update::START)
{
struct ui_file *stream = gdb_stdout;
gdb_printf (stream, "%s", msg.c_str ());
info.state = progress_update::WORKING;
}
}
/* Clear the buffer. */
void

View File

@@ -25,7 +25,6 @@
struct ui_out;
struct ui_file;
class mi_ui_out : public ui_out
{
public:
@@ -83,13 +82,9 @@ 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_notify (double) override
{
}
virtual void do_progress_start () override;
virtual void do_progress_notify (const std::string &, const std::string &,
double, double) override;
virtual void do_progress_end () override
{
@@ -101,6 +96,16 @@ 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;
};
/* 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. */

View File

@@ -277,39 +277,58 @@ 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,
/* 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)
: 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, std::string& 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)
@@ -344,9 +363,9 @@ class ui_out
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 std::string &,
double, double) = 0;
/* Set as not MI-like by default. It is overridden in subclasses if
necessary. */