//-------------------------------------------------------------------------- // returns true: multithreaded application has been detected bool linux_debmod_t::tdb_new(int pid) { if ( ta == NULL ) { #ifdef LDEB msg("checking pid %d with thread_db\n", pid); #endif prochandle.pid = pid; td_err_e err = td_ta_new(&prochandle, &ta); // the call might fail the first time if libc is not loaded yet // so don't show misleading message to the user // COMPLAIN_IF_FAILED("td_ta_new", err); if ( err != TD_OK ) { ta = NULL; return false; } td_thrhandle_t th; err = td_ta_map_lwp2thr(ta, pid, &th); COMPLAIN_IF_FAILED("td_ta_map_lwp2thr", err); if ( err != TD_OK ) return false; err = td_thr_event_enable(&th, TD_CREATE); DIE_IF_FAILED("td_thr_event_enable(TD_CREATE)", err); err = td_thr_event_enable(&th, TD_DEATH); DIE_IF_FAILED("td_thr_event_enable(TD_DEATH)", err); // set breakpoints for thread birth/death td_thr_events_t events; td_event_emptyset(&events); td_event_addset(&events, TD_CREATE); td_event_addset(&events, TD_DEATH); err = td_ta_set_event(ta, &events); DIE_IF_FAILED("td_ta_set_event", err); tdb_enable_event(TD_CREATE, &birth_bpt); tdb_enable_event(TD_DEATH, &death_bpt); #ifdef LDEB msg("thread support has been detected, birth_bpt=%a death_bpt=%a\n", birth_bpt.bpt_addr, death_bpt.bpt_addr); #endif /* // hack: enable thread_td debug. later: it turned out to be useless debug print. td_log(); uchar *ptr = (uchar *)td_log; QASSERT(ptr[0] == 0xFF && ptr[1] == 0x25); ptr = **(uchar***)(ptr+2); ptr += 0x7158 - 0x1120; *ptr = 1; // enable */ tdb_update_threads(); } return true; }
static int thread_db_enable_reporting () { td_thr_events_t events; td_notify_t notify; td_err_e err; /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); td_event_addset (&events, TD_CREATE); #if 0 /* This is reported to be broken in glibc 2.1.3. A different approach will be necessary to support that. */ td_event_addset (&events, TD_DEATH); #endif err = td_ta_set_event (thread_agent, &events); if (err != TD_OK) { warning ("Unable to set global thread event mask: %s", thread_db_err_str (err)); return 0; } /* Get address for thread creation breakpoint. */ err = td_ta_event_addr (thread_agent, TD_CREATE, ¬ify); if (err != TD_OK) { warning ("Unable to get location for thread creation breakpoint: %s", thread_db_err_str (err)); return 0; } set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, thread_db_create_event); #if 0 /* Don't concern ourselves with reported thread deaths, only with actual thread deaths (via wait). */ /* Get address for thread death breakpoint. */ err = td_ta_event_addr (thread_agent, TD_DEATH, ¬ify); if (err != TD_OK) { warning ("Unable to get location for thread death breakpoint: %s", thread_db_err_str (err)); return; } set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, thread_db_death_event); #endif return 1; }
//-------------------------------------------------------------------------- void linux_debmod_t::new_thread(thrinfo_t *info) { int tid = info->ti_lid; threads_t::iterator p = threads.find(tid); if ( p == threads.end() ) // not found { #ifdef LDEB msg("thread %d is new\n", tid); ::display_thrinfo(*info); #endif td_err_e err; td_thr_events_t events; td_event_emptyset(&events); td_event_addset(&events, TD_CREATE); td_event_addset(&events, TD_DEATH); td_event_addset(&events, TD_CATCHSIG); err = td_thr_set_event(info->th_p, &events); DIE_IF_FAILED("td_thr_set_event", err); err = td_thr_event_enable(info->th_p, 1); COMPLAIN_IF_FAILED("td_thr_event_enable", err); if ( err != TD_OK ) { #ifdef LDEB msg("%d: thread dead already? not adding to list.\n", tid); #endif return; } sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSTOP); td_thr_setsigpending(info->th_p, 1, &set); debug_event_t ev; ev.eid = THREAD_START; ev.pid = process_handle; ev.tid = tid; ev.ea = (ea_t)info->ti_startfunc; ev.handled = true; add_thread(tid); // attach to the thread and make is ready for debugging qptrace(PTRACE_ATTACH, tid, 0, 0); int status; int tid2 = waitpid(tid, &status, __WCLONE); // (must succeed) consume SIGSTOP QASSERT(tid2 != -1 && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); get_thread(tid)->waiting_sigstop = false; enqueue_event(ev, IN_FRONT); } get_thread(tid)->thr = info->th_p; }
static void enable_thread_event_reporting (void) { td_thr_events_t events; td_notify_t notify; td_err_e err; /* We cannot use the thread event reporting facility if these functions aren't available. */ if (td_ta_event_addr_p == NULL || td_ta_set_event_p == NULL || td_ta_event_getmsg_p == NULL || td_thr_event_enable_p == NULL) return; /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); td_event_addset (&events, TD_CREATE); td_event_addset (&events, TD_DEATH); err = td_ta_set_event_p (thread_agent, &events); if (err != TD_OK) { warning ("Unable to set global thread event mask: %s", thread_db_err_str (err)); return; } /* Delete previous thread event breakpoints, if any. */ remove_thread_event_breakpoints (); td_create_bp_addr = 0; td_death_bp_addr = 0; /* Set up the thread creation event. */ err = enable_thread_event (thread_agent, TD_CREATE, &td_create_bp_addr); if (err != TD_OK) { warning ("Unable to get location for thread creation breakpoint: %s", thread_db_err_str (err)); return; } /* Set up the thread death event. */ err = enable_thread_event (thread_agent, TD_DEATH, &td_death_bp_addr); if (err != TD_OK) { warning ("Unable to get location for thread death breakpoint: %s", thread_db_err_str (err)); return; } }
static int update_threads_cb(const td_thrhandle_t *th_p, void *data) { thrinfovec_t &newlist = *(thrinfovec_t *)data; thrinfo_t ti; td_err_e err = td_thr_get_info(th_p, &ti); DIE_IF_FAILED("td_thr_get_info", err); if ( ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE ) return 0; if ( ti.ti_tid != 0 ) { td_thr_events_t events; td_event_emptyset(&events); td_event_addset(&events, TD_CREATE); td_event_addset(&events, TD_DEATH); // td_event_addset(&events, TD_CATCHSIG); err = td_thr_set_event(th_p, &events); DIE_IF_FAILED("td_thr_set_event", err); err = td_thr_event_enable(th_p, 1); DIE_IF_FAILED("td_thr_event_enable", err); sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSTOP); td_thr_setsigpending(th_p, 1, &set); } ti.th_p = th_p; #ifdef LDEB msg("update_threads_cb: got thread %d\n", ti.ti_lid); //td_thr_get_info(th_p, &ti); //display_thrinfo(ti); #endif newlist.push_back(ti); return 0; }
static int thread_db_enable_reporting (void) { td_thr_events_t events; td_notify_t notify; td_err_e err; struct thread_db *thread_db = current_process ()->priv->thread_db; if (thread_db->td_ta_set_event_p == NULL || thread_db->td_ta_event_addr_p == NULL || thread_db->td_ta_event_getmsg_p == NULL) /* This libthread_db is missing required support. */ return 0; /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); td_event_addset (&events, TD_CREATE); err = thread_db->td_ta_set_event_p (thread_db->thread_agent, &events); if (err != TD_OK) { warning ("Unable to set global thread event mask: %s", thread_db_err_str (err)); return 0; } /* Get address for thread creation breakpoint. */ err = thread_db->td_ta_event_addr_p (thread_db->thread_agent, TD_CREATE, ¬ify); if (err != TD_OK) { warning ("Unable to get location for thread creation breakpoint: %s", thread_db_err_str (err)); return 0; } thread_db->td_create_bp = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, thread_db_create_event); return 1; }
static void enable_thread_event_reporting (void) { td_thr_events_t events; td_err_e err; #ifdef HAVE_GNU_LIBC_VERSION_H const char *libc_version; int libc_major, libc_minor; #endif struct thread_db_info *info; info = get_thread_db_info (GET_PID (inferior_ptid)); /* We cannot use the thread event reporting facility if these functions aren't available. */ if (info->td_ta_event_addr_p == NULL || info->td_ta_set_event_p == NULL || info->td_ta_event_getmsg_p == NULL || info->td_thr_event_enable_p == NULL) return; /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); td_event_addset (&events, TD_CREATE); #ifdef HAVE_GNU_LIBC_VERSION_H /* The event reporting facility is broken for TD_DEATH events in glibc 2.1.3, so don't enable it if we have glibc but a lower version. */ libc_version = gnu_get_libc_version (); if (sscanf (libc_version, "%d.%d", &libc_major, &libc_minor) == 2 && (libc_major > 2 || (libc_major == 2 && libc_minor > 1))) #endif td_event_addset (&events, TD_DEATH); err = info->td_ta_set_event_p (info->thread_agent, &events); if (err != TD_OK) { warning (_("Unable to set global thread event mask: %s"), thread_db_err_str (err)); return; } /* Delete previous thread event breakpoints, if any. */ remove_thread_event_breakpoints (); info->td_create_bp_addr = 0; info->td_death_bp_addr = 0; /* Set up the thread creation event. */ err = enable_thread_event (TD_CREATE, &info->td_create_bp_addr); if (err != TD_OK) { warning (_("Unable to get location for thread creation breakpoint: %s"), thread_db_err_str (err)); return; } /* Set up the thread death event. */ err = enable_thread_event (TD_DEATH, &info->td_death_bp_addr); if (err != TD_OK) { warning (_("Unable to get location for thread death breakpoint: %s"), thread_db_err_str (err)); return; } }