From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24495 invoked by alias); 16 Jul 2009 11:55:33 -0000 Received: (qmail 24486 invoked by uid 22791); 16 Jul 2009 11:55:32 -0000 X-SWARE-Spam-Status: No, hits=-2.0 required=5.0 tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS X-Spam-Check-By: sourceware.org Received: from mail-fx0-f225.google.com (HELO mail-fx0-f225.google.com) (209.85.220.225) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 16 Jul 2009 11:55:26 +0000 Received: by fxm25 with SMTP id 25so60604fxm.26 for ; Thu, 16 Jul 2009 04:55:24 -0700 (PDT) MIME-Version: 1.0 Received: by 10.103.181.2 with SMTP id i2mr4784372mup.20.1247745324056; Thu, 16 Jul 2009 04:55:24 -0700 (PDT) In-Reply-To: <57e4a4fe0907160452t28770d13n50c19c8ebe5e034c@mail.gmail.com> References: <57e4a4fe0907160452t28770d13n50c19c8ebe5e034c@mail.gmail.com> From: Kevin Pouget Date: Thu, 16 Jul 2009 11:55:00 -0000 Message-ID: <57e4a4fe0907160455q3ec0b66fl722f5f85a48d6dab@mail.gmail.com> Subject: Modification to support hybrid thread schedulers To: gdb@sources.redhat.com Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Mailing-List: contact gdb-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-owner@sourceware.org X-SW-Source: 2009-07/txt/msg00100.txt.bz2 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 ; > }