diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index fdbed6f5e91..1cc9143980e 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -26,6 +26,7 @@
 #include "completer.h"
 #include "readline/readline.h"
 #include "cli/cli-style.h"
+#include "top.h"
 
 /* These are the CLI output functions */
 
@@ -262,105 +263,164 @@ 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:
-     <>
+/* Initialize a progress update to be displayed with
+   cli_ui_out::do_progress_notify.  */
+
+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));
+}
+
+/* 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. "KB"). 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 std::string &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 ());
+
+#define MIN_CHARS_PER_LINE 50
+#define MAX_CHARS_PER_LINE 4096
+
+  if (chars_per_line > MAX_CHARS_PER_LINE
+      || chars_per_line == -1)
+    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;
+	}
+    }
 
-  meter.last_value = 0;
-  meter.name = name;
-  if (!stream->isatty ())
+  if (info.state != progress_update::BAR
+      || chars_per_line < MIN_CHARS_PER_LINE)
+    return;
+
+  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.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 ());
+  int chars_per_line = get_chars_per_line ();
 
-  if (meter.printing == NO_PRINT)
+  if (!stream->isatty ()
+      || !current_ui->input_interactive_p ())
     return;
 
-  if (meter.printing == START)
-    {
-      gdb_printf (stream, "%s\n", meter.name.c_str ());
-      gdb_flush (stream);
-      meter.printing = WORKING;
-    }
+  if (chars_per_line > MAX_CHARS_PER_LINE
+      || chars_per_line == -1)
+    chars_per_line = MAX_CHARS_PER_LINE;
 
-  if (meter.printing == WORKING && howmuch >= 1.0)
-    return;
+  int width = chars_per_line;
 
-  if (!stream->isatty ())
-    return;
+  gdb_printf (stream, "\r");
+  for (int i = 0; i < width; ++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 */
diff --git a/gdb/cli-out.h b/gdb/cli-out.h
index 3f01fe0db6d..01aed00bdf0 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
@@ -71,8 +72,9 @@ 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 std::string &,
+				   double, double) override;
   virtual void do_progress_end () override;
 
   bool suppress_output ()
@@ -85,32 +87,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;
-    /* The name to print.  */
-    std::string name;
-    /* The last notification value.  */
-    double last_value;
+  {   
+    /* Position of the progress indicator.  */
+    int pos;
+    /* The current state.  */  
+    progress_update::state state;
+    /* Progress indicator's time of last 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 void cli_display_match_list (char **matches, int len, int max);
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 5f04a2b38ca..fc62412fff2 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -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;
+  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";
-
-	  /* 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;
+	  std::string unit = "";
+
+	  get_size_and_unit (d_total, unit);
+	  std::string msg = string_printf ("Downloading %0.2f %s %s %s",
+					   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",
+				   data->desc, styled_fname.c_str ());
+  data->progress.update_progress (msg);
   return 0;
 }
 
@@ -230,6 +270,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
@@ -263,11 +320,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);
@@ -305,11 +358,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);
@@ -346,12 +395,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);
diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
index 96a847eb6b6..5c60b139775 100644
--- a/gdb/mi/mi-out.c
+++ b/gdb/mi/mi-out.c
@@ -259,6 +259,34 @@ mi_ui_out::main_stream ()
   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 ()
+{
+  mi_progress_info info;
+
+  info.state = progress_update::START;
+  m_progress_info.push_back (std::move (info));
+}
+
+/* Indicate that a task described by MSG 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...\n", msg.c_str ());
+      info.state = progress_update::WORKING;
+    }
+}
+
 /* Clear the buffer.  */
 
 void
diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h
index 36d7e42345f..d80a7fce259 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,13 +82,9 @@ 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_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 @@ 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 65d40875926..63334dbb8d8 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -278,39 +278,56 @@ 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
+    };
+
     /* 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)
@@ -345,9 +362,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.  */
