* Modification to support hybrid thread schedulers
[not found] <57e4a4fe0907160452t28770d13n50c19c8ebe5e034c@mail.gmail.com>
@ 2009-07-16 11:55 ` Kevin Pouget
2009-07-16 12:03 ` Daniel Jacobowitz
0 siblings, 1 reply; 3+ messages in thread
From: Kevin Pouget @ 2009-07-16 11:55 UTC (permalink / raw)
To: gdb
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 ;
> }
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Modification to support hybrid thread schedulers
2009-07-16 11:55 ` Modification to support hybrid thread schedulers Kevin Pouget
@ 2009-07-16 12:03 ` Daniel Jacobowitz
2009-07-17 11:23 ` Kevin Pouget
0 siblings, 1 reply; 3+ messages in thread
From: Daniel Jacobowitz @ 2009-07-16 12:03 UTC (permalink / raw)
To: Kevin Pouget; +Cc: gdb
On Thu, Jul 16, 2009 at 01:55:03PM +0200, Kevin Pouget wrote:
> 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
Older versions of GDB supported the full range of libthread_db
operations on Linux. But there were too many problems with
libthread_db, and bugs found in it, for us to continue. The facts are
that linux-thread-db.c is specifically for GLIBC (and works well
enough for uClibc, most of the time). It's just got an inappropriate
name.
One problem was that GLIBC was rarely updated for new registers
made available by the kernel.
You can find the patches in the list archives or CVS history that
removed the operations you're adding. That may be helpful.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Modification to support hybrid thread schedulers
2009-07-16 12:03 ` Daniel Jacobowitz
@ 2009-07-17 11:23 ` Kevin Pouget
0 siblings, 0 replies; 3+ messages in thread
From: Kevin Pouget @ 2009-07-17 11:23 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb
> You can find the patches in the list archives or CVS history that
> removed the operations you're adding. That may be helpful.
I think this is the patch you mean :
http://www.sourceware.org/ml/gdb-patches/2006-07/msg00140.html
(thread_db_fetch_registers),
but the code is globally identical to the one used in the Solaris version ...
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2009-07-17 11:23 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <57e4a4fe0907160452t28770d13n50c19c8ebe5e034c@mail.gmail.com>
2009-07-16 11:55 ` Modification to support hybrid thread schedulers Kevin Pouget
2009-07-16 12:03 ` Daniel Jacobowitz
2009-07-17 11:23 ` Kevin Pouget
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox