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 B2E0C3857C69 for ; Thu, 30 Nov 2023 16:29:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B2E0C3857C69 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B2E0C3857C69 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1701361798; cv=none; b=C4apx2VBZ+tlAE0Sbpiuw7vYEjzciD0yzhsW48/pv3XUdVlgjwugpqY9V0FBUGSoKb585ZHinEfKi3SRM9rE/KPnLpu2UY5liDFFaKnsTVS9HyEwtvK/P7NOqg5rK7Qrx6n99ZtAF/ogubDP2w5KjbjDLCWjPTHT7Pgla9pK4UE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1701361798; c=relaxed/simple; bh=E5w1/xmillS8dUFmAfKF/DrdWDb3gZ3lUc3cO1/ZUyU=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=tXo/veqZg2YWCKd0hjyv823ntlem56mHihaVsp4CZzv9Ku433M+Y55+LolcprqAt+JdH5llducWWHRqH3Wi3ZrQRzixCCNDGlLR99DvWnRSTW+Hpn7v/dcim3lhFGBmwXhE5Xe9aaGXWBt7ENy9tRpzSVg425VC58Ceah9YpdPA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1701361794; 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=3OS+ScK9gTt77QPvFBuVre9G0Rzg3geNH7QPVyyeVAU=; b=hnclyuXN7zr1AQQyjsvO3WodyoSdyBdgCh1jvR1aq7RQtR2UaeDSME6LBQqz3NZJL/NwgI xjLK3t9XGe5mtOTUttAuZpzQU3ZrJSaRZjcUuZ5w9cniefrpazxPVcTmgSWj0NAyQNGqLd MrhSOvajsBOHlASw1tx4FfiY1lDoz38= Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-518-9UBLTUmqMtShyUn9ux1hZg-1; Thu, 30 Nov 2023 11:29:52 -0500 X-MC-Unique: 9UBLTUmqMtShyUn9ux1hZg-1 Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-28079cae9fdso1298468a91.2 for ; Thu, 30 Nov 2023 08:29:52 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701361791; x=1701966591; 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=3OS+ScK9gTt77QPvFBuVre9G0Rzg3geNH7QPVyyeVAU=; b=YW3gaNEaFsuuWn3ZP6Sy3eENkwZUD/eSosnhXUaAvA6E6c5Ibbkuhvww9gS3gWcfjk G1cPgKIP0LMfFRCfzFoQdk5z+Z+D0ICmjE+LLyYcHyZFkICOZ1SY7dMgBmpdOH2sXOxi h9GdoYIttrTJfl/SLTcn2rE1PmWNaHf+MZGia7pW1lV5+451M8FqT9QptaY+PXUIWCK1 SnReS+P7bAisA2RO3y9YzLED9HvfXXzv8riXe6lfraH5++oB23UPt2A9LqM06a3QN/zN damkqtOR9zQZseiTtuuo6LYKlIeUuwxahNo1rvHnsdPYVTssCmydprqfn7xOnDkq+rZ0 XAHA== X-Gm-Message-State: AOJu0YyKqgss0SUBejLx1CRXGh7k12h7kTgF1ozHbQq2Xu7m4Pk83UKm IU4YcJY84uqzWOxrvofm4+Z+Zx52C8hhIW+rPjMLIdGqgn+ho6aNDYLx4lAmB57FLa///AQckmc 15SAhVvxrTzqSyH3X3B3Zbtl1scm40VT2/h3vyAwNmVET X-Received: by 2002:a17:90b:4d06:b0:27c:f9e7:30fd with SMTP id mw6-20020a17090b4d0600b0027cf9e730fdmr22365750pjb.7.1701361791290; Thu, 30 Nov 2023 08:29:51 -0800 (PST) X-Google-Smtp-Source: AGHT+IELIqkmwsF1A4IBcs8OzxSCfxrmX6ig1DYTqGBQx1nsuuFQEH4KUoAq9AsJITlyTYlByv9bsKnkI7c5nwVNKSo= X-Received: by 2002:a17:90b:4d06:b0:27c:f9e7:30fd with SMTP id mw6-20020a17090b4d0600b0027cf9e730fdmr22365722pjb.7.1701361790684; Thu, 30 Nov 2023 08:29:50 -0800 (PST) MIME-Version: 1.0 References: <20231028002008.1105723-1-amerey@redhat.com> <20231028002008.1105723-2-amerey@redhat.com> In-Reply-To: From: Aaron Merey Date: Thu, 30 Nov 2023 11:29:39 -0500 Message-ID: Subject: [PING*3][PATCH 1/4 v7] gdb: Buffer output streams during events that might download debuginfo To: gdb-patches@sourceware.org Cc: Andrew Burgess 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=-10.5 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_H3,RCVD_IN_MSPIKE_WL,RCVD_IN_SORBS_WEB,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 Mon, Nov 20, 2023 at 1:38=E2=80=AFPM Aaron Merey wro= te: > > Ping > > Thanks, > Aaron > > On Sun, Nov 12, 2023 at 3:20=E2=80=AFPM Aaron Merey w= rote: > > > > Ping > > > > Thanks, > > Aaron > > > > On Fri, Oct 27, 2023 at 8:20=E2=80=AFPM Aaron Merey = wrote: > > > > > > v6: https://sourceware.org/pipermail/gdb-patches/2023-October/203147.= html > > > > > > v7 adds support for buffering output stream flush(). Newline prefix > > > states have been removed from this patch and instead added to patch 4= /4 > > > in this series. > > > > > > Commit message: > > > > > > Introduce new ui_file buffering_file to temporarily collect output > > > written to gdb_std* output streams during print_thread, print_frame_i= nfo > > > and print_stop_event. > > > > > > This ensures that output during 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 these events. > > > Without any intervention we can end up with poorly formatted output: > > > > > > (gdb) backtrace > > > [...] > > > #8 0x00007fbe8af7d7cf in pygi_invoke_c_callable (Downloading sep= arate debug info for /lib64/libpython3.11.so.1.0 > > > function_cache=3D0x561221b224d0, state=3D... > > > > > > To fix this we buffer writes to gdb_std* output streams while allowin= g > > > debuginfod progress messages to skip the buffers and print to the > > > underlying output streams immediately. Buffered output is then writt= en > > > to the output streams. This ensures that progress messages print fir= st, > > > followed by uninterrupted frame/thread/stop info: > > > > > > (gdb) backtrace > > > [...] > > > Downloading separate debug info for /lib64/libpython3.11.so.1.0 > > > #8 0x00007fbe8af7d7cf in pygi_invoke_c_callable (function_cache= =3D0x561221b224d0, state=3D... > > > > > > Co-Authored-By: Andrew Burgess > > > --- > > > gdb/cli-out.c | 10 ++- > > > gdb/cli-out.h | 3 + > > > gdb/debuginfod-support.c | 15 ++-- > > > gdb/infrun.c | 16 +++- > > > gdb/mi/mi-out.h | 3 + > > > gdb/python/py-mi.c | 3 + > > > gdb/stack.c | 35 +++++--- > > > gdb/thread.c | 171 ++++++++++++++++++++--------------- > > > gdb/ui-file.h | 2 +- > > > gdb/ui-out.c | 144 ++++++++++++++++++++++++++++++ > > > gdb/ui-out.h | 186 +++++++++++++++++++++++++++++++++++++= ++ > > > 11 files changed, 493 insertions(+), 95 deletions(-) > > > > > > diff --git a/gdb/cli-out.c b/gdb/cli-out.c > > > index 20d3d93f1ad..c919622d418 100644 > > > --- a/gdb/cli-out.c > > > +++ b/gdb/cli-out.c > > > @@ -299,7 +299,7 @@ cli_ui_out::do_progress_notify (const std::string= &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 get_unbuffered (m_streams.back ()); > > > cli_progress_info &info (m_progress_info.back ()); > > > > > > if (chars_per_line > MAX_CHARS_PER_LINE) > > > @@ -384,7 +384,7 @@ cli_ui_out::do_progress_notify (const std::string= &msg, > > > void > > > cli_ui_out::clear_progress_notify () > > > { > > > - struct ui_file *stream =3D m_streams.back (); > > > + struct ui_file *stream =3D get_unbuffered (m_streams.back ()); > > > int chars_per_line =3D get_chars_per_line (); > > > > > > scoped_restore save_pagination > > > @@ -413,10 +413,12 @@ void > > > cli_ui_out::do_progress_end () > > > { > > > struct ui_file *stream =3D m_streams.back (); > > > - m_progress_info.pop_back (); > > > + cli_progress_info &info (m_progress_info.back ()); > > > > > > - if (stream->isatty ()) > > > + if (stream->isatty () && info.state !=3D progress_update::START) > > > clear_progress_notify (); > > > + > > > + m_progress_info.pop_back (); > > > } > > > > > > /* local functions */ > > > diff --git a/gdb/cli-out.h b/gdb/cli-out.h > > > index 34016182269..89b4aa40870 100644 > > > --- a/gdb/cli-out.h > > > +++ b/gdb/cli-out.h > > > @@ -35,6 +35,9 @@ class cli_ui_out : public ui_out > > > > > > bool can_emit_style_escape () const override; > > > > > > + ui_file *current_stream () const override > > > + { return m_streams.back (); } > > > + > > > protected: > > > > > > virtual void do_table_begin (int nbrofcols, int nr_rows, > > > diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c > > > index 902af405cc6..b36fb8c35de 100644 > > > --- a/gdb/debuginfod-support.c > > > +++ b/gdb/debuginfod-support.c > > > @@ -155,7 +155,8 @@ progressfn (debuginfod_client *c, long cur, long = total) > > > > > > if (check_quit_flag ()) > > > { > > > - gdb_printf ("Cancelling download of %s %s...\n", > > > + ui_file *outstream =3D get_unbuffered (gdb_stdout); > > > + gdb_printf (outstream, _("Cancelling download of %s %s...\n"), > > > data->desc, styled_fname.c_str ()); > > > return 1; > > > } > > > @@ -296,10 +297,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 get_unbuffered (gdb_stdout); > > > + gdb_printf (outstream, > > > + _("Download failed: %s. Continuing without %s %ps.= \n"), > > > + safe_strerror (-fd), > > > + desc, > > > + styled_string (file_name_style.style (), fname)); > > > + } > > > } > > > > > > /* See debuginfod-support.h */ > > > diff --git a/gdb/infrun.c b/gdb/infrun.c > > > index 4fde96800fb..7c1a7cca74f 100644 > > > --- a/gdb/infrun.c > > > +++ b/gdb/infrun.c > > > @@ -8788,10 +8788,10 @@ print_stop_location (const target_waitstatus = &ws) > > > print_stack_frame (get_selected_frame (nullptr), 0, source_flag,= 1); > > > } > > > > > > -/* See infrun.h. */ > > > +/* See `print_stop_event` in infrun.h. */ > > > > > > -void > > > -print_stop_event (struct ui_out *uiout, bool displays) > > > +static void > > > +do_print_stop_event (struct ui_out *uiout, bool displays) > > > { > > > struct target_waitstatus last; > > > struct thread_info *tp; > > > @@ -8820,6 +8820,16 @@ print_stop_event (struct ui_out *uiout, bool d= isplays) > > > } > > > } > > > > > > +/* See infrun.h. This function itself sets up buffered output for t= he > > > + duration of do_print_stop_event, which performs the actual event > > > + printing. */ > > > + > > > +void > > > +print_stop_event (struct ui_out *uiout, bool displays) > > > +{ > > > + do_with_buffered_output (do_print_stop_event, uiout, displays); > > > +} > > > + > > > /* See infrun.h. */ > > > > > > void > > > diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h > > > index 0dd7479a52f..68ff5faf632 100644 > > > --- a/gdb/mi/mi-out.h > > > +++ b/gdb/mi/mi-out.h > > > @@ -45,6 +45,9 @@ class mi_ui_out : public ui_out > > > return false; > > > } > > > > > > + ui_file *current_stream () const override > > > + { return m_streams.back (); } > > > + > > > protected: > > > > > > virtual void do_table_begin (int nbrofcols, int nr_rows, const cha= r *tblid) > > > diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c > > > index a7b4f4fa3cf..ba913bf1fee 100644 > > > --- a/gdb/python/py-mi.c > > > +++ b/gdb/python/py-mi.c > > > @@ -61,6 +61,9 @@ class py_ui_out : public ui_out > > > return current ().obj.release (); > > > } > > > > > > + ui_file *current_stream () const override > > > + { return nullptr; } > > > + > > > protected: > > > > > > void do_progress_end () override { } > > > diff --git a/gdb/stack.c b/gdb/stack.c > > > index 0b35d62f82f..0560261144c 100644 > > > --- a/gdb/stack.c > > > +++ b/gdb/stack.c > > > @@ -220,7 +220,8 @@ static void print_frame_local_vars (frame_info_pt= r frame, > > > const char *regexp, const char *t= _regexp, > > > int num_tabs, struct ui_file *str= eam); > > > > > > -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_args, > > > struct symtab_and_line sal); > > > @@ -1020,16 +1021,15 @@ get_user_print_what_frame_info (gdb::optional= *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_options= &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) > > > @@ -1105,7 +1105,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_args= , 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); > > > > > > @@ -1185,6 +1186,20 @@ print_frame_info (const frame_print_options &f= p_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) > > > +{ > > > + do_with_buffered_output (do_print_frame_info, current_uiout, > > > + fp_opts, frame, print_level, print_what, > > > + print_args, set_current_sal); > > > +} > > > + > > > /* See stack.h. */ > > > > > > void > > > @@ -1309,13 +1324,13 @@ find_frame_funname (frame_info_ptr frame, enu= m 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 c8145da59bc..f6cf2eb9cf4 100644 > > > --- a/gdb/thread.c > > > +++ b/gdb/thread.c > > > @@ -1064,6 +1064,103 @@ 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->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_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 *curr= ent_thread) > > > + > > > +{ > > > + 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. */ > > > @@ -1147,82 +1244,12 @@ print_thread_info_1 (struct ui_out *uiout, co= nst 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_EXI= TED) > > > current_exited =3D true; > > > > > > - if (!should_print_thread (requested_threads, default_inf_nu= m, > > > - 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 fiel= d. > > > - 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= ->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_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 (lea= f > > > - 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); > > > + print_thread (uiout, requested_threads, global_ids, pid, > > > + show_global_ids, default_inf_num, tp, current= _thread); > > > } > > > > > > /* This end scope restores the current thread and the frame > > > diff --git a/gdb/ui-file.h b/gdb/ui-file.h > > > index 31f87ffd51d..8385033b441 100644 > > > --- a/gdb/ui-file.h > > > +++ b/gdb/ui-file.h > > > @@ -224,7 +224,7 @@ 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; > > > > > > diff --git a/gdb/ui-out.c b/gdb/ui-out.c > > > index defa8f9dfa4..9f643b1ce95 100644 > > > --- a/gdb/ui-out.c > > > +++ b/gdb/ui-out.c > > > @@ -871,3 +871,147 @@ ui_out::ui_out (ui_out_flags flags) > > > ui_out::~ui_out () > > > { > > > } > > > + > > > +/* See ui-out.h. */ > > > + > > > +void > > > +buffer_group::output_unit::flush () const > > > +{ > > > + if (!m_msg.empty ()) > > > + m_stream->puts (m_msg.c_str ()); > > > + > > > + if (m_wrap_hint >=3D 0) > > > + m_stream->wrap_here (m_wrap_hint); > > > + > > > + if (m_flush) > > > + m_stream->flush (); > > > +} > > > + > > > +/* See ui-out.h. */ > > > + > > > +void > > > +buffer_group::write (const char *buf, long length_buf, ui_file *stre= am) > > > +{ > > > + /* Record each line separately. */ > > > + for (size_t prev =3D 0, cur =3D 0; cur < length_buf; ++cur) > > > + if (buf[cur] =3D=3D '\n' || cur =3D=3D length_buf - 1) > > > + { > > > + std::string msg (buf + prev, cur - prev + 1); > > > + > > > + if (m_buffered_output.size () > 0 > > > + && m_buffered_output.back ().m_wrap_hint =3D=3D -1 > > > + && m_buffered_output.back ().m_stream =3D=3D stream > > > + && m_buffered_output.back ().m_msg.size () > 0 > > > + && m_buffered_output.back ().m_msg.back () !=3D '\n') > > > + m_buffered_output.back ().m_msg.append (msg); > > > + else > > > + { > > > + m_buffered_output.emplace_back (msg); > > > + m_buffered_output.back ().m_stream =3D stream; > > > + } > > > + prev =3D cur + 1; > > > + } > > > +} > > > + > > > +/* See ui-out.h. */ > > > + > > > +void > > > +buffer_group::wrap_here (int indent, ui_file *stream) > > > +{ > > > + m_buffered_output.emplace_back ("", indent); > > > + m_buffered_output.back ().m_stream =3D stream; > > > +} > > > + > > > +/* See ui-out.h. */ > > > + > > > +void > > > +buffer_group::flush_here (ui_file *stream) > > > +{ > > > + m_buffered_output.emplace_back ("", -1, true); > > > + m_buffered_output.back ().m_stream =3D stream; > > > +} > > > + > > > +/* See ui-out.h. */ > > > + > > > +ui_file * > > > +get_unbuffered (ui_file * stream) > > > +{ > > > + buffering_file *buf =3D dynamic_cast (stream); > > > + > > > + if (buf =3D=3D nullptr) > > > + return stream; > > > + > > > + return get_unbuffered (buf->stream ()); > > > +} > > > + > > > +buffered_streams::buffered_streams (buffer_group *group, ui_out *uio= ut) > > > + : m_buffered_stdout (group, gdb_stdout), > > > + m_buffered_stderr (group, gdb_stderr), > > > + m_buffered_stdlog (group, gdb_stdlog), > > > + m_buffered_stdtarg (group, gdb_stdtarg), > > > + m_buffered_stdtargerr (group, gdb_stdtargerr), > > > + m_uiout (uiout) > > > + { > > > + gdb_stdout =3D &m_buffered_stdout; > > > + gdb_stderr =3D &m_buffered_stderr; > > > + gdb_stdlog =3D &m_buffered_stdlog; > > > + gdb_stdtarg =3D &m_buffered_stdtarg; > > > + gdb_stdtargerr =3D &m_buffered_stdtargerr; > > > + > > > + ui_file *stream =3D current_uiout->current_stream (); > > > + if (stream !=3D nullptr) > > > + { > > > + m_buffered_current_uiout.emplace (group, stream); > > > + current_uiout->redirect (&(*m_buffered_current_uiout)); > > > + } > > > + > > > + stream =3D m_uiout->current_stream (); > > > + if (stream !=3D nullptr && current_uiout !=3D m_uiout) > > > + { > > > + m_buffered_uiout.emplace (group, stream); > > > + m_uiout->redirect (&(*m_buffered_uiout)); > > > + } > > > + > > > + m_buffers_in_place =3D true; > > > + }; > > > + > > > +/* See ui-out.h. */ > > > + > > > +void > > > +buffered_streams::remove_buffers () > > > + { > > > + if (!m_buffers_in_place) > > > + return; > > > + > > > + m_buffers_in_place =3D false; > > > + > > > + gdb_stdout =3D m_buffered_stdout.stream (); > > > + gdb_stderr =3D m_buffered_stderr.stream (); > > > + gdb_stdlog =3D m_buffered_stdlog.stream (); > > > + gdb_stdtarg =3D m_buffered_stdtarg.stream (); > > > + gdb_stdtargerr =3D m_buffered_stdtargerr.stream (); > > > + > > > + if (m_buffered_current_uiout.has_value ()) > > > + current_uiout->redirect (nullptr); > > > + > > > + if (m_buffered_uiout.has_value ()) > > > + m_uiout->redirect (nullptr); > > > + } > > > + > > > +buffer_group::buffer_group (ui_out *uiout) > > > + : m_buffered_streams (new buffered_streams (this, uiout)) > > > +{ /* Nothing. */ } > > > + > > > +buffer_group::~buffer_group () > > > +{ /* Nothing. */ } > > > + > > > +/* See ui-out.h. */ > > > + > > > +void > > > +buffer_group::flush () const > > > +{ > > > + m_buffered_streams->remove_buffers (); > > > + > > > + for (const output_unit &ou : m_buffered_output) > > > + ou.flush (); > > > +} > > > diff --git a/gdb/ui-out.h b/gdb/ui-out.h > > > index 07567a1df35..70a7145741f 100644 > > > --- a/gdb/ui-out.h > > > +++ b/gdb/ui-out.h > > > @@ -278,6 +278,9 @@ class ui_out > > > escapes. */ > > > virtual bool can_emit_style_escape () const =3D 0; > > > > > > + /* Return the ui_file currently used for output. */ > > > + virtual ui_file *current_stream () const =3D 0; > > > + > > > /* An object that starts and finishes displaying progress updates.= */ > > > class progress_update > > > { > > > @@ -470,4 +473,187 @@ class ui_out_redirect_pop > > > struct ui_out *m_uiout; > > > }; > > > > > > +struct buffered_streams; > > > + > > > +/* Organizes writes to a collection of buffered output streams > > > + so that when flushed, output is written to all streams in > > > + chronological order. */ > > > + > > > +struct buffer_group > > > +{ > > > + buffer_group (ui_out *uiout); > > > + > > > + ~buffer_group (); > > > + > > > + /* Flush all buffered writes to the underlying output streams. */ > > > + void flush () const; > > > + > > > + /* Record contents of BUF and associate it with STREAM. */ > > > + void write (const char *buf, long length_buf, ui_file *stream); > > > + > > > + /* Record a wrap_here and associate it with STREAM. */ > > > + void wrap_here (int indent, ui_file *stream); > > > + > > > + /* Record a call to flush and associate it with STREAM. */ > > > + void flush_here (ui_file *stream); > > > + > > > +private: > > > + > > > + struct output_unit > > > + { > > > + output_unit (std::string msg, int wrap_hint =3D -1, bool flush = =3D false) > > > + : m_msg (msg), m_wrap_hint (wrap_hint), m_flush (flush) > > > + {} > > > + > > > + /* Write contents of this output_unit to the underlying stream. = */ > > > + void flush () const; > > > + > > > + /* Underlying stream for which this output unit will be written = to. */ > > > + ui_file *m_stream; > > > + > > > + /* String to be written to underlying buffer. */ > > > + std::string m_msg; > > > + > > > + /* Argument to wrap_here. -1 indicates no wrap. Used to call w= rap_here > > > + during buffer flush. */ > > > + int m_wrap_hint; > > > + > > > + /* Indicate that the underlying output stream's flush should be = called. */ > > > + bool m_flush; > > > + }; > > > + > > > + /* Output_units to be written to buffered output streams. */ > > > + std::vector m_buffered_output; > > > + > > > + /* Buffered output streams. */ > > > + std::unique_ptr m_buffered_streams; > > > +}; > > > + > > > +/* If FILE is a buffering_file, return it's underlying stream. */ > > > + > > > +extern ui_file *get_unbuffered (ui_file *file); > > > + > > > +/* Buffer output to gdb_stdout and gdb_stderr for the duration of FU= NC. */ > > > + > > > +template > > > +void > > > +do_with_buffered_output (F func, ui_out *uiout, Arg... args) > > > +{ > > > + buffer_group g (uiout); > > > + > > > + try > > > + { > > > + func (uiout, std::forward (args)...); > > > + } > > > + catch (gdb_exception &ex) > > > + { > > > + /* Ideally flush would be called in the destructor of buffer_g= roup, > > > + however flushing might cause an exception to be thrown. Cat= ch it > > > + and ensure the first exception propagates. */ > > > + try > > > + { > > > + g.flush (); > > > + } > > > + catch (const gdb_exception &) > > > + { > > > + } > > > + > > > + throw_exception (std::move (ex)); > > > + } > > > + > > > + /* Try was successful. Let any further exceptions propagate. */ > > > + g.flush (); > > > +} > > > + > > > +/* Accumulate writes to an underlying ui_file. Output to the > > > + underlying file is deferred until required. */ > > > + > > > +struct buffering_file : public ui_file > > > +{ > > > + buffering_file (buffer_group *group, ui_file *stream) > > > + : m_group (group), > > > + m_stream (stream) > > > + { /* Nothing. */ } > > > + > > > + /* Return the underlying output stream. */ > > > + ui_file *stream () const > > > + { > > > + return m_stream; > > > + } > > > + > > > + /* Record the contents of BUF. */ > > > + void write (const char *buf, long length_buf) override > > > + { > > > + m_group->write (buf, length_buf, m_stream); > > > + } > > > + > > > + /* Record a wrap_here call with argument INDENT. */ > > > + void wrap_here (int indent) override > > > + { > > > + m_group->wrap_here (indent, m_stream); > > > + } > > > + > > > + /* Return true if the underlying stream is a tty. */ > > > + bool isatty () override > > > + { > > > + return m_stream->isatty (); > > > + } > > > + > > > + /* Return true if ANSI escapes can be used on the underlying strea= m. */ > > > + bool can_emit_style_escape () override > > > + { > > > + return m_stream->can_emit_style_escape (); > > > + } > > > + > > > + /* Flush the underlying output stream. */ > > > + void flush () override > > > + { > > > + return m_group->flush_here (m_stream); > > > + } > > > + > > > +private: > > > + > > > + /* Coordinates buffering across multiple buffering_files. */ > > > + buffer_group *m_group; > > > + > > > + /* The underlying output stream. */ > > > + ui_file *m_stream; > > > +}; > > > + > > > +/* Attaches and detaches buffers for each of the gdb_std* streams. = */ > > > + > > > +struct buffered_streams > > > +{ > > > + buffered_streams (buffer_group *group, ui_out *uiout); > > > + > > > + ~buffered_streams () > > > + { > > > + this->remove_buffers (); > > > + } > > > + > > > + /* Remove buffering_files from all underlying streams. */ > > > + void remove_buffers (); > > > + > > > +private: > > > + > > > + /* True if buffers are still attached to each underlying output st= ream. */ > > > + bool m_buffers_in_place; > > > + > > > + /* Buffers for each gdb_std* output stream. */ > > > + buffering_file m_buffered_stdout; > > > + buffering_file m_buffered_stderr; > > > + buffering_file m_buffered_stdlog; > > > + buffering_file m_buffered_stdtarg; > > > + buffering_file m_buffered_stdtargerr; > > > + > > > + /* Buffer for current_uiout's output stream. */ > > > + gdb::optional m_buffered_current_uiout; > > > + > > > + /* Additional ui_out being buffered. */ > > > + ui_out *m_uiout; > > > + > > > + /* Buffer for m_uiout's output stream. */ > > > + gdb::optional m_buffered_uiout; > > > +}; > > > + > > > #endif /* UI_OUT_H */ > > > -- > > > 2.41.0 > > >