From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id A484C3858418 for ; Tue, 23 May 2023 20:56:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A484C3858418 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1684875407; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DbcnFDnpj/0vTHMUS2UIASJU/IqFM1h3S2IPEeoshIE=; b=farnO1Kxc998ZVbCyBoj7ud50FdwcPalIlMxeou+ZJyMJDDseFamlvfORi9G8DTwla6kqB Kel7rbl5VqAxAiQi5KQflWhxhqMhMNollGDYXrINlC+sBRvroNyNR53e3FEw6+uKD0sAXl +xV4CF+hSb68fdpZirg6FpmgjlpfJvE= Received: from mail-pj1-f72.google.com (mail-pj1-f72.google.com [209.85.216.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-260-Jwx0PiKOPlW6FO5SzVywxg-1; Tue, 23 May 2023 16:56:46 -0400 X-MC-Unique: Jwx0PiKOPlW6FO5SzVywxg-1 Received: by mail-pj1-f72.google.com with SMTP id 98e67ed59e1d1-25374bd7129so137078a91.1 for ; Tue, 23 May 2023 13:56:45 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684875405; x=1687467405; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DbcnFDnpj/0vTHMUS2UIASJU/IqFM1h3S2IPEeoshIE=; b=PnXO5Sptd0YDFqsSF78pr2MVsCla7SN0ZiAUbcX6t6wrC2RR/VfvQpeG1oBJ4ouOjL 7aFDdFMw08HYYiQBpjzAsnFiql5v05y7N3eaSlnXup93U/w5TQd+dXIeGRKh/IWH5sqD bmCM6mpIlURpi+yB7UJyPJX06R03Va1cOxkxzSBDs6maFkftG3pWeQIfKHFOCWjjAKrf zG+pl6WYcQZx1dpvJE9torZFwz0FUYXQYUkIwIHKvk73I1kL/FjswhFf1upVRTu+mZux 3AgyOuNYNd9T3EIdiztmiunFEe72IrTMkU97qb3ONSagjJYkDOcVKDqEQwZSHGc++vh2 CMZg== X-Gm-Message-State: AC+VfDxumfMZqW5PgvpJP8l+ZozDPiJqUl528fi0fPvBKPOJLerVD6wp QpZagBu6g5O8DIh/K7vmqBuJtbzE0ZVr9q5fiekU0m59CrH+98K4gghFYeD3ay52+VdpxnCgqko LUTBt7/YxYSdbkUsEevnv299ln1twKwpory5F/W8Wk5RN X-Received: by 2002:a17:90a:fd94:b0:255:929c:5166 with SMTP id cx20-20020a17090afd9400b00255929c5166mr4496438pjb.26.1684875404397; Tue, 23 May 2023 13:56:44 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7lYoDdx9IsjA7W+ox6AmcZMQITabWJlyKbcOctX9Z3959KzDNkiTrfNvbSgRR+Cjf12/6ygTdUzB3gAaw61OE= X-Received: by 2002:a17:90a:fd94:b0:255:929c:5166 with SMTP id cx20-20020a17090afd9400b00255929c5166mr4496412pjb.26.1684875403809; Tue, 23 May 2023 13:56:43 -0700 (PDT) MIME-Version: 1.0 References: <20230417180556.1213862-1-amerey@redhat.com> In-Reply-To: From: Aaron Merey Date: Tue, 23 May 2023 16:56:32 -0400 Message-ID: Subject: Re: [PING*4][PATCH 4/7 v2] gdb: Buffer output during print_thread and print_frame_info To: gdb-patches@sourceware.org Cc: Tom Tromey X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Ping Thanks, Aaron On Tue, May 16, 2023 at 10:49=E2=80=AFAM Aaron Merey wr= ote: > > Ping > > Thanks, > Aaron > > On Tue, May 9, 2023 at 9:47=E2=80=AFAM Aaron Merey wr= ote: > > > > Ping, > > > > Thanks, > > Aaron > > > > On Tue, May 2, 2023 at 10:25=E2=80=AFAM Aaron Merey = wrote: > > > > > > Ping > > > > > > Thanks, > > > Aaron > > > > > > On Mon, Apr 17, 2023 at 2:06=E2=80=AFPM Aaron Merey wrote: > > > > > > > > v1 can be found here: > > > > https://sourceware.org/pipermail/gdb-patches/2023-February/197457.h= tml > > > > > > > > v2 addresses Tom's feedback: > > > > https://sourceware.org/pipermail/gdb-patches/2023-March/197716.html > > > > > > > > To ensure that debuginfod progress messages appear correctly during= the > > > > printing of frame and thread info, v2 removes newline tracking and > > > > instead uses buffered output streams described below. > > > > > > > > --- > > > > > > > > Introduce new ui_file buffer_file to temporarily collect output > > > > during print_thread and print_frame_info. This ensures that > > > > output from these functions is not interrupted by debuginfod > > > > progress messages. > > > > > > > > With the addition of deferred debuginfo downloading it is possible > > > > for download progress messages to print during frame and thread > > > > printing. Without any intervention we can end up with poorly > > > > formatted output: > > > > > > > > (gdb) backtrace > > > > [...] > > > > #8 0x00007fbe8af7d7cf in pygi_invoke_c_callable (Downloading s= eparate debug info for /lib64/libpython3.11.so.1.0 > > > > function_cache=3D0x561221b224d0, state=3D... > > > > > > > > To fix this we accumulate the frame/thread output in a buffer_file > > > > and have progress messages skip the buffer and print to gdb_stdout > > > > immediately. This ensures progress messages print first, followed = by > > > > uninterrupted frame/thread info: > > > > > > > > (gdb) backtrace > > > > [...] > > > > Downloading separate debug info for /lib64/libpython3.11.so.1.0 > > > > #8 0x00007fbe8af7d7cf in pygi_invoke_c_callable (function_cach= e=3D0x561221b224d0, state=3D... > > > > --- > > > > gdb/cli-out.c | 22 ++++- > > > > gdb/cli-out.h | 1 + > > > > gdb/debuginfod-support.c | 15 ++-- > > > > gdb/mi/mi-out.c | 22 +++++ > > > > gdb/mi/mi-out.h | 1 + > > > > gdb/stack.c | 38 ++++++--- > > > > gdb/thread.c | 174 +++++++++++++++++++++++------------= ---- > > > > gdb/ui-file.c | 77 +++++++++++++++++ > > > > gdb/ui-file.h | 42 +++++++++- > > > > gdb/ui-out.c | 20 +++++ > > > > gdb/ui-out.h | 66 +++++++++++++++ > > > > 11 files changed, 387 insertions(+), 91 deletions(-) > > > > > > > > diff --git a/gdb/cli-out.c b/gdb/cli-out.c > > > > index fdfd0f7f0cf..8162c1474ee 100644 > > > > --- a/gdb/cli-out.c > > > > +++ b/gdb/cli-out.c > > > > @@ -263,6 +263,19 @@ cli_ui_out::do_redirect (ui_file *outstream) > > > > m_streams.pop_back (); > > > > } > > > > > > > > +void > > > > +cli_ui_out::do_redirect_to_buffer (buffer_file *buf_file) > > > > +{ > > > > + if (buf_file !=3D nullptr) > > > > + { > > > > + gdb_assert (m_streams.size () >=3D 1); > > > > + buf_file->set_stream (m_streams.back ()); > > > > + do_redirect (buf_file); > > > > + } > > > > + else > > > > + m_streams.pop_back (); > > > > +} > > > > + > > > > /* Initialize a progress update to be displayed with > > > > cli_ui_out::do_progress_notify. */ > > > > > > > > @@ -299,7 +312,8 @@ cli_ui_out::do_progress_notify (const std::stri= ng &msg, > > > > double howmuch, double total) > > > > { > > > > int chars_per_line =3D get_chars_per_line (); > > > > - struct ui_file *stream =3D m_streams.back (); > > > > + struct ui_file *stream > > > > + =3D buffer_file::get_unbuffered_stream (m_streams.back ()); > > > > cli_progress_info &info (m_progress_info.back ()); > > > > > > > > if (chars_per_line > MAX_CHARS_PER_LINE) > > > > @@ -384,7 +398,8 @@ cli_ui_out::do_progress_notify (const std::stri= ng &msg, > > > > void > > > > cli_ui_out::clear_current_line () > > > > { > > > > - struct ui_file *stream =3D m_streams.back (); > > > > + struct ui_file *stream > > > > + =3D buffer_file::get_unbuffered_stream (m_streams.back ()); > > > > int chars_per_line =3D get_chars_per_line (); > > > > > > > > if (!stream->isatty () > > > > @@ -410,10 +425,11 @@ void > > > > cli_ui_out::do_progress_end () > > > > { > > > > struct ui_file *stream =3D m_streams.back (); > > > > - m_progress_info.pop_back (); > > > > > > > > if (stream->isatty ()) > > > > clear_current_line (); > > > > + > > > > + m_progress_info.pop_back (); > > > > } > > > > > > > > /* local functions */ > > > > diff --git a/gdb/cli-out.h b/gdb/cli-out.h > > > > index 0729834cbc6..2bccaa8e9e8 100644 > > > > --- a/gdb/cli-out.h > > > > +++ b/gdb/cli-out.h > > > > @@ -71,6 +71,7 @@ class cli_ui_out : public ui_out > > > > virtual void do_wrap_hint (int indent) override; > > > > virtual void do_flush () override; > > > > virtual void do_redirect (struct ui_file *outstream) override; > > > > + virtual void do_redirect_to_buffer (buffer_file *buf_file) overr= ide; > > > > > > > > virtual void do_progress_start () override; > > > > virtual void do_progress_notify (const std::string &, const char= *, > > > > diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c > > > > index fb96dbaced1..22fa3ff2457 100644 > > > > --- a/gdb/debuginfod-support.c > > > > +++ b/gdb/debuginfod-support.c > > > > @@ -159,7 +159,8 @@ progressfn (debuginfod_client *c, long cur, lon= g total) > > > > > > > > if (check_quit_flag ()) > > > > { > > > > - gdb_printf ("Cancelling download of %s %s...\n", > > > > + ui_file *outstream =3D buffer_file::get_unbuffered_stream (g= db_stdout); > > > > + gdb_printf (outstream, "Cancelling download of %s %s...\n", > > > > data->desc, styled_fname.c_str ()); > > > > return 1; > > > > } > > > > @@ -275,10 +276,14 @@ static void > > > > print_outcome (int fd, const char *desc, const char *fname) > > > > { > > > > if (fd < 0 && fd !=3D -ENOENT) > > > > - gdb_printf (_("Download failed: %s. Continuing without %s %ps= .\n"), > > > > - safe_strerror (-fd), > > > > - desc, > > > > - styled_string (file_name_style.style (), fname)); > > > > + { > > > > + ui_file *outstream =3D buffer_file::get_unbuffered_stream (g= db_stdout); > > > > + gdb_printf (outstream, > > > > + _("Download failed: %s. Continuing without %s %p= s.\n"), > > > > + safe_strerror (-fd), > > > > + desc, > > > > + styled_string (file_name_style.style (), fname)); > > > > + } > > > > } > > > > > > > > /* See debuginfod-support.h */ > > > > diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c > > > > index 29a416a426d..8c4a920ac13 100644 > > > > --- a/gdb/mi/mi-out.c > > > > +++ b/gdb/mi/mi-out.c > > > > @@ -194,6 +194,28 @@ mi_ui_out::do_redirect (ui_file *outstream) > > > > m_streams.pop_back (); > > > > } > > > > > > > > +void > > > > +mi_ui_out::do_redirect_to_buffer (buffer_file *buf_file) > > > > +{ > > > > + if (buf_file !=3D nullptr) > > > > + { > > > > + gdb_assert (m_streams.size () >=3D 1); > > > > + ui_file *stream =3D m_streams.back (); > > > > + > > > > + string_file *sstream =3D dynamic_cast(stream)= ; > > > > + if (sstream !=3D nullptr) > > > > + { > > > > + buf_file->write (sstream->data (), sstream->size ()); > > > > + sstream->clear (); > > > > + } > > > > + > > > > + buf_file->set_stream (stream); > > > > + m_streams.push_back (buf_file); > > > > + } > > > > + else > > > > + m_streams.pop_back (); > > > > +} > > > > + > > > > void > > > > mi_ui_out::field_separator () > > > > { > > > > diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h > > > > index 10c9f8a4585..d2f2345daf5 100644 > > > > --- a/gdb/mi/mi-out.h > > > > +++ b/gdb/mi/mi-out.h > > > > @@ -79,6 +79,7 @@ class mi_ui_out : public ui_out > > > > virtual void do_wrap_hint (int indent) override; > > > > virtual void do_flush () override; > > > > virtual void do_redirect (struct ui_file *outstream) override; > > > > + virtual void do_redirect_to_buffer (buffer_file *buf_file) overr= ide; > > > > > > > > virtual bool do_is_mi_like_p () const override > > > > { return true; } > > > > diff --git a/gdb/stack.c b/gdb/stack.c > > > > index 03e903d901b..cf82ef88ea5 100644 > > > > --- a/gdb/stack.c > > > > +++ b/gdb/stack.c > > > > @@ -220,7 +220,8 @@ static void print_frame_local_vars (frame_info_= ptr frame, > > > > const char *regexp, const char = *t_regexp, > > > > int num_tabs, struct ui_file *s= tream); > > > > > > > > -static void print_frame (const frame_print_options &opts, > > > > +static void print_frame (struct ui_out *uiout, > > > > + const frame_print_options &opts, > > > > frame_info_ptr frame, int print_level, > > > > enum print_what print_what, int print_arg= s, > > > > struct symtab_and_line sal); > > > > @@ -1026,16 +1027,15 @@ get_user_print_what_frame_info (gdb::option= al *what) > > > > Used in "where" output, and to emit breakpoint or step > > > > messages. */ > > > > > > > > -void > > > > -print_frame_info (const frame_print_options &fp_opts, > > > > - frame_info_ptr frame, int print_level, > > > > - enum print_what print_what, int print_args, > > > > - int set_current_sal) > > > > +static void > > > > +do_print_frame_info (struct ui_out *uiout, const frame_print_optio= ns &fp_opts, > > > > + frame_info_ptr frame, int print_level, > > > > + enum print_what print_what, int print_args, > > > > + int set_current_sal) > > > > { > > > > struct gdbarch *gdbarch =3D get_frame_arch (frame); > > > > int source_print; > > > > int location_print; > > > > - struct ui_out *uiout =3D current_uiout; > > > > > > > > if (!current_uiout->is_mi_like_p () > > > > && fp_opts.print_frame_info !=3D print_frame_info_auto) > > > > @@ -1111,7 +1111,8 @@ print_frame_info (const frame_print_options &= fp_opts, > > > > || print_what =3D=3D LOC_AND_ADDRESS > > > > || print_what =3D=3D SHORT_LOCATION); > > > > if (location_print || !sal.symtab) > > > > - print_frame (fp_opts, frame, print_level, print_what, print_ar= gs, sal); > > > > + print_frame (uiout, fp_opts, frame, print_level, > > > > + print_what, print_args, sal); > > > > > > > > source_print =3D (print_what =3D=3D SRC_LINE || print_what =3D= =3D SRC_AND_LOC); > > > > > > > > @@ -1191,6 +1192,23 @@ print_frame_info (const frame_print_options = &fp_opts, > > > > gdb_flush (gdb_stdout); > > > > } > > > > > > > > +/* Redirect output to a temporary buffer for the duration > > > > + of do_print_frame_info. */ > > > > + > > > > +void > > > > +print_frame_info (const frame_print_options &fp_opts, > > > > + frame_info_ptr frame, int print_level, > > > > + enum print_what print_what, int print_args, > > > > + int set_current_sal) > > > > +{ > > > > + using ftype =3D void (ui_out *, const frame_print_options &, fra= me_info_ptr, > > > > + int, enum print_what, int, int); > > > > + > > > > + do_with_buffered_output (do_print_frame_info, current_uio= ut, > > > > + fp_opts, frame, print_level, prin= t_what, > > > > + print_args, set_current_sal); > > > > +} > > > > + > > > > /* See stack.h. */ > > > > > > > > void > > > > @@ -1315,13 +1333,13 @@ find_frame_funname (frame_info_ptr frame, e= num language *funlang, > > > > } > > > > > > > > static void > > > > -print_frame (const frame_print_options &fp_opts, > > > > +print_frame (struct ui_out *uiout, > > > > + const frame_print_options &fp_opts, > > > > frame_info_ptr frame, int print_level, > > > > enum print_what print_what, int print_args, > > > > struct symtab_and_line sal) > > > > { > > > > struct gdbarch *gdbarch =3D get_frame_arch (frame); > > > > - struct ui_out *uiout =3D current_uiout; > > > > enum language funlang =3D language_unknown; > > > > struct value_print_options opts; > > > > struct symbol *func; > > > > diff --git a/gdb/thread.c b/gdb/thread.c > > > > index 4d97ed3f2d1..f224e003690 100644 > > > > --- a/gdb/thread.c > > > > +++ b/gdb/thread.c > > > > @@ -1012,6 +1012,106 @@ thread_target_id_str (thread_info *tp) > > > > return target_id; > > > > } > > > > > > > > +/* Print thread TP. GLOBAL_IDS indicates whether REQUESTED_THREADS > > > > + is a list of global or per-inferior thread ids. */ > > > > + > > > > +static void > > > > +do_print_thread (ui_out *uiout, const char *requested_threads, > > > > + int global_ids, int pid, int show_global_ids, > > > > + int default_inf_num, thread_info *tp, > > > > + thread_info *current_thread) > > > > +{ > > > > + int core; > > > > + > > > > + if (!should_print_thread (requested_threads, default_inf_num, > > > > + global_ids, pid, tp)) > > > > + return; > > > > + > > > > + ui_out_emit_tuple tuple_emitter (uiout, NULL); > > > > + > > > > + if (!uiout->is_mi_like_p ()) > > > > + { > > > > + if (tp =3D=3D current_thread) > > > > + uiout->field_string ("current", "*"); > > > > + else > > > > + uiout->field_skip ("current"); > > > > + > > > > + uiout->field_string ("id-in-tg", print_thread_id (tp)); > > > > + } > > > > + > > > > + if (show_global_ids || uiout->is_mi_like_p ()) > > > > + uiout->field_signed ("id", tp->global_num); > > > > + > > > > + /* Switch to the thread (and inferior / target). */ > > > > + switch_to_thread (tp); > > > > + > > > > + /* For the CLI, we stuff everything into the target-id field. > > > > + This is a gross hack to make the output come out looking > > > > + correct. The underlying problem here is that ui-out has no > > > > + way to specify that a field's space allocation should be > > > > + shared by several fields. For MI, we do the right thing > > > > + instead. */ > > > > + > > > > + if (uiout->is_mi_like_p ()) > > > > + { > > > > + uiout->field_string ("target-id", target_pid_to_str (tp->pti= d)); > > > > + > > > > + const char *extra_info =3D target_extra_thread_info (tp); > > > > + if (extra_info !=3D nullptr) > > > > + uiout->field_string ("details", extra_info); > > > > + > > > > + const char *name =3D thread_name (tp); > > > > + if (name !=3D NULL) > > > > + uiout->field_string ("name", name); > > > > + } > > > > + else > > > > + { > > > > + uiout->field_string ("target-id", thread_target_id_str (tp))= ; > > > > + } > > > > + > > > > + if (tp->state =3D=3D THREAD_RUNNING) > > > > + uiout->text ("(running)\n"); > > > > + else > > > > + { > > > > + /* The switch above put us at the top of the stack (leaf > > > > + frame). */ > > > > + print_stack_frame (get_selected_frame (NULL), > > > > + /* For MI output, print frame level. */ > > > > + uiout->is_mi_like_p (), > > > > + LOCATION, 0); > > > > + } > > > > + > > > > + if (uiout->is_mi_like_p ()) > > > > + { > > > > + const char *state =3D "stopped"; > > > > + > > > > + if (tp->state =3D=3D THREAD_RUNNING) > > > > + state =3D "running"; > > > > + uiout->field_string ("state", state); > > > > + } > > > > + > > > > + core =3D target_core_of_thread (tp->ptid); > > > > + if (uiout->is_mi_like_p () && core !=3D -1) > > > > + uiout->field_signed ("core", core); > > > > +} > > > > + > > > > +/* Redirect output to a temporary buffer for the duration > > > > + of do_print_thread. */ > > > > + > > > > +static void > > > > +print_thread (ui_out *uiout, const char *requested_threads, > > > > + int global_ids, int pid, int show_global_ids, > > > > + int default_inf_num, thread_info *tp, thread_info *cu= rrent_thread) > > > > + > > > > +{ > > > > + using ftype =3D void (ui_out *, const char *, int, int, int, > > > > + int, thread_info *, thread_info *); > > > > + > > > > + do_with_buffered_output > > > > + (do_print_thread, uiout, requested_threads, global_ids, pid, > > > > + show_global_ids, default_inf_num, tp, current_thread); > > > > +} > > > > + > > > > /* Like print_thread_info, but in addition, GLOBAL_IDS indicates > > > > whether REQUESTED_THREADS is a list of global or per-inferior > > > > thread ids. */ > > > > @@ -1095,82 +1195,12 @@ print_thread_info_1 (struct ui_out *uiout, = const char *requested_threads, > > > > for (inferior *inf : all_inferiors ()) > > > > for (thread_info *tp : inf->threads ()) > > > > { > > > > - int core; > > > > - > > > > any_thread =3D true; > > > > if (tp =3D=3D current_thread && tp->state =3D=3D THREAD_E= XITED) > > > > current_exited =3D true; > > > > > > > > - if (!should_print_thread (requested_threads, default_inf_= num, > > > > - global_ids, pid, tp)) > > > > - continue; > > > > - > > > > - ui_out_emit_tuple tuple_emitter (uiout, NULL); > > > > - > > > > - if (!uiout->is_mi_like_p ()) > > > > - { > > > > - if (tp =3D=3D current_thread) > > > > - uiout->field_string ("current", "*"); > > > > - else > > > > - uiout->field_skip ("current"); > > > > - > > > > - uiout->field_string ("id-in-tg", print_thread_id (tp)= ); > > > > - } > > > > - > > > > - if (show_global_ids || uiout->is_mi_like_p ()) > > > > - uiout->field_signed ("id", tp->global_num); > > > > - > > > > - /* Switch to the thread (and inferior / target). */ > > > > - switch_to_thread (tp); > > > > - > > > > - /* For the CLI, we stuff everything into the target-id fi= eld. > > > > - This is a gross hack to make the output come out looki= ng > > > > - correct. The underlying problem here is that ui-out h= as no > > > > - way to specify that a field's space allocation should = be > > > > - shared by several fields. For MI, we do the right thi= ng > > > > - instead. */ > > > > - > > > > - if (uiout->is_mi_like_p ()) > > > > - { > > > > - uiout->field_string ("target-id", target_pid_to_str (= tp->ptid)); > > > > - > > > > - const char *extra_info =3D target_extra_thread_info (= tp); > > > > - if (extra_info !=3D nullptr) > > > > - uiout->field_string ("details", extra_info); > > > > - > > > > - const char *name =3D thread_name (tp); > > > > - if (name !=3D NULL) > > > > - uiout->field_string ("name", name); > > > > - } > > > > - else > > > > - { > > > > - uiout->field_string ("target-id", thread_target_id_st= r (tp)); > > > > - } > > > > - > > > > - if (tp->state =3D=3D THREAD_RUNNING) > > > > - uiout->text ("(running)\n"); > > > > - else > > > > - { > > > > - /* The switch above put us at the top of the stack (l= eaf > > > > - frame). */ > > > > - print_stack_frame (get_selected_frame (NULL), > > > > - /* For MI output, print frame leve= l. */ > > > > - uiout->is_mi_like_p (), > > > > - LOCATION, 0); > > > > - } > > > > - > > > > - if (uiout->is_mi_like_p ()) > > > > - { > > > > - const char *state =3D "stopped"; > > > > - > > > > - if (tp->state =3D=3D THREAD_RUNNING) > > > > - state =3D "running"; > > > > - uiout->field_string ("state", state); > > > > - } > > > > - > > > > - core =3D target_core_of_thread (tp->ptid); > > > > - if (uiout->is_mi_like_p () && core !=3D -1) > > > > - uiout->field_signed ("core", core); > > > > + print_thread (uiout, requested_threads, global_ids, pid, > > > > + show_global_ids, default_inf_num, tp, curre= nt_thread); > > > > } > > > > > > > > /* This end scope restores the current thread and the frame > > > > diff --git a/gdb/ui-file.c b/gdb/ui-file.c > > > > index e0814fe2b2d..af02f7defc0 100644 > > > > --- a/gdb/ui-file.c > > > > +++ b/gdb/ui-file.c > > > > @@ -234,6 +234,83 @@ string_file::can_emit_style_escape () > > > > > > > > > > > > > > > > +/* See ui-file.h. */ > > > > + > > > > +void > > > > +buffer_file::wrap_here (int indent) > > > > +{ > > > > + m_string_wraps.emplace (m_string_wraps.end (), > > > > + string_wrap_pair (m_string, indent)); > > > > + m_string.clear (); > > > > +} > > > > + > > > > +/* See ui-file.h. */ > > > > + > > > > +void > > > > +buffer_file::flush_to_stream () > > > > +{ > > > > + if (m_stream =3D=3D nullptr) > > > > + return; > > > > + > > > > + /* Add m_string to m_string_wraps with no corresponding wrap_her= e. */ > > > > + wrap_here (-1); > > > > + > > > > + for (string_wrap_pair pair : m_string_wraps) > > > > + { > > > > + std::string buf =3D std::move (pair.first); > > > > + size_t size =3D buf.size (); > > > > + > > > > + /* Write each line separately. */ > > > > + for (size_t prev =3D 0, cur =3D 0; cur < size; ++cur) > > > > + if (buf.at (cur) =3D=3D '\n' || cur =3D=3D size - 1) > > > > + { > > > > + std::string sub =3D buf.substr (prev, cur - prev + 1); > > > > + m_stream->puts (sub.c_str ()); > > > > + prev =3D cur + 1; > > > > + } > > > > + > > > > + if (pair.second >=3D 0) > > > > + m_stream->wrap_here (pair.second); > > > > + } > > > > + > > > > + m_string_wraps.clear (); > > > > +} > > > > + > > > > +/* See ui-file.h. */ > > > > + > > > > +ui_file * > > > > +buffer_file::get_unbuffered_stream (ui_file *stream) > > > > +{ > > > > + buffer_file *buf =3D dynamic_cast (stream); > > > > + > > > > + if (buf =3D=3D nullptr || buf->m_stream =3D=3D nullptr) > > > > + return stream; > > > > + > > > > + return get_unbuffered_stream (buf->m_stream); > > > > +} > > > > + > > > > +/* See ui-file.h. */ > > > > + > > > > +void > > > > +buffer_file::set_stream (ui_file *stream) > > > > +{ > > > > + if (m_stream =3D=3D nullptr) > > > > + m_stream =3D stream; > > > > +} > > > > + > > > > +/* See ui-file.h. */ > > > > + > > > > +bool > > > > +buffer_file::isatty () > > > > +{ > > > > + if (m_stream !=3D nullptr) > > > > + return m_stream->isatty (); > > > > + > > > > + return false; > > > > +} > > > > + > > > > + > > > > + > > > > stdio_file::stdio_file (FILE *file, bool close_p) > > > > { > > > > set_stream (file); > > > > diff --git a/gdb/ui-file.h b/gdb/ui-file.h > > > > index de24620e247..49967b3cfc6 100644 > > > > --- a/gdb/ui-file.h > > > > +++ b/gdb/ui-file.h > > > > @@ -220,13 +220,53 @@ class string_file : public ui_file > > > > bool empty () const { return m_string.empty (); } > > > > void clear () { return m_string.clear (); } > > > > > > > > -private: > > > > +protected: > > > > /* The internal buffer. */ > > > > std::string m_string; > > > > > > > > bool m_term_out; > > > > }; > > > > > > > > +/* A string_file implementation that collects output on behalf of = a > > > > + given ui_file. Provides access to the underlying ui_file so > > > > + that buffering can be selectively bypassed. */ > > > > + > > > > +class buffer_file : public string_file > > > > +{ > > > > +public: > > > > + explicit buffer_file (bool term_out) > > > > + : string_file (term_out), m_stream (nullptr) > > > > + {} > > > > + > > > > + /* Associate STREAM with this buffer_file. */ > > > > + void set_stream (ui_file *stream); > > > > + > > > > + /* Record the wrap hint. When flushing the buffer, the underlyi= ng > > > > + ui_file's wrap_here will be called at the current point in th= e output. */ > > > > + void wrap_here (int indent) override; > > > > + > > > > + /* Flush collected output to the underlying ui_file. */ > > > > + void flush_to_stream (); > > > > + > > > > + /* Return true if the underlying stream is a tty. */ > > > > + bool isatty () override; > > > > + > > > > + /* Return a pointer to STREAM's underlying ui_file. Recursively= called until > > > > + a non-buffer_file is found. */ > > > > + static ui_file *get_unbuffered_stream (ui_file *stream); > > > > + > > > > +private: > > > > + > > > > + /* The underlying output stream. */ > > > > + ui_file *m_stream; > > > > + > > > > + typedef std::pair string_wrap_pair; > > > > + > > > > + /* A collection of strings paired with an int representing the a= rgument > > > > + to a wrap_here call. */ > > > > + std::vector m_string_wraps; > > > > +}; > > > > + > > > > /* A ui_file implementation that maps directly onto 's FI= LE. > > > > A stdio_file can either own its underlying file, or not. If it > > > > owns the file, then destroying the stdio_file closes the underl= ying > > > > diff --git a/gdb/ui-out.c b/gdb/ui-out.c > > > > index 9380630c320..3a54ea401b7 100644 > > > > --- a/gdb/ui-out.c > > > > +++ b/gdb/ui-out.c > > > > @@ -799,6 +799,12 @@ ui_out::redirect (ui_file *outstream) > > > > do_redirect (outstream); > > > > } > > > > > > > > +void > > > > +ui_out::redirect_to_buffer (buffer_file *buf_file) > > > > +{ > > > > + do_redirect_to_buffer (buf_file); > > > > +} > > > > + > > > > /* Test the flags against the mask given. */ > > > > ui_out_flags > > > > ui_out::test_flags (ui_out_flags mask) > > > > @@ -871,3 +877,17 @@ ui_out::ui_out (ui_out_flags flags) > > > > ui_out::~ui_out () > > > > { > > > > } > > > > + > > > > +ui_out_buffer_pop::ui_out_buffer_pop (ui_out *uiout) > > > > +: m_uiout (uiout), m_buf_file (uiout->can_emit_style_escape ()), > > > > + m_prev_gdb_stdout (gdb_stdout) > > > > +{ > > > > + m_uiout->redirect_to_buffer (&m_buf_file); > > > > + gdb_stdout =3D &m_buf_file; > > > > +} > > > > + > > > > +ui_out_buffer_pop::~ui_out_buffer_pop () > > > > +{ > > > > + m_uiout->redirect_to_buffer (nullptr); > > > > + gdb_stdout =3D m_prev_gdb_stdout; > > > > +} > > > > diff --git a/gdb/ui-out.h b/gdb/ui-out.h > > > > index ba5b1de68ab..1e24a4db2b0 100644 > > > > --- a/gdb/ui-out.h > > > > +++ b/gdb/ui-out.h > > > > @@ -262,6 +262,9 @@ class ui_out > > > > /* Redirect the output of a ui_out object temporarily. */ > > > > void redirect (ui_file *outstream); > > > > > > > > + /* Redirect the output of a ui_out object to a buffer_file tempo= rarily. */ > > > > + void redirect_to_buffer (buffer_file *buf_file); > > > > + > > > > ui_out_flags test_flags (ui_out_flags mask); > > > > > > > > /* HACK: Code in GDB is currently checking to see the type of ui= _out > > > > @@ -360,6 +363,7 @@ class ui_out > > > > virtual void do_wrap_hint (int indent) =3D 0; > > > > virtual void do_flush () =3D 0; > > > > virtual void do_redirect (struct ui_file *outstream) =3D 0; > > > > + virtual void do_redirect_to_buffer (buffer_file *buf_file) =3D 0= ; > > > > > > > > virtual void do_progress_start () =3D 0; > > > > virtual void do_progress_notify (const std::string &, const char= *, > > > > @@ -470,4 +474,66 @@ class ui_out_redirect_pop > > > > struct ui_out *m_uiout; > > > > }; > > > > > > > > +/* On construction, redirect a uiout and gdb_stdout to a buffer_fi= le. > > > > + On destruction restore uiout and gdb_stdout. */ > > > > + > > > > +class ui_out_buffer_pop > > > > +{ > > > > +public: > > > > + ui_out_buffer_pop (ui_out *uiout); > > > > + > > > > + ~ui_out_buffer_pop (); > > > > + > > > > + /* Flush buffered output to the underlying output stream. */ > > > > + void flush () > > > > + { > > > > + m_buf_file.flush_to_stream (); > > > > + } > > > > + > > > > + ui_out_buffer_pop (const ui_out_buffer_pop &) =3D delete; > > > > + ui_out_buffer_pop &operator=3D (const ui_out_buffer_pop &) =3D d= elete; > > > > + > > > > +private: > > > > + /* ui_out being temporarily redirected. */ > > > > + struct ui_out *m_uiout; > > > > + > > > > + /* Buffer which output is temporarily redirected to for the life= time of > > > > + this object. */ > > > > + buffer_file m_buf_file; > > > > + > > > > + /* Original gdb_stdout at the time of this object's construction= . */ > > > > + ui_file *m_prev_gdb_stdout; > > > > +}; > > > > + > > > > +/* Redirect output to a buffer_file for the duration of FUNC. */ > > > > + > > > > +template > > > > +void > > > > +do_with_buffered_output (F func, ui_out *uiout, Arg... args) > > > > +{ > > > > + ui_out_buffer_pop buf (uiout); > > > > + > > > > + try > > > > + { > > > > + func (uiout, std::forward (args)...); > > > > + } > > > > + catch (gdb_exception &ex) > > > > + { > > > > + /* Ideally flush would be called in the destructor of buf, > > > > + however flushing might cause an exception to be thrown. > > > > + Catch it and ensure the first exception propagates. */ > > > > + try > > > > + { > > > > + buf.flush (); > > > > + } > > > > + catch (const gdb_exception &ignore) > > > > + { > > > > + } > > > > + > > > > + throw_exception (std::move (ex)); > > > > + } > > > > + > > > > + /* Try was successful. Let any further exceptions propagate. *= / > > > > + buf.flush (); > > > > +} > > > > #endif /* UI_OUT_H */ > > > > -- > > > > 2.39.2 > > > >