On 2024/01/05 16:30, Aditya Gupta wrote:
> Currently for most gdb_interface call with a non-null file pointer,
> GDB's output stream is replaced with the passed file pointer
>
> 'info threads', which is a gdb passthrough, fails to print all thread
> after support was added to get registers from crash_target:
>
> Id Target Id Frame
> 1 CPU 0 <unavailable> in ?? ()
> 2 CPU 1
>
> Also, it was noticed that 'info threads' will print all threads when
> 'info threads' is run 2nd and subsequent times.
>
> This incomplete output of 'info threads' was due to a subtle bug in
> gdb_interface. For any passthrough, it switches the gdb output and
> stderr streams to another FILE* passed in `struct gnu_request`.
> But it does not restore the original FILE* stream gdb was using.
>
> For each thread, which does not have it's registers yet, gdb goes to
> crash_target::fetch_registers to ask for registers. Which in turn might
> call 'datatype_info', which causes gdb's output stream to be set to
> '/dev/null'
>
> So all output after the call to datatype_info will go to /dev/null, and
> hence the data to be printed is lost.
>
> When 'info threads' is run a second time, it sets GDB's output streams
> to something which is NOT /dev/null, and since it already has registers
> for each thread, it does not go to 'crash_target::fetch_registers' and
> hence the output stream is consistent, and we get complete output
>
> So what happened is this:
> 1. GDB prints thread 1 (CPU 0) correctly since it had it's registers
> cached during initialisation in 'crash_target_init' (at that times
> registers where still unavailable, so it shows unavailable)
> 2. At CPU 1, to print the frame, it goes to 'crash_target::fetch_registers'
> to get the registers
> 3. crash_target then calls 'machdep->get_cpu_reg' which finally ends up
> at 'get_netdump_regs' which uses 'datatype_info' to get info of
> 'elf_prstatus.pr_reg'
> 4. Inside 'datatype_info', we set the file stream to /dev/null
> (pc->nullfp). Then it passes this request object to gdb_interface
> 5. gdb_interface replaces GDB's output stream to the passed stream, ie.
> /dev/null
> 6. Now, ALL FUTURE WRITES EVEN BY GDB, SUCH AS FOR THE 'info threads'
> COMMAND GOES TO THIS STREAM gnu_request->fp, WHICH is /dev/null,
> until someone else sets the non-null stream later
> 7. Similar behaviour occurs with all other threads
> 8. And hence we lose all subsequent output by GDB also
>
> When 'info threads' is run a second time:
> 1. 'gdb_pass_through' passes a default FILE* to 'gdb_interface', which
> is not /dev/null
> 2. Since the registers are cached for all threads now, it does not go
> back to 'crash_target' for registers, and hence no call is made to
> 'datatype_info', hence GDB's output stream is not changing
> 3. Since the gdb's stream, even though not what it was using originally,
> it's still valid, and not '/dev/null', hence we get the complete output
>
> Fix this by restoring the original output streams, after gdb_interface
> has handled the output
>
> Signed-off-by: Aditya Gupta <[email protected]>
> ---
> gdb-10.2.patch | 46 +++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 45 insertions(+), 1 deletion(-)
>
> diff --git a/gdb-10.2.patch b/gdb-10.2.patch
> index 8c1b43eb07b7..a4e45fa0e6fa 100644
> --- a/gdb-10.2.patch
> +++ b/gdb-10.2.patch
> @@ -1484,13 +1484,29 @@ exit 0
> + }
> +}
> +#endif
> +--- gdb-10.2/gdb/ui-file.c.orig
> ++++ gdb-10.2/gdb/ui-file.c
> +@@ -161,6 +161,12 @@ stdio_file::~stdio_file ()
> + fclose (m_file);
> + }
> +
> ++FILE*
> ++stdio_file::get_stream(void)
> ++{
> ++ return m_file;
> ++}
> ++
> + void
> + stdio_file::set_stream (FILE *file)
> + {
Please move this hunk to the end of gdb-10.2.patch.
> --- gdb-10.2/gdb/ui-file.h.orig
> +++ gdb-10.2/gdb/ui-file.h
> -@@ -195,10 +195,10 @@ class stdio_file : public ui_file
> +@@ -195,10 +195,11 @@ class stdio_file : public ui_file
>
> bool can_emit_style_escape () override;
>
> -private:
> ++ FILE *get_stream(void);
> /* Sets the internal stream to FILE, and saves the FILE's file
> descriptor in M_FD. */
> void set_stream (FILE *file);
> @@ -3267,3 +3283,31 @@ exit 0
>
> for (compunit_symtab *cust : objfile->compunits ())
> {
Please 'add' this patch to the end of gdb-10.2.patch, instead of
changing the existing hunk.
We adopt addition-only policy for gdb-10.2.patch [1] to not make the
patch and branch handling too complicated.
[1] https://github.com/crash-utility/crash/wiki#writing-patches
Thanks,
Kazu
> +--- gdb-10.2/gdb/symtab.c.orig
> ++++ gdb-10.2/gdb/symtab.c
> +@@ -6964,8 +6964,12 @@ void
> + gdb_command_funnel_1(struct gnu_request *req)
> + {
> + struct symbol *sym;
> ++ FILE *original_stdout_stream = nullptr;
> ++ FILE *original_stderr_stream = nullptr;
> +
> + if (req->command != GNU_VERSION && req->command !=
> GNU_USER_PRINT_OPTION) {
> ++ original_stdout_stream = (dynamic_cast< stdio_file *
> >gdb_stdout)->get_stream();
> ++ original_stderr_stream = (dynamic_cast< stdio_file *
> >gdb_stderr)->get_stream();
> + (dynamic_cast<stdio_file *>gdb_stdout)->set_stream(req->fp);
> + (dynamic_cast<stdio_file *>gdb_stderr)->set_stream(req->fp);
> + }
> +@@ -7068,6 +7072,12 @@ gdb_command_funnel_1(struct gnu_request *req)
> + req->flags |= GNU_COMMAND_FAILED;
> + break;
> + }
> ++
> ++ /* Restore the streams gdb output was using */
> ++ if (original_stdout_stream)
> ++ (dynamic_cast<stdio_file
> *>gdb_stdout)->set_stream(original_stdout_stream);
> ++ if (original_stderr_stream)
> ++ (dynamic_cast<stdio_file
> *>gdb_stderr)->set_stream(original_stderr_stream);
> + }
> +
> + /*
--
Crash-utility mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/
Contribution Guidelines: https://github.com/crash-utility/crash/wiki