This is the mail archive of the
gdb@sourceware.org
mailing list for the GDB project.
Modification to support hybrid thread schedulers
- From: Kevin Pouget <kevin dot pouget at etu dot u-bordeaux1 dot fr>
- To: gdb at sources dot redhat dot com
- Date: Thu, 16 Jul 2009 13:55:03 +0200
- Subject: Modification to support hybrid thread schedulers
- References: <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 ;
> }