+static int n_worker_threads = -1;
+
+/* Update the thread pool for the desired number of threads. */
+static void
+update_thread_pool_size ()
+{
+#if CXX_STD_THREAD
+ int n_threads = n_worker_threads;
+
+ if (n_threads < 0)
+ n_threads = std::thread::hardware_concurrency ();
+
+ gdb::thread_pool::g_thread_pool->set_thread_count (n_threads);
+#endif
+}
+
+static void
+maintenance_set_worker_threads (const char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ update_thread_pool_size ();
+}
+
+\f
+/* If true, display time usage both at startup and for each command. */
+
+static bool per_command_time;
+
+/* If true, display space usage both at startup and for each command. */
+
+static bool per_command_space;
+
+/* If true, display basic symtab stats for each command. */
+
+static bool per_command_symtab;
+
+/* mt per-command commands. */
+
+static struct cmd_list_element *per_command_setlist;
+static struct cmd_list_element *per_command_showlist;
+
+/* Set whether to display time statistics to NEW_VALUE
+ (non-zero means true). */
+
+void
+set_per_command_time (int new_value)
+{
+ per_command_time = new_value;
+}
+
+/* Set whether to display space statistics to NEW_VALUE
+ (non-zero means true). */
+
+void
+set_per_command_space (int new_value)
+{
+ per_command_space = new_value;
+}
+
+/* Count the number of symtabs and blocks. */
+
+static void
+count_symtabs_and_blocks (int *nr_symtabs_ptr, int *nr_compunit_symtabs_ptr,
+ int *nr_blocks_ptr)
+{
+ int nr_symtabs = 0;
+ int nr_compunit_symtabs = 0;
+ int nr_blocks = 0;
+
+ /* When collecting statistics during startup, this is called before
+ pretty much anything in gdb has been initialized, and thus
+ current_program_space may be NULL. */
+ if (current_program_space != NULL)
+ {
+ for (objfile *o : current_program_space->objfiles ())
+ {
+ for (compunit_symtab *cu : o->compunits ())
+ {
+ ++nr_compunit_symtabs;
+ nr_blocks += BLOCKVECTOR_NBLOCKS (COMPUNIT_BLOCKVECTOR (cu));
+ nr_symtabs += std::distance (compunit_filetabs (cu).begin (),
+ compunit_filetabs (cu).end ());
+ }
+ }
+ }
+
+ *nr_symtabs_ptr = nr_symtabs;
+ *nr_compunit_symtabs_ptr = nr_compunit_symtabs;
+ *nr_blocks_ptr = nr_blocks;
+}
+
+/* As indicated by display_time and display_space, report GDB's
+ elapsed time and space usage from the base time and space recorded
+ in this object. */
+
+scoped_command_stats::~scoped_command_stats ()
+{
+ /* Early exit if we're not reporting any stats. It can be expensive to
+ compute the pre-command values so don't collect them at all if we're
+ not reporting stats. Alas this doesn't work in the startup case because
+ we don't know yet whether we will be reporting the stats. For the
+ startup case collect the data anyway (it should be cheap at this point),
+ and leave it to the reporter to decide whether to print them. */
+ if (m_msg_type
+ && !per_command_time
+ && !per_command_space
+ && !per_command_symtab)
+ return;
+
+ if (m_time_enabled && per_command_time)
+ {
+ print_time (_("command finished"));
+
+ using namespace std::chrono;
+
+ run_time_clock::duration cmd_time
+ = run_time_clock::now () - m_start_cpu_time;
+
+ steady_clock::duration wall_time
+ = steady_clock::now () - m_start_wall_time;
+ /* Subtract time spend in prompt_for_continue from walltime. */
+ wall_time -= get_prompt_for_continue_wait_time ();
+
+ printf_unfiltered (!m_msg_type
+ ? _("Startup time: %.6f (cpu), %.6f (wall)\n")
+ : _("Command execution time: %.6f (cpu), %.6f (wall)\n"),
+ duration<double> (cmd_time).count (),
+ duration<double> (wall_time).count ());
+ }
+
+ if (m_space_enabled && per_command_space)
+ {
+#ifdef HAVE_USEFUL_SBRK
+ char *lim = (char *) sbrk (0);
+
+ long space_now = lim - lim_at_start;
+ long space_diff = space_now - m_start_space;
+
+ printf_unfiltered (!m_msg_type
+ ? _("Space used: %ld (%s%ld during startup)\n")
+ : _("Space used: %ld (%s%ld for this command)\n"),
+ space_now,
+ (space_diff >= 0 ? "+" : ""),
+ space_diff);
+#endif
+ }
+
+ if (m_symtab_enabled && per_command_symtab)
+ {
+ int nr_symtabs, nr_compunit_symtabs, nr_blocks;
+
+ count_symtabs_and_blocks (&nr_symtabs, &nr_compunit_symtabs, &nr_blocks);
+ printf_unfiltered (_("#symtabs: %d (+%d),"
+ " #compunits: %d (+%d),"
+ " #blocks: %d (+%d)\n"),
+ nr_symtabs,
+ nr_symtabs - m_start_nr_symtabs,
+ nr_compunit_symtabs,
+ (nr_compunit_symtabs
+ - m_start_nr_compunit_symtabs),
+ nr_blocks,
+ nr_blocks - m_start_nr_blocks);
+ }
+}
+
+scoped_command_stats::scoped_command_stats (bool msg_type)
+: m_msg_type (msg_type)
+{
+ if (!m_msg_type || per_command_space)
+ {
+#ifdef HAVE_USEFUL_SBRK
+ char *lim = (char *) sbrk (0);
+ m_start_space = lim - lim_at_start;
+ m_space_enabled = 1;
+#endif
+ }
+ else
+ m_space_enabled = 0;
+
+ if (msg_type == 0 || per_command_time)
+ {
+ using namespace std::chrono;
+
+ m_start_cpu_time = run_time_clock::now ();
+ m_start_wall_time = steady_clock::now ();
+ m_time_enabled = 1;
+
+ if (per_command_time)
+ print_time (_("command started"));
+ }
+ else
+ m_time_enabled = 0;
+
+ if (msg_type == 0 || per_command_symtab)
+ {
+ int nr_symtabs, nr_compunit_symtabs, nr_blocks;
+
+ count_symtabs_and_blocks (&nr_symtabs, &nr_compunit_symtabs, &nr_blocks);
+ m_start_nr_symtabs = nr_symtabs;
+ m_start_nr_compunit_symtabs = nr_compunit_symtabs;
+ m_start_nr_blocks = nr_blocks;
+ m_symtab_enabled = 1;
+ }
+ else
+ m_symtab_enabled = 0;
+
+ /* Initialize timer to keep track of how long we waited for the user. */
+ reset_prompt_for_continue_wait_time ();
+}
+
+/* See maint.h. */
+
+void
+scoped_command_stats::print_time (const char *msg)
+{
+ using namespace std::chrono;
+
+ auto now = system_clock::now ();
+ auto ticks = now.time_since_epoch ().count () / (1000 * 1000);
+ auto millis = ticks % 1000;
+
+ std::time_t as_time = system_clock::to_time_t (now);
+ struct tm tm;
+ localtime_r (&as_time, &tm);
+
+ char out[100];
+ strftime (out, sizeof (out), "%F %H:%M:%S", &tm);
+
+ printf_unfiltered ("%s.%03d - %s\n", out, (int) millis, msg);
+}
+
+/* Handle unknown "mt set per-command" arguments.
+ In this case have "mt set per-command on|off" affect every setting. */
+
+static void
+set_per_command_cmd (const char *args, int from_tty)
+{
+ struct cmd_list_element *list;
+ int val;
+
+ val = parse_cli_boolean_value (args);
+ if (val < 0)
+ error (_("Bad value for 'mt set per-command no'."));
+
+ for (list = per_command_setlist; list != NULL; list = list->next)
+ if (list->var_type == var_boolean)
+ {
+ gdb_assert (list->type == set_cmd);
+ do_set_command (args, from_tty, list);
+ }
+}
+
+/* Command "show per-command" displays summary of all the current
+ "show per-command " settings. */
+
+static void
+show_per_command_cmd (const char *args, int from_tty)
+{
+ cmd_show_list (per_command_showlist, from_tty, "");
+}
+\f
+
+/* The "maintenance selftest" command. */
+
+static void
+maintenance_selftest (const char *args, int from_tty)
+{
+#if GDB_SELF_TEST
+ selftests::run_tests (args);
+#else
+ printf_filtered (_("\
+Selftests have been disabled for this build.\n"));
+#endif
+}
+
+static void
+maintenance_info_selftests (const char *arg, int from_tty)
+{
+#if GDB_SELF_TEST
+ printf_filtered ("Registered selftests:\n");
+ selftests::for_each_selftest ([] (const std::string &name) {
+ printf_filtered (" - %s\n", name.c_str ());
+ });
+#else
+ printf_filtered (_("\
+Selftests have been disabled for this build.\n"));
+#endif
+}
+
+\f