+/* qsort helper function for info_auto_load_libthread_db, sort the
+ thread_db_info pointers primarily by their FILENAME and secondarily by their
+ PID, both in ascending order. */
+
+static int
+info_auto_load_libthread_db_compare (const void *ap, const void *bp)
+{
+ struct thread_db_info *a = *(struct thread_db_info **) ap;
+ struct thread_db_info *b = *(struct thread_db_info **) bp;
+ int retval;
+
+ retval = strcmp (a->filename, b->filename);
+ if (retval)
+ return retval;
+
+ return (a->pid > b->pid) - (a->pid - b->pid);
+}
+
+/* Implement 'info auto-load libthread-db'. */
+
+static void
+info_auto_load_libthread_db (char *args, int from_tty)
+{
+ struct ui_out *uiout = current_uiout;
+ const char *cs = args ? args : "";
+ struct thread_db_info *info, **array;
+ unsigned info_count, unique_filenames;
+ size_t max_filename_len, max_pids_len, pids_len;
+ struct cleanup *back_to;
+ char *pids;
+ int i;
+
+ cs = skip_spaces_const (cs);
+ if (*cs)
+ error (_("'info auto-load libthread-db' does not accept any parameters"));
+
+ info_count = 0;
+ for (info = thread_db_list; info; info = info->next)
+ if (info->filename != NULL)
+ info_count++;
+
+ array = XNEWVEC (struct thread_db_info *, info_count);
+ back_to = make_cleanup (xfree, array);
+
+ info_count = 0;
+ for (info = thread_db_list; info; info = info->next)
+ if (info->filename != NULL)
+ array[info_count++] = info;
+
+ /* Sort ARRAY by filenames and PIDs. */
+
+ qsort (array, info_count, sizeof (*array),
+ info_auto_load_libthread_db_compare);
+
+ /* Calculate the number of unique filenames (rows) and the maximum string
+ length of PIDs list for the unique filenames (columns). */
+
+ unique_filenames = 0;
+ max_filename_len = 0;
+ max_pids_len = 0;
+ pids_len = 0;
+ for (i = 0; i < info_count; i++)
+ {
+ int pid = array[i]->pid;
+ size_t this_pid_len;
+
+ for (this_pid_len = 0; pid != 0; pid /= 10)
+ this_pid_len++;
+
+ if (i == 0 || strcmp (array[i - 1]->filename, array[i]->filename) != 0)
+ {
+ unique_filenames++;
+ max_filename_len = std::max (max_filename_len,
+ strlen (array[i]->filename));
+
+ if (i > 0)
+ {
+ pids_len -= strlen (", ");
+ max_pids_len = std::max (max_pids_len, pids_len);
+ }
+ pids_len = 0;
+ }
+ pids_len += this_pid_len + strlen (", ");
+ }
+ if (i)
+ {
+ pids_len -= strlen (", ");
+ max_pids_len = std::max (max_pids_len, pids_len);
+ }
+
+ /* Table header shifted right by preceding "libthread-db: " would not match
+ its columns. */
+ if (info_count > 0 && args == auto_load_info_scripts_pattern_nl)
+ uiout->text ("\n");
+
+ make_cleanup_ui_out_table_begin_end (uiout, 2, unique_filenames,
+ "LinuxThreadDbTable");
+
+ uiout->table_header (max_filename_len, ui_left, "filename", "Filename");
+ uiout->table_header (pids_len, ui_left, "PIDs", "Pids");
+ uiout->table_body ();
+
+ pids = (char *) xmalloc (max_pids_len + 1);
+ make_cleanup (xfree, pids);
+
+ /* Note I is incremented inside the cycle, not at its end. */
+ for (i = 0; i < info_count;)
+ {
+ struct cleanup *chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+ char *pids_end;
+
+ info = array[i];
+ uiout->field_string ("filename", info->filename);
+ pids_end = pids;
+
+ while (i < info_count && strcmp (info->filename, array[i]->filename) == 0)
+ {
+ if (pids_end != pids)
+ {
+ *pids_end++ = ',';
+ *pids_end++ = ' ';
+ }
+ pids_end += xsnprintf (pids_end, &pids[max_pids_len + 1] - pids_end,
+ "%u", array[i]->pid);
+ gdb_assert (pids_end < &pids[max_pids_len + 1]);
+
+ i++;
+ }
+ *pids_end = '\0';
+
+ uiout->field_string ("pids", pids);
+
+ uiout->text ("\n");
+ do_cleanups (chain);
+ }
+
+ do_cleanups (back_to);
+
+ if (info_count == 0)
+ uiout->message (_("No auto-loaded libthread-db.\n"));
+}
+