diff options
author | Aaron Merey <amerey@redhat.com> | 2024-01-12 14:38:45 -0500 |
---|---|---|
committer | Aaron Merey <amerey@redhat.com> | 2024-01-19 00:18:00 -0500 |
commit | 519d634396592c5698add4a327468e6e6920576e (patch) | |
tree | 695c241945e77efdfae59af2a33922425cb8f290 /gdb/thread.c | |
parent | 64db3e4d881d347704139c89d90048082774c9cc (diff) | |
download | fsf-binutils-gdb-519d634396592c5698add4a327468e6e6920576e.zip fsf-binutils-gdb-519d634396592c5698add4a327468e6e6920576e.tar.gz fsf-binutils-gdb-519d634396592c5698add4a327468e6e6920576e.tar.bz2 |
gdb: Buffer output streams during events that might download debuginfo
Introduce new ui_file buffering_file to temporarily collect output
written to gdb_std* output streams during print_thread, print_frame_info
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 separate debug info for /lib64/libpython3.11.so.1.0
function_cache=0x561221b224d0, state=<optimized out>...
To fix this we buffer writes to gdb_std* output streams while allowing
debuginfod progress messages to skip the buffers and print to the
underlying output streams immediately. Buffered output is then written
to the output streams. This ensures that progress messages print first,
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=0x561221b224d0, state=<optimized out>...
Co-Authored-By: Andrew Burgess <aburgess@redhat.com>
Approved-By: Andrew Burgess <aburgess@redhat.com>
Diffstat (limited to 'gdb/thread.c')
-rw-r--r-- | gdb/thread.c | 180 |
1 files changed, 104 insertions, 76 deletions
diff --git a/gdb/thread.c b/gdb/thread.c index 62fd9c6..2252356 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1089,6 +1089,107 @@ 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; + + /* In case REQUESTED_THREADS contains $_thread. */ + if (current_thread != nullptr) + switch_to_thread (current_thread); + + 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 == 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 = target_extra_thread_info (tp); + if (extra_info != nullptr) + uiout->field_string ("details", extra_info); + + const char *name = thread_name (tp); + if (name != NULL) + uiout->field_string ("name", name); + } + else + { + uiout->field_string ("target-id", thread_target_id_str (tp)); + } + + if (tp->state == 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 = "stopped"; + + if (tp->state == THREAD_RUNNING) + state = "running"; + uiout->field_string ("state", state); + } + + core = target_core_of_thread (tp->ptid); + if (uiout->is_mi_like_p () && core != -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 *current_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. */ @@ -1176,86 +1277,13 @@ 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 = true; + if (tp == current_thread && tp->state == THREAD_EXITED) current_exited = true; - /* In case REQUESTED_THREADS contains $_thread. */ - if (current_thread != nullptr) - switch_to_thread (current_thread); - - 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 == 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 = target_extra_thread_info (tp); - if (extra_info != nullptr) - uiout->field_string ("details", extra_info); - - const char *name = thread_name (tp); - if (name != NULL) - uiout->field_string ("name", name); - } - else - { - uiout->field_string ("target-id", thread_target_id_str (tp)); - } - - if (tp->state == 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 = "stopped"; - - if (tp->state == THREAD_RUNNING) - state = "running"; - uiout->field_string ("state", state); - } - - core = target_core_of_thread (tp->ptid); - if (uiout->is_mi_like_p () && core != -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 |