From: Kevin Pouget <kevin.pouget@etu.u-bordeaux1.fr>
To: gdb@sources.redhat.com
Subject: Modification to support hybrid thread schedulers
Date: Thu, 16 Jul 2009 11:55:00 -0000 [thread overview]
Message-ID: <57e4a4fe0907160455q3ec0b66fl722f5f85a48d6dab@mail.gmail.com> (raw)
In-Reply-To: <57e4a4fe0907160452t28770d13n50c19c8ebe5e034c@mail.gmail.com>
Hi there,
as written in the title, I am currently working on the implementation
of the Thread_DB interface for our hybrid thread scheduler,
and you may know that the current implementation of the
linux-thread-db module does not allow to debug anything else than the
Linux' NPTL
---
a little precision regarding this point, when you switch to a thread
and ask for its backtrace, two situations may appear :
the thread is currently running on a LWP
the thread is blocked in the scheduler
in the first case, GDB knows how to read the registers (via the
proc-service interface), but in the second, they must be read in the
memory (eg, in the jmp_buffer or in the mcontext_t)
In the case of a kernel scheduler (NPTL), only the first option
exists, and that is where the problems start. GDB/Linux short-cuts the
Thread_db module, and read directly the registers on the LWP. It works
correctly while the NPTL is used, but screws up the debugging of
user/hybrid libraries ... GDB/Solaris doesn't have this behavior, as
Solaris thread were hybrid until a recent time
---
I managed to mix the two version to make my debugging work correctly
under Linux (basically by adding these three operations:
thread_db_ops.to_fetch_registers .to_store_registers
.to_prepare_to_store),
but sometimes (5% of the time) it happens that in
tdb_thread_fetch_registers, the **REGCACHE** variable is reset.
I understood that it goes this way:
> regcache is provided by the caller,
> td_thr_getgregs_p is called
> --- ps_lgetregs is called
> --- --- get_thread_regcache is called
> --- --- --- the current regcache is freed (regcache_xfree)
> --- --- --- and a new one is created and returned
> --- ---
> ---
> the initial regcache is provided to supply_gregset
> but its regcache->descr is NULL, dereferenced, and it segfaults
but I cannot solve this bug by my own, too much details are involved ...
I know that my problem is not easy, but I would sincerely appreciate
some help to understand all this code I manipulated but barely
perceived
Thanks for your help,
Kevin Pouget
> static void
> tdb_thread_fetch_registers (struct regcache *regcache, int regnum)
> {
> thread_t thread;
> td_thrhandle_t thandle;
> td_err_e val;
> prgregset_t gregset;
> prfpregset_t fpregset;
> gdb_gregset_t *gregset_p = &gregset;
> gdb_fpregset_t *fpregset_p = &fpregset;
>
> if (!is_thread (inferior_ptid))
> {
> /* It's an LWP; pass the request on to procfs. */
> if (target_has_execution)
> target_beneath->to_fetch_registers (regcache, regnum);
>
> return;
> }
>
> /* Solaris thread: convert INFERIOR_PTID into a td_thrhandle_t. */
> thread = GET_THREAD (inferior_ptid);
> if (thread == 0)
> error ("tdb_thread_fetch_registers: thread == 0");
>
> val = td_ta_map_id2thr_p (thread_agent, thread, &thandle);
> if (val != TD_OK && val != TD_NOTHR)
> error ("tdb_thread_fetch_registers: td_ta_map_id2thr: %s",
> thread_db_err_str (val));
>
> if (val == TD_NOTHR) {
> bzero(gregset_p, sizeof(gdb_gregset_t)) ;
> bzero(fpregset_p, sizeof(gdb_fpregset_t)) ;
>
> } else {
> /* Get the general-purpose registers. */
>
> val = td_thr_getgregs_p (&thandle, gregset);
> if (val != TD_OK && val != TD_PARTIALREG)
> error ("tdb_thread_fetch_registers: td_thr_getgregs %s",
> thread_db_err_str (val));
>
> /* For SPARC, TD_PARTIALREG means that only %i0...%i7, %l0..%l7, %pc
> and %sp are saved (by a thread context switch). */
>
> /* And, now the floating-point registers. */
>
> val = td_thr_getfpregs_p (&thandle, &fpregset);
> if (val != TD_OK && val != TD_NOFPREGS)
> error ("tdb_thread_fetch_registers: td_thr_getfpregs %s",
> thread_db_err_str (val));
>
> }
> /* Note that we must call supply_gregset and supply_fpregset *after*
> calling the td routines because the td routines call ps_lget*
> which affect the values stored in the registers array. */
>
> supply_gregset (regcache, (const gdb_gregset_t *) gregset_p);
> supply_fpregset (regcache, (const gdb_fpregset_t *) fpregset_p);
> }
----
> /* Get ready to modify the registers array. On machines which store
> individual registers, this doesn't need to do anything. On
> machines which store all the registers in one fell swoop, this
> makes sure that registers contains all the registers from the
> program being debugged. */
>
> static void
> tdb_thread_prepare_to_store (struct regcache *regcache)
> {
> target_beneath->to_prepare_to_store (regcache);
> }
----
> static void
> tdb_thread_store_registers (struct regcache *regcache, int regnum)
> {
> thread_t thread;
> td_thrhandle_t thandle;
> td_err_e val;
> prgregset_t gregset;
> prfpregset_t fpregset;
>
> if (!is_thread (inferior_ptid))
> {
> /* It's an LWP; pass the request on to procfs.c. */
> target_beneath->to_store_registers (regcache, regnum);
> return;
> }
>
> /* TDB thread: convert INFERIOR_PTID into a td_thrhandle_t. */
> thread = GET_THREAD (inferior_ptid);
>
> val = td_ta_map_id2thr_p (thread_agent, thread, &thandle);
> if (val != TD_OK)
> error ("tdb_thread_store_registers: td_ta_map_id2thr %s",
> thread_db_err_str (val));
>
> if (regnum != -1)
> {
> /* Not writing all the registers. */
> char old_value[MAX_REGISTER_SIZE];
>
> /* Save new register value. */
> regcache_raw_collect (regcache, regnum, old_value);
>
> val = td_thr_getgregs_p (&thandle, gregset);
> if (val != TD_OK)
> error ("tdb_thread_store_registers: td_thr_getgregs %s",
> thread_db_err_str (val));
> val = td_thr_getfpregs_p (&thandle, &fpregset);
> if (val != TD_OK)
> error ("tdb_thread_store_registers: td_thr_getfpregs %s",
> thread_db_err_str (val));
>
> /* Restore new register value. */
> regcache_raw_supply (regcache, regnum, old_value);
>
> }
>
> fill_gregset (regcache, (gdb_gregset_t *) &gregset, regnum);
> fill_fpregset (regcache, (gdb_fpregset_t *) &fpregset, regnum);
>
> val = td_thr_setgregs_p (&thandle, gregset);
> if (val != TD_OK)
> error ("tdb_thread_store_registers: td_thr_setgregs %s",
> thread_db_err_str (val));
> val = td_thr_setfpregs_p (&thandle, &fpregset);
> if (val != TD_OK)
> error ("tdb_thread_store_registers: td_thr_setfpregs %s",
> thread_db_err_str (val));
> }
---
>
> static void
> init_thread_db_ops (void)
> {
> ...
> thread_db_ops.to_fetch_registers = tdb_thread_fetch_registers ;
> thread_db_ops.to_store_registers = tdb_thread_store_registers ;
> thread_db_ops.to_prepare_to_store = tdb_thread_prepare_to_store ;
> }
next parent reply other threads:[~2009-07-16 11:55 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <57e4a4fe0907160452t28770d13n50c19c8ebe5e034c@mail.gmail.com>
2009-07-16 11:55 ` Kevin Pouget [this message]
2009-07-16 12:03 ` Daniel Jacobowitz
2009-07-17 11:23 ` Kevin Pouget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=57e4a4fe0907160455q3ec0b66fl722f5f85a48d6dab@mail.gmail.com \
--to=kevin.pouget@etu.u-bordeaux1.fr \
--cc=gdb@sources.redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox