2 * Copyright (c) 1996-2009 Barton P. Miller
4 * We provide the Paradyn Parallel Performance Tools (below
5 * described as "Paradyn") on an AS IS basis, and do not warrant its
6 * validity or performance. We reserve the right to update, modify,
7 * or discontinue this software at any time. We shall have no
8 * obligation to supply such updates or modifications or any other
9 * form of support to you.
11 * By your use of Paradyn, you understand and agree that we (or any
12 * other person or entity with proprietary rights in Paradyn) are
13 * under no obligation to provide either maintenance services,
14 * update services, notices of latent defects, or correction of
15 * defects for Paradyn.
17 * This library is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Lesser General Public
19 * License as published by the Free Software Foundation; either
20 * version 2.1 of the License, or (at your option) any later version.
22 * This library is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * Lesser General Public License for more details.
27 * You should have received a copy of the GNU Lesser General Public
28 * License along with this library; if not, write to the Free Software
29 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <proc_service.h>
33 #include <thread_db.h>
40 #include "common/h/dthread.h"
41 #include "int_thread_db.h"
44 * proc_service interface implementation, needed by libthread_db
47 ps_err_e ps_pglobal_lookup(struct ps_prochandle *handle, const char *objName,
48 const char *symName, psaddr_t *symbolAddr)
50 return handle->thread_db_proc->getSymbolAddr(objName, symName, symbolAddr);
53 ps_err_e ps_pread(struct ps_prochandle *handle, psaddr_t remote, void *local, size_t size) {
54 pthrd_printf("thread_db reading from %#lx to %#lx, size = %d on %d\n",
55 (unsigned long)remote, (unsigned long)local, size, handle->thread_db_proc->getPid());
56 if( !handle->thread_db_proc->plat_readProcMem(local, (Dyninst::Address)remote, size) ) {
57 pthrd_printf("Failed to read from %#lx to %#lx, size = %d on %d: %s\n",
58 (unsigned long)remote, (unsigned long)local, size, handle->thread_db_proc->getPid(),
66 ps_err_e ps_pwrite(struct ps_prochandle *handle, psaddr_t remote, const void *local, size_t size) {
67 pthrd_printf("thread_db writing to %#lx write %#lx, size = %d on %d\n",
68 (unsigned long)remote, (unsigned long)local, size, handle->thread_db_proc->getPid());
69 if( !handle->thread_db_proc->plat_writeProcMem(const_cast<void *>(local), (Dyninst::Address)remote, size) ) {
70 pthrd_printf("Failed to write to %#lx write %#lx, size = %d on %d: %s\n",
71 (unsigned long)remote, (unsigned long)local, size, handle->thread_db_proc->getPid(),
79 ps_err_e ps_linfo(struct ps_prochandle *handle, lwpid_t lwp, void *lwpInfo) {
80 if( !handle->thread_db_proc->plat_getLWPInfo(lwp, lwpInfo) )
86 ps_err_e ps_lstop(struct ps_prochandle *handle, lwpid_t lwp) {
87 if( !handle->thread_db_proc->plat_stopThread(lwp) ) {
94 ps_err_e ps_lcontinue(struct ps_prochandle *handle, lwpid_t lwp) {
95 if( !handle->thread_db_proc->plat_contThread(lwp) ) {
102 void ps_plog(const char *format, ...) {
103 if( !dyninst_debug_proccontrol ) return;
104 if( NULL == format ) return;
107 va_start(va, format);
108 vfprintf(pctrl_err_out, format, va);
112 #define NA_IMPLEMENTED "This function is not implemented"
114 ps_err_e ps_lgetfpregs(struct ps_prochandle *, lwpid_t, prfpregset_t *) {
115 assert(!NA_IMPLEMENTED);
119 ps_err_e ps_lgetregs(struct ps_prochandle *, lwpid_t, prgregset_t) {
120 assert(!NA_IMPLEMENTED);
124 ps_err_e ps_lsetfpregs(struct ps_prochandle *, lwpid_t, const prfpregset_t *) {
125 assert(!NA_IMPLEMENTED);
129 ps_err_e ps_lsetregs(struct ps_prochandle *, lwpid_t, const prgregset_t) {
130 assert(!NA_IMPLEMENTED);
134 ps_err_e ps_lgetxmmregs (struct ps_prochandle *, lwpid_t, char *) {
135 assert(!NA_IMPLEMENTED);
139 ps_err_e ps_lsetxmmregs (struct ps_prochandle *, lwpid_t, const char *) {
140 assert(!NA_IMPLEMENTED);
144 ps_err_e ps_pcontinue(struct ps_prochandle *) {
145 assert(!NA_IMPLEMENTED);
149 ps_err_e ps_pdmodel(struct ps_prochandle *, int *) {
150 assert(!NA_IMPLEMENTED);
154 ps_err_e ps_pstop(struct ps_prochandle *) {
155 assert(!NA_IMPLEMENTED);
159 #ifndef CASE_RETURN_STR
160 #define CASE_RETURN_STR(x) case x: return #x;
163 static const char *tdErr2Str(td_err_e errVal) {
165 CASE_RETURN_STR(TD_ERR)
166 CASE_RETURN_STR(TD_OK)
167 CASE_RETURN_STR(TD_BADKEY)
168 CASE_RETURN_STR(TD_BADPH)
169 CASE_RETURN_STR(TD_BADSH)
170 CASE_RETURN_STR(TD_BADTA)
171 CASE_RETURN_STR(TD_BADTH)
172 CASE_RETURN_STR(TD_DBERR)
173 CASE_RETURN_STR(TD_MALLOC)
174 CASE_RETURN_STR(TD_NOAPLIC)
175 CASE_RETURN_STR(TD_NOCAPAB)
176 CASE_RETURN_STR(TD_NOEVENT)
177 CASE_RETURN_STR(TD_NOFPREGS)
178 CASE_RETURN_STR(TD_NOLIBTHREAD)
179 CASE_RETURN_STR(TD_NOLWP)
180 CASE_RETURN_STR(TD_NOMSG)
181 CASE_RETURN_STR(TD_NOSV)
182 CASE_RETURN_STR(TD_NOTHR)
183 CASE_RETURN_STR(TD_NOTSD)
184 CASE_RETURN_STR(TD_NOXREGS)
185 CASE_RETURN_STR(TD_PARTIALREG)
192 Event::ptr decodeThreadEvent(td_event_msg_t *eventMsg) {
193 td_thrinfo_t threadInfo;
195 if( TD_OK != (errVal = td_thr_get_info(eventMsg->th_p, &threadInfo)) ) {
196 perr_printf("Failed to get thread event info from event msg: %s(%d)\n",
197 tdErr2Str(errVal), errVal);
201 switch(eventMsg->event) {
203 if( TD_OK != (errVal = td_thr_dbsuspend(eventMsg->th_p)) ) {
204 perr_printf("Failed suspend new thread via thread_db: %s(%d)\n",
205 tdErr2Str(errVal), errVal);
208 return Event::ptr(new EventNewThread((Dyninst::LWP)threadInfo.ti_lid));
211 return Event::ptr(new EventThreadDestroy(EventType::Pre));
214 pthrd_printf("Unimplemented libthread_db event encountered. Skipping for now.\n");
221 volatile bool thread_db_process::thread_db_initialized = false;
222 Mutex thread_db_process::thread_db_init_lock;
224 thread_db_process::thread_db_process(Dyninst::PID p, std::string e, std::vector<std::string> a)
225 : sysv_process(p, e, a), threadAgent(NULL)
227 self = new ps_prochandle();
229 self->thread_db_proc = this;
232 thread_db_process::thread_db_process(Dyninst::PID pid_, int_process *p)
233 : sysv_process(pid_, p), threadAgent(NULL)
235 self = new ps_prochandle();
237 self->thread_db_proc = this;
240 thread_db_process::~thread_db_process()
242 // Free the breakpoints allocated for events
243 map<Dyninst::Address, pair<int_breakpoint *, EventType> >::iterator brkptIter;
244 for(brkptIter = addr2Event.begin(); brkptIter != addr2Event.end(); ++brkptIter) {
245 delete brkptIter->second.first;
248 // Close all the symbol readers used
249 map<string, pair<LoadedLib *, SymReader *> >::iterator symReaderIter;
250 for(symReaderIter = symReaders.begin(); symReaderIter != symReaders.end();
253 symreader_factory->closeSymbolReader(symReaderIter->second.second);
259 bool thread_db_process::initThreadDB() {
260 // Q: Why isn't this in the constructor?
261 // A: This function depends on the corresponding thread library being loaded
262 // and this event occurs some time after process creation.
264 // Make sure thread_db is initialized - only once for all instances
265 if( !thread_db_initialized ) {
266 thread_db_init_lock.lock();
267 if( !thread_db_initialized ) {
269 if( TD_OK != (errVal = td_init()) ) {
270 perr_printf("Failed to initialize libthread_db: %s(%d)\n",
271 tdErr2Str(errVal), errVal);
272 setLastError(err_internal, "libthread_db initialization failed");
275 pthrd_printf("Sucessfully initialized thread_db\n");
276 thread_db_initialized = true;
278 thread_db_init_lock.unlock();
281 // Create the thread agent
282 td_err_e errVal = td_ta_new(self, &threadAgent);
287 pthrd_printf("Debuggee isn't multithreaded at this point, libthread_db not enabled\n");
290 perr_printf("Failed to create thread agent: %s(%d)\n",
291 tdErr2Str(errVal), errVal);
292 setLastError(err_internal, "Failed to create libthread_db agent");
297 td_thr_events_t eventMask;
298 td_event_fillset(&eventMask);
300 errVal = td_ta_set_event(threadAgent, &eventMask);
301 if( TD_OK != errVal ) {
302 perr_printf("Failed to enable events: %s(%d)\n",
303 tdErr2Str(errVal), errVal);
304 setLastError(err_internal, "Failed to enable libthread_db events");
308 // Determine the addresses for all events
309 td_event_e allEvents[] = { TD_CATCHSIG, TD_CONCURRENCY, TD_CREATE,
310 TD_DEATH, TD_IDLE, TD_LOCK_TRY, TD_PREEMPT, TD_PRI_INHERIT,
311 TD_READY, TD_REAP, TD_SLEEP, TD_SWITCHFROM, TD_SWITCHTO,
314 for(unsigned i = 0; i < (sizeof(allEvents)/sizeof(td_event_e)); ++i) {
315 td_notify_t notifyResult;
316 errVal = td_ta_event_addr(threadAgent, allEvents[i], ¬ifyResult);
318 // This indicates that the event isn't supported
319 if( TD_OK != errVal ) continue;
321 assert( notifyResult.type == NOTIFY_BPT && "Untested notify type" );
324 switch(allEvents[i]) {
326 newEvent = EventType(EventType::Post, EventType::ThreadCreate);
327 pthrd_printf("Installing breakpoint for thread creation events\n");
330 newEvent = EventType(EventType::Post, EventType::ThreadDestroy);
331 pthrd_printf("Installing breakpoint for thread destroy events\n");
334 pthrd_printf("Unimplemented libthread_db event encountered. Skipping for now.\n");
338 int_breakpoint *newEventBrkpt = new int_breakpoint(Breakpoint::ptr());
339 if( !addBreakpoint((Dyninst::Address)notifyResult.u.bptaddr,
342 perr_printf("Failed to install new event breakpoint\n");
343 setLastError(err_internal, "Failed to install new thread_db event breakpoint");
344 delete newEventBrkpt;
349 pair<map<Dyninst::Address, pair<int_breakpoint *, EventType> >::iterator, bool> insertIter;
350 insertIter = addr2Event.insert(make_pair((Dyninst::Address)notifyResult.u.bptaddr,
351 make_pair(newEventBrkpt, newEvent)));
353 assert( insertIter.second && "event breakpoint address not unique" );
359 void thread_db_process::freeThreadDBAgent() {
361 // This code cannot be in the destructor because it makes use of
362 // the proc_service interface and this makes calls to functions
363 // that are pure virtual in this class.
365 // A possible, better solution would be to make the functions static
366 // but we lose all the convenience of pure virtual functions
368 // At any rate, this function should be called from a derived class'
371 if( thread_db_initialized && threadAgent ) {
372 td_err_e errVal = td_ta_delete(threadAgent);
373 if( TD_OK != errVal ) {
374 perr_printf("Failed to delete thread agent: %s(%d)\n",
375 tdErr2Str(errVal), errVal);
377 assert( TD_OK == errVal && "Failed to delete thread agent" );
382 bool thread_db_process::getEventsAtAddr(Dyninst::Address addr,
383 thread_db_thread *eventThread, vector<Event::ptr> &threadEvents)
385 unsigned oldSize = threadEvents.size();
387 // Determine what type event occurs at the specified address
388 map<Dyninst::Address, pair<int_breakpoint *, EventType> >::iterator addrIter;
389 addrIter = addr2Event.find(addr);
390 if( addrIter == addr2Event.end() ) return false;
392 switch(addrIter->second.second.code()) {
393 case EventType::ThreadCreate:
395 pthrd_printf("Address 0x%lx corresponds to a thread create event.\n",
397 // Need to ask via the thread_db agent for creation events. This
398 // could result in getting information about other events. All of
399 // these events need to be handled.
400 td_event_msg_t threadMsg;
401 td_err_e msgErr = TD_OK;
402 while(msgErr == TD_OK) {
403 msgErr = td_ta_event_getmsg(threadAgent, &threadMsg);
404 if( msgErr == TD_OK ) {
405 Event::ptr threadEvent = decodeThreadEvent(&threadMsg);
407 threadEvents.push_back(threadEvent);
412 if( msgErr != TD_NOMSG ) {
413 perr_printf("Failed to retrieve thread event: %s(%d)\n",
414 tdErr2Str(msgErr), msgErr);
418 case EventType::ThreadDestroy:
420 pthrd_printf("Address 0x%lx corresponds to a thread destroy event.\n",
423 Event::ptr threadEvent = eventThread->getThreadEvent();
425 threadEvents.push_back(threadEvent);
427 perr_printf("Failed to retrieve thread event for LWP %d\n",
428 eventThread->getLWP());
433 pthrd_printf("Unimplemented libthread_db event encountered. Skipping for now.\n");
437 return oldSize != threadEvents.size();
440 td_thragent_t *thread_db_process::getThreadDBAgent() {
444 ps_err_e thread_db_process::getSymbolAddr(const char *objName, const char *symName,
445 psaddr_t *symbolAddr)
447 SymReader *objSymReader = NULL;
448 LoadedLib *lib = NULL;
450 // For static executables, we need to search the executable instead of the
453 // For static executables, breakpoint_addr isn't set
454 if( !breakpoint_addr ) {
455 lib = translator->getExecutable();
457 perr_printf("Failed to get loaded version of executable\n");
458 setLastError(err_internal, "Failed to get loaded version of executable");
462 map<string, pair<LoadedLib *, SymReader *> >::iterator symReaderIter;
463 symReaderIter = symReaders.find(lib->getName());
464 if( symReaderIter == symReaders.end() ) {
465 objSymReader = symreader_factory->openSymbolReader(lib->getName());
466 if( NULL == objSymReader ) {
467 perr_printf("Failed to open symbol reader for %s\n",
468 lib->getName().c_str());
469 setLastError(err_internal, "Failed to open executable for symbol reading");
472 symReaders.insert(make_pair(lib->getName(), make_pair(lib, objSymReader)));
474 objSymReader = symReaderIter->second.second;
477 // FreeBSD implementation doesn't set objName
479 if( NULL == objName ) {
480 objNameStr = getThreadLibName(symName);
482 objNameStr = objName;
485 map<string, pair<LoadedLib *, SymReader *> >::iterator symReaderIter;
486 symReaderIter = symReaders.find(objNameStr);
487 if( symReaderIter == symReaders.end() ) {
488 vector<LoadedLib *> libs;
489 if( !translator->getLibs(libs) ) {
490 perr_printf("Failed to retrieve loaded libraries\n");
491 setLastError(err_internal, "Failed to retrieve loaded libraries");
495 vector<LoadedLib *>::iterator loadedLibIter;
496 for(loadedLibIter = libs.begin(); loadedLibIter != libs.end();
499 if( (*loadedLibIter)->getName().find(objNameStr) != string::npos ) {
500 lib = (*loadedLibIter);
506 perr_printf("Failed to find loaded library for %s\n", objNameStr.c_str());
507 setLastError(err_internal, "Failed to find loaded library");
511 objSymReader = symreader_factory->openSymbolReader(lib->getName());
513 if( NULL == objSymReader ) {
514 perr_printf("Failed to open symbol reader for %s\n", objNameStr.c_str());
515 setLastError(err_internal, "Failed to open library for symbol reading");
519 symReaders.insert(make_pair(objNameStr, make_pair(lib, objSymReader)));
521 lib = symReaderIter->second.first;
522 objSymReader = symReaderIter->second.second;
526 Symbol_t lookupSym = objSymReader->getSymbolByName(string(symName));
528 if( !objSymReader->isValidSymbol(lookupSym) ) {
532 *symbolAddr = (psaddr_t)lib->offToAddress(
533 objSymReader->getSymbolOffset(lookupSym));
538 bool thread_db_process::post_create() {
539 if( !int_process::post_create() ) return false;
541 return initThreadDB();
544 bool thread_db_process::post_attach() {
545 if( !int_process::post_attach() ) return false;
547 return initThreadDB();
550 bool thread_db_process::getPostDestroyEvents(vector<Event::ptr> &events) {
551 unsigned oldSize = events.size();
553 int_threadPool::iterator i;
554 for(i = threadPool()->begin(); i != threadPool()->end(); ++i) {
555 thread_db_thread *tmpThread = static_cast<thread_db_thread *>(*i);
556 if( tmpThread->isDestroyed() ) {
557 pthrd_printf("Generating post-ThreadDestroy for %d/%d\n",
558 getPid(), tmpThread->getLWP());
560 Event::ptr destroyEvent = Event::ptr(new EventThreadDestroy(EventType::Post));
561 destroyEvent->setThread(tmpThread->thread());
562 destroyEvent->setProcess(proc());
563 events.push_back(destroyEvent);
567 return events.size() != oldSize;
570 void thread_db_process::addThreadDBHandlers(HandlerPool *hpool) {
571 static bool initialized = false;
572 static ThreadDBLibHandler *libHandler = NULL;
573 static ThreadDBCreateHandler *createHandler = NULL;
574 static ThreadDBDestroyHandler *destroyHandler = NULL;
576 libHandler = new ThreadDBLibHandler();
577 createHandler = new ThreadDBCreateHandler();
578 destroyHandler = new ThreadDBDestroyHandler();
581 hpool->addHandler(libHandler);
582 hpool->addHandler(createHandler);
583 hpool->addHandler(destroyHandler);
586 ThreadDBLibHandler::ThreadDBLibHandler() :
587 Handler("thread_db Library Handler")
591 ThreadDBLibHandler::~ThreadDBLibHandler()
595 bool ThreadDBLibHandler::handleEvent(Event::ptr ev) {
596 EventLibrary::const_ptr libEv = ev->getEventLibrary();
598 thread_db_process *proc = static_cast<thread_db_process *>(ev->getProcess()->llproc());
600 const set<Library::ptr> &addLibs = libEv->libsAdded();
602 set<Library::ptr>::iterator libIter;
603 for( libIter = addLibs.begin(); libIter != addLibs.end(); ++libIter ) {
604 if( proc->isSupportedThreadLib((*libIter)->getName()) ) {
605 pthrd_printf("Enabling thread_db support for pid %d\n",
607 if( !proc->initThreadDB() ) {
608 pthrd_printf("Failed to initialize thread_db for pid %d\n",
619 int ThreadDBLibHandler::getPriority() const {
620 return PostPlatformPriority;
623 void ThreadDBLibHandler::getEventTypesHandled(vector<EventType> &etypes) {
624 etypes.push_back(EventType(EventType::None, EventType::Library));
627 ThreadDBCreateHandler::ThreadDBCreateHandler() :
628 Handler("thread_db New Thread Handler")
632 ThreadDBCreateHandler::~ThreadDBCreateHandler()
636 bool ThreadDBCreateHandler::handleEvent(Event::ptr ev) {
637 EventNewThread::const_ptr threadEv = ev->getEventNewThread();
639 thread_db_thread *thread = static_cast<thread_db_thread *>(threadEv->getNewThread()->llthrd());
641 assert(thread && "Thread was not initialized before calling this handler");
643 // Because the new thread was created during a Breakpoint, it is already of
644 // out of sync with the user state
645 thread->desyncInternalState();
647 // Explicitly ignore errors here
648 thread->setEventReporting(true);
650 // The initial generator state is stopped
656 int ThreadDBCreateHandler::getPriority() const {
657 return PostPlatformPriority;
660 void ThreadDBCreateHandler::getEventTypesHandled(vector<EventType> &etypes) {
661 etypes.push_back(EventType(EventType::None, EventType::ThreadCreate));
664 ThreadDBDestroyHandler::ThreadDBDestroyHandler() :
665 Handler("thread_db Destroy Handler")
669 ThreadDBDestroyHandler::~ThreadDBDestroyHandler()
673 bool ThreadDBDestroyHandler::handleEvent(Event::ptr ev) {
674 thread_db_thread *thrd = static_cast<thread_db_thread *>(ev->getThread()->llthrd());
675 if( ev->getEventType().time() == EventType::Pre) {
676 pthrd_printf("Marking LWP %d destroyed\n", thrd->getLWP());
677 thrd->markDestroyed();
678 }else if( ev->getEventType().time() == EventType::Post) {
679 thrd->setGeneratorState(int_thread::exited);
681 // Need to make sure that the thread actually finishes at this point
688 int ThreadDBDestroyHandler::getPriority() const {
689 return PrePlatformPriority;
692 void ThreadDBDestroyHandler::getEventTypesHandled(vector<EventType> &etypes) {
693 etypes.push_back(EventType(EventType::Any, EventType::ThreadDestroy));
696 thread_db_thread::thread_db_thread(int_process *p, Dyninst::THR_ID t, Dyninst::LWP l)
697 : int_thread(p, t, l), threadHandle(NULL), destroyed(false)
701 thread_db_thread::~thread_db_thread()
706 bool thread_db_thread::initThreadHandle() {
707 if( NULL != threadHandle ) return true;
709 threadHandle = new td_thrhandle_t;
711 thread_db_process *lproc = static_cast<thread_db_process *>(proc_);
713 td_err_e errVal = td_ta_map_lwp2thr(lproc->getThreadDBAgent(),
715 if( TD_OK != errVal ) {
716 perr_printf("Failed to map LWP %d to thread_db thread: %s(%d)\n",
717 lwp, tdErr2Str(errVal), errVal);
718 setLastError(err_internal, "Failed to get thread_db thread handle");
725 Event::ptr thread_db_thread::getThreadEvent() {
726 if( !initThreadHandle() ) return Event::ptr();
728 td_event_msg_t eventMsg;
730 td_err_e errVal = td_thr_event_getmsg(threadHandle, &eventMsg);
732 if( TD_NOMSG == errVal ) {
733 pthrd_printf("No message available for LWP %d via thread_db\n", lwp);
737 if( TD_OK != errVal ) {
738 perr_printf("Failed to get thread event message: %s(%d)\n",
739 tdErr2Str(errVal), errVal);
740 setLastError(err_internal, "Failed to get thread event message");
744 return decodeThreadEvent(&eventMsg);
747 bool thread_db_thread::setEventReporting(bool on) {
748 if( !initThreadHandle() ) return false;
750 td_err_e errVal = td_thr_event_enable(threadHandle, (on ? 1 : 0 ));
752 if( TD_OK != errVal ) {
753 perr_printf("Failed to enable events for LWP %d: %s(%d)\n",
754 lwp, tdErr2Str(errVal), errVal);
755 setLastError(err_internal, "Failed to enable thread_db events");
759 pthrd_printf("Enabled thread_db events for LWP %d\n", lwp);
764 bool thread_db_thread::resume() {
765 if( !initThreadHandle() ) return false;
767 td_err_e errVal = td_thr_dbresume(threadHandle);
769 if( TD_OK != errVal ) {
770 perr_printf("Failed to resume LWP %d: %s(%d)\n",
771 lwp, tdErr2Str(errVal), errVal);
772 setLastError(err_internal, "Failed to resume LWP");
776 pthrd_printf("Resumed LWP %d via thread_db\n", lwp);
781 bool thread_db_thread::suspend() {
782 if( !initThreadHandle() ) return false;
784 td_err_e errVal = td_thr_dbsuspend(threadHandle);
786 if( TD_OK != errVal ) {
787 perr_printf("Failed to suspend LWP %d: %s(%d)\n",
788 lwp, tdErr2Str(errVal), errVal);
789 setLastError(err_internal, "Failed to suspend LWP");
793 pthrd_printf("Suspended LWP %d via thread_db\n", lwp);
798 void thread_db_thread::markDestroyed() {
802 bool thread_db_thread::isDestroyed() {