This is the mail archive of the gdb@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Modification to support hybrid thread schedulers


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 ;
> }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]