198944Sobrien/* Low level interface for debugging UnixWare user-mode threads for 298944Sobrien GDB, the GNU debugger. 398944Sobrien 498944Sobrien Copyright 1999, 2000, 2001 Free Software Foundation, Inc. 598944Sobrien Written by Nick Duffek <nsd@cygnus.com>. 698944Sobrien 798944Sobrien This file is part of GDB. 898944Sobrien 998944Sobrien This program is free software; you can redistribute it and/or modify 1098944Sobrien it under the terms of the GNU General Public License as published by 1198944Sobrien the Free Software Foundation; either version 2 of the License, or 1298944Sobrien (at your option) any later version. 1398944Sobrien 1498944Sobrien This program is distributed in the hope that it will be useful, 1598944Sobrien but WITHOUT ANY WARRANTY; without even the implied warranty of 1698944Sobrien MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1798944Sobrien GNU General Public License for more details. 1898944Sobrien 1998944Sobrien You should have received a copy of the GNU General Public License 2098944Sobrien along with this program; if not, write to the Free Software 2198944Sobrien Foundation, Inc., 59 Temple Place - Suite 330, 2298944Sobrien Boston, MA 02111-1307, USA. */ 2398944Sobrien 2498944Sobrien 2598944Sobrien/* Like many systems, UnixWare implements two classes of threads: 2698944Sobrien kernel-mode threads, which are scheduled by the kernel; and 2798944Sobrien user-mode threads, which are scheduled by a library. UnixWare 2898944Sobrien calls these two classes lightweight processes (LWPs) and threads, 2998944Sobrien respectively. 3098944Sobrien 3198944Sobrien This module deals with user-mode threads. It calls procfs_ops 3298944Sobrien functions to deal with LWPs and processes and core_ops functions to 3398944Sobrien deal with core files. 3498944Sobrien 3598944Sobrien As of this writing, the user-mode thread debugging interface is not 3698944Sobrien documented beyond the comments in <thread.h>. The following 3798944Sobrien description has been gleaned from experience and from information 3898944Sobrien provided by SCO. 3998944Sobrien 4098944Sobrien libthread.so, against which all UnixWare user-mode thread programs 4198944Sobrien link, provides a global thread_debug structure named _thr_debug. 4298944Sobrien It has three fields: 4398944Sobrien 4498944Sobrien (1) thr_map is a pointer to a pointer to an element of a 4598944Sobrien thread_map ring. A thread_map contains a single thread's id 4698944Sobrien number, state, LWP pointer, recent register state, and other 4798944Sobrien useful information. 4898944Sobrien 4998944Sobrien (2) thr_brk is a pointer to a stub function that libthread.so 5098944Sobrien calls when it changes a thread's state, e.g. by creating it, 5198944Sobrien switching it to an LWP, or causing it to exit. 5298944Sobrien 5398944Sobrien (3) thr_debug_on controls whether libthread.so calls thr_brk(). 5498944Sobrien 5598944Sobrien Debuggers are able to track thread activity by setting a private 5698944Sobrien breakpoint on thr_brk() and setting thr_debug_on to 1. 5798944Sobrien 5898944Sobrien thr_brk() receives two arguments: 5998944Sobrien 6098944Sobrien (1) a pointer to a thread_map describing the thread being 6198944Sobrien changed; and 6298944Sobrien 6398944Sobrien (2) an enum thread_change specifying one of the following 6498944Sobrien changes: 6598944Sobrien 6698944Sobrien invalid unknown 6798944Sobrien thread_create thread has just been created 6898944Sobrien thread_exit thread has just exited 6998944Sobrien switch_begin thread will be switched to an LWP 7098944Sobrien switch_complete thread has been switched to an LWP 7198944Sobrien cancel_complete thread wasn't switched to an LWP 7298944Sobrien thread_suspend thread has been thr_suspend()ed 7398944Sobrien thread_suspend_pending thread will be thr_suspend()ed 7498944Sobrien thread_continue thread has been thr_continue()d 7598944Sobrien 7698944Sobrien The thread_map argument to thr_brk() is NULL under the following 7798944Sobrien circumstances: 7898944Sobrien 7998944Sobrien - The main thread is being acted upon. The main thread always 8098944Sobrien has id 1, so its thread_map is easy to find by scanning through 8198944Sobrien _thr_debug.thr_map. 8298944Sobrien 8398944Sobrien - A "switch_complete" change is occurring, which means that the 8498944Sobrien thread specified in the most recent "switch_begin" change has 8598944Sobrien moved to an LWP. 8698944Sobrien 8798944Sobrien - A "cancel_complete" change is occurring, which means that the 8898944Sobrien thread specified in the most recent "switch_begin" change has 8998944Sobrien not moved to an LWP after all. 9098944Sobrien 9198944Sobrien - A spurious "switch_begin" change is occurring after a 9298944Sobrien "thread_exit" change. 9398944Sobrien 9498944Sobrien Between switch_begin and switch_complete or cancel_complete, the 9598944Sobrien affected thread's LWP pointer is not reliable. It is possible that 9698944Sobrien other parts of the thread's thread_map are also unreliable during 9798944Sobrien that time. */ 9898944Sobrien 9998944Sobrien 10098944Sobrien#include "defs.h" 10198944Sobrien#include "gdbthread.h" 10298944Sobrien#include "target.h" 10398944Sobrien#include "inferior.h" 10498944Sobrien#include "regcache.h" 10598944Sobrien#include <fcntl.h> 10698944Sobrien 10798944Sobrien/* <thread.h> includes <sys/priocntl.h>, which requires boolean_t from 10898944Sobrien <sys/types.h>, which doesn't typedef boolean_t with gcc. */ 10998944Sobrien 11098944Sobrien#define boolean_t int 11198944Sobrien#include <thread.h> 11298944Sobrien#undef boolean_t 11398944Sobrien 11498944Sobrien#include <synch.h> /* for UnixWare 2.x */ 11598944Sobrien 11698944Sobrien/* Prototypes for supply_gregset etc. */ 11798944Sobrien#include "gregset.h" 11898944Sobrien 119130803Smarcel/* Offset from SP to first arg on stack at first instruction of a 120130803Smarcel function. We provide a default here that's right for most, if not 121130803Smarcel all, targets that use this file. */ 122130803Smarcel 123130803Smarcel#ifndef SP_ARG0 124130803Smarcel#define SP_ARG0 (1 * 4) 125130803Smarcel#endif 126130803Smarcel 12798944Sobrien/* Whether to emit debugging output. */ 12898944Sobrien 12998944Sobrien#define DEBUG 0 13098944Sobrien 13198944Sobrien/* Default debugging output file, overridden by envvar UWTHR_DEBUG. */ 13298944Sobrien 13398944Sobrien#define DEBUG_FILE "/dev/tty" 13498944Sobrien 13598944Sobrien/* #if DEBUG, write string S to the debugging output channel. */ 13698944Sobrien 13798944Sobrien#if !DEBUG 13898944Sobrien# define DBG(fmt_and_args) 13998944Sobrien# define DBG2(fmt_and_args) 14098944Sobrien#else 14198944Sobrien# define DBG(fmt_and_args) dbg fmt_and_args 14298944Sobrien# define DBG2(fmt_and_args) 14398944Sobrien#endif 14498944Sobrien 14598944Sobrien/* Back end to CALL_BASE() and TRY_BASE(): evaluate CALL, then convert 14698944Sobrien inferior_ptid to a composite thread/process id. */ 14798944Sobrien 14898944Sobrien#define CALL_BASE_1(call) \ 14998944Sobriendo { \ 15098944Sobrien DBG2(("CALL_BASE(" #call ")")); \ 15198944Sobrien call; \ 15298944Sobrien do_cleanups (infpid_cleanup); \ 15398944Sobrien} while (0) 15498944Sobrien 15598944Sobrien/* If inferior_ptid can be converted to a composite lwp/process id, do so, 15698944Sobrien evaluate base_ops function CALL, and then convert inferior_ptid back to a 15798944Sobrien composite thread/process id. 15898944Sobrien 15998944Sobrien Otherwise, issue an error message and return nonlocally. */ 16098944Sobrien 16198944Sobrien#define CALL_BASE(call) \ 16298944Sobriendo { \ 16398944Sobrien if (!lwp_infpid ()) \ 16498944Sobrien error ("uw-thread: no lwp"); \ 16598944Sobrien CALL_BASE_1 (call); \ 16698944Sobrien} while (0) 16798944Sobrien 16898944Sobrien/* Like CALL_BASE(), but instead of returning nonlocally on error, set 16998944Sobrien *CALLED to whether the inferior_ptid conversion was successful. */ 17098944Sobrien 17198944Sobrien#define TRY_BASE(call, called) \ 17298944Sobriendo { \ 17398944Sobrien if ((*(called) = lwp_infpid ())) \ 17498944Sobrien CALL_BASE_1 (call); \ 17598944Sobrien} while (0) 17698944Sobrien 17798944Sobrien/* Information passed by thread_iter() to its callback parameter. */ 17898944Sobrien 17998944Sobrientypedef struct { 18098944Sobrien struct thread_map map; 18198944Sobrien __lwp_desc_t lwp; 18298944Sobrien CORE_ADDR mapp; 18398944Sobrien} iter_t; 18498944Sobrien 18598944Sobrien/* Private thread data for the thread_info struct. */ 18698944Sobrien 18798944Sobrienstruct private_thread_info { 18898944Sobrien int stable; /* 0 if libthread.so is modifying thread map */ 18998944Sobrien int thrid; /* thread id assigned by libthread.so */ 19098944Sobrien int lwpid; /* thread's lwp if .stable, 0 means no lwp */ 19198944Sobrien CORE_ADDR mapp; /* address of thread's map structure */ 19298944Sobrien}; 19398944Sobrien 19498944Sobrien 19598944Sobrien/* procfs.c's target-specific operations. */ 19698944Sobrienextern struct target_ops procfs_ops; 19798944Sobrien 19898944Sobrien/* Flag to prevent procfs.c from starting inferior processes. */ 19998944Sobrienextern int procfs_suppress_run; 20098944Sobrien 20198944Sobrien/* This module's target-specific operations. */ 20298944Sobrienstatic struct target_ops uw_thread_ops; 20398944Sobrien 20498944Sobrien/* Copy of the target over which uw_thread_ops is pushed. This is 20598944Sobrien more convenient than a pointer to procfs_ops or core_ops, because 20698944Sobrien they lack current_target's default callbacks. */ 20798944Sobrienstatic struct target_ops base_ops; 20898944Sobrien 20998944Sobrien/* Saved pointer to previous owner of target_new_objfile_hook. */ 21098944Sobrienstatic void (*target_new_objfile_chain)(struct objfile *); 21198944Sobrien 21298944Sobrien/* Whether we are debugging a user-space thread program. This isn't 21398944Sobrien set until after libthread.so is loaded by the program being 21498944Sobrien debugged. 21598944Sobrien 21698944Sobrien Except for module one-time intialization and where otherwise 21798944Sobrien documented, no functions in this module get called when 21898944Sobrien !uw_thread_active. */ 21998944Sobrienstatic int uw_thread_active; 22098944Sobrien 22198944Sobrien/* For efficiency, cache the addresses of libthread.so's _thr_debug 22298944Sobrien structure, its thr_brk stub function, and the main thread's map. */ 22398944Sobrienstatic CORE_ADDR thr_debug_addr; 22498944Sobrienstatic CORE_ADDR thr_brk_addr; 22598944Sobrienstatic CORE_ADDR thr_map_main; 22698944Sobrien 22798944Sobrien/* Remember the thread most recently marked as switching. Necessary because 22898944Sobrien libthread.so passes null map when calling stub with tc_*_complete. */ 22998944Sobrienstatic struct thread_info *switchto_thread; 23098944Sobrien 23198944Sobrien/* Cleanup chain for safely restoring inferior_ptid after CALL_BASE. */ 23298944Sobrienstatic struct cleanup *infpid_cleanup; 23398944Sobrien 23498944Sobrien 23598944Sobrien#if DEBUG 23698944Sobrien/* Helper function for DBG() macro: if printf-style FMT is non-null, format it 23798944Sobrien with args and display the result on the debugging output channel. */ 23898944Sobrien 23998944Sobrienstatic void 24098944Sobriendbg (char *fmt, ...) 24198944Sobrien{ 24298944Sobrien static int fd = -1, len; 24398944Sobrien va_list args; 24498944Sobrien char buf[1024]; 24598944Sobrien char *path; 24698944Sobrien 24798944Sobrien if (!fmt) 24898944Sobrien return; 24998944Sobrien 25098944Sobrien if (fd < 0) 25198944Sobrien { 25298944Sobrien path = getenv ("UWTHR_DEBUG"); 25398944Sobrien if (!path) 25498944Sobrien path = DEBUG_FILE; 25598944Sobrien if ((fd = open (path, O_WRONLY | O_CREAT | O_TRUNC, 0664)) < 0) 25698944Sobrien error ("can't open %s\n", path); 25798944Sobrien } 25898944Sobrien 25998944Sobrien va_start (args, fmt); 26098944Sobrien vsprintf (buf, fmt, args); 26198944Sobrien va_end (args); 26298944Sobrien 26398944Sobrien len = strlen (buf); 26498944Sobrien buf[len] = '\n'; 26598944Sobrien (void)write (fd, buf, len + 1); 26698944Sobrien} 26798944Sobrien 26898944Sobrien#if 0 26998944Sobrien/* Return a string representing composite PID's components. */ 27098944Sobrien 27198944Sobrienstatic char * 27298944Sobriendbgpid (ptid_t ptid) 27398944Sobrien{ 27498944Sobrien static char *buf, buf1[80], buf2[80]; 27598944Sobrien if (!buf || buf == buf2) 27698944Sobrien buf = buf1; 27798944Sobrien else 27898944Sobrien buf = buf2; 27998944Sobrien 28098944Sobrien if (PIDGET (ptid) <= 0) 28198944Sobrien sprintf (buf, "%d", PIDGET (ptid)); 28298944Sobrien else 28398944Sobrien sprintf (buf, "%s %ld/%d", ISTID (pid) ? "thr" : "lwp", 28498944Sobrien TIDGET (pid), PIDGET (pid)); 28598944Sobrien 28698944Sobrien return buf; 28798944Sobrien} 28898944Sobrien 28998944Sobrien/* Return a string representing thread state CHANGE. */ 29098944Sobrien 29198944Sobrienstatic char * 29298944Sobriendbgchange (enum thread_change change) 29398944Sobrien{ 29498944Sobrien switch (change) { 29598944Sobrien case tc_invalid: return "invalid"; 29698944Sobrien case tc_thread_create: return "thread_create"; 29798944Sobrien case tc_thread_exit: return "thread_exit"; 29898944Sobrien case tc_switch_begin: return "switch_begin"; 29998944Sobrien case tc_switch_complete: return "switch_complete"; 30098944Sobrien case tc_cancel_complete: return "cancel_complete"; 30198944Sobrien case tc_thread_suspend: return "thread_suspend"; 30298944Sobrien case tc_thread_suspend_pending: return "thread_suspend_pending"; 30398944Sobrien case tc_thread_continue: return "thread_continue"; 30498944Sobrien default: return "unknown"; 30598944Sobrien } 30698944Sobrien} 30798944Sobrien 30898944Sobrien/* Return a string representing thread STATE. */ 30998944Sobrien 31098944Sobrienstatic char * 31198944Sobriendbgstate (int state) 31298944Sobrien{ 31398944Sobrien switch (state) { 31498944Sobrien case TS_ONPROC: return "running"; 31598944Sobrien case TS_SLEEP: return "sleeping"; 31698944Sobrien case TS_RUNNABLE: return "runnable"; 31798944Sobrien case TS_ZOMBIE: return "zombie"; 31898944Sobrien case TS_SUSPENDED: return "suspended"; 31998944Sobrien#ifdef TS_FORK 32098944Sobrien case TS_FORK: return "forking"; 32198944Sobrien#endif 32298944Sobrien default: return "confused"; 32398944Sobrien } 32498944Sobrien} 32598944Sobrien#endif /* 0 */ 32698944Sobrien#endif /* DEBUG */ 32798944Sobrien 32898944Sobrien 32998944Sobrien/* Read the contents of _thr_debug into *DEBUGP. Return success. */ 33098944Sobrien 33198944Sobrienstatic int 33298944Sobrienread_thr_debug (struct thread_debug *debugp) 33398944Sobrien{ 33498944Sobrien return base_ops.to_xfer_memory (thr_debug_addr, (char *)debugp, 33598944Sobrien sizeof (*debugp), 0, NULL, &base_ops); 33698944Sobrien} 33798944Sobrien 33898944Sobrien/* Read into MAP the contents of the thread map at inferior process address 33998944Sobrien MAPP. Return success. */ 34098944Sobrien 34198944Sobrienstatic int 34298944Sobrienread_map (CORE_ADDR mapp, struct thread_map *map) 34398944Sobrien{ 34498944Sobrien return base_ops.to_xfer_memory ((CORE_ADDR)THR_MAP (mapp), (char *)map, 34598944Sobrien sizeof (*map), 0, NULL, &base_ops); 34698944Sobrien} 34798944Sobrien 34898944Sobrien/* Read into LWP the contents of the lwp decriptor at inferior process address 34998944Sobrien LWPP. Return success. */ 35098944Sobrien 35198944Sobrienstatic int 35298944Sobrienread_lwp (CORE_ADDR lwpp, __lwp_desc_t *lwp) 35398944Sobrien{ 35498944Sobrien return base_ops.to_xfer_memory (lwpp, (char *)lwp, 35598944Sobrien sizeof (*lwp), 0, NULL, &base_ops); 35698944Sobrien} 35798944Sobrien 35898944Sobrien/* Iterate through all user threads, applying FUNC(<map>, <lwp>, DATA) until 35998944Sobrien (a) FUNC returns nonzero, 36098944Sobrien (b) FUNC has been applied to all threads, or 36198944Sobrien (c) an error occurs, 36298944Sobrien where <map> is the thread's struct thread_map and <lwp> if non-null is the 36398944Sobrien thread's current __lwp_desc_t. 36498944Sobrien 36598944Sobrien If a call to FUNC returns nonzero, return that value; otherwise, return 0. */ 36698944Sobrien 36798944Sobrienstatic int 36898944Sobrienthread_iter (int (*func)(iter_t *, void *), void *data) 36998944Sobrien{ 37098944Sobrien struct thread_debug debug; 37198944Sobrien CORE_ADDR first, mapp; 37298944Sobrien iter_t iter; 37398944Sobrien int ret; 37498944Sobrien 37598944Sobrien if (!read_thr_debug (&debug)) 37698944Sobrien return 0; 37798944Sobrien if (!base_ops.to_xfer_memory ((CORE_ADDR)debug.thr_map, (char *)&mapp, 37898944Sobrien sizeof (mapp), 0, NULL, &base_ops)) 37998944Sobrien return 0; 38098944Sobrien if (!mapp) 38198944Sobrien return 0; 38298944Sobrien 38398944Sobrien for (first = mapp;;) 38498944Sobrien { 38598944Sobrien if (!read_map (mapp, &iter.map)) 38698944Sobrien return 0; 38798944Sobrien 38898944Sobrien if (iter.map.thr_lwpp) 38998944Sobrien if (!read_lwp ((CORE_ADDR)iter.map.thr_lwpp, &iter.lwp)) 39098944Sobrien return 0; 39198944Sobrien 39298944Sobrien iter.mapp = mapp; 39398944Sobrien if ((ret = func (&iter, data))) 39498944Sobrien return ret; 39598944Sobrien 39698944Sobrien mapp = (CORE_ADDR)iter.map.thr_next; 39798944Sobrien if (mapp == first) 39898944Sobrien return 0; 39998944Sobrien } 40098944Sobrien} 40198944Sobrien 40298944Sobrien/* Deactivate user-mode thread support. */ 40398944Sobrien 40498944Sobrienstatic void 40598944Sobriendeactivate_uw_thread (void) 40698944Sobrien{ 40798944Sobrien remove_thread_event_breakpoints (); 40898944Sobrien uw_thread_active = 0; 40998944Sobrien unpush_target (&uw_thread_ops); 41098944Sobrien} 41198944Sobrien 41298944Sobrien/* Return the composite lwp/process id corresponding to composite 41398944Sobrien id PID. If PID is a thread with no lwp, return 0. */ 41498944Sobrien 41598944Sobrienstatic ptid_t 41698944Sobrienthr_to_lwp (ptid_t ptid) 41798944Sobrien{ 41898944Sobrien struct thread_info *info; 41998944Sobrien ptid_t lid; 42098944Sobrien 42198944Sobrien if (!ISTID (ptid)) 42298944Sobrien lid = ptid; 42398944Sobrien else if (!(info = find_thread_pid (ptid))) 42498944Sobrien lid = null_ptid; 42598944Sobrien else if (!info->private->lwpid) 42698944Sobrien lid = null_ptid; 42798944Sobrien else 42898944Sobrien lid = MKLID (PIDGET (ptid), info->private->lwpid); 42998944Sobrien 43098944Sobrien DBG2((" thr_to_lwp(%s) = %s", dbgpid (pid), dbgpid (lid))); 43198944Sobrien return lid; 43298944Sobrien} 43398944Sobrien 43498944Sobrien/* find_thread_lwp() callback: return whether TP describes a thread 43598944Sobrien associated with lwp id DATA. */ 43698944Sobrien 43798944Sobrienstatic int 43898944Sobrienfind_thread_lwp_callback (struct thread_info *tp, void *data) 43998944Sobrien{ 44098944Sobrien int lwpid = (int)data; 44198944Sobrien 44298944Sobrien if (!ISTID (tp->ptid)) 44398944Sobrien return 0; 44498944Sobrien if (!tp->private->stable) 44598944Sobrien return 0; 44698944Sobrien if (lwpid != tp->private->lwpid) 44798944Sobrien return 0; 44898944Sobrien 44998944Sobrien /* match */ 45098944Sobrien return 1; 45198944Sobrien} 45298944Sobrien 45398944Sobrien/* If a thread is associated with lwp id LWPID, return the corresponding 45498944Sobrien member of the global thread list; otherwise, return null. */ 45598944Sobrien 45698944Sobrienstatic struct thread_info * 45798944Sobrienfind_thread_lwp (int lwpid) 45898944Sobrien{ 45998944Sobrien return iterate_over_threads (find_thread_lwp_callback, (void *)lwpid); 46098944Sobrien} 46198944Sobrien 46298944Sobrien/* Return the composite thread/process id corresponding to composite 46398944Sobrien id PID. If PID is an lwp with no thread, return PID. */ 46498944Sobrien 46598944Sobrienstatic ptid_t 46698944Sobrienlwp_to_thr (ptid_t ptid) 46798944Sobrien{ 46898944Sobrien struct thread_info *info; 46998944Sobrien int lwpid; 47098944Sobrien ptid_t tid = ptid; 47198944Sobrien 47298944Sobrien if (ISTID (ptid)) 47398944Sobrien goto done; 47498944Sobrien if (!(lwpid = LIDGET (ptid))) 47598944Sobrien goto done; 47698944Sobrien if (!(info = find_thread_lwp (lwpid))) 47798944Sobrien goto done; 47898944Sobrien tid = MKTID (PIDGET (ptid), info->private->thrid); 47998944Sobrien 48098944Sobrien done: 48198944Sobrien DBG2((ISTID (tid) ? NULL : "lwp_to_thr: no thr for %s", dbgpid (ptid))); 48298944Sobrien return tid; 48398944Sobrien} 48498944Sobrien 48598944Sobrien/* do_cleanups() callback: convert inferior_ptid to a composite 48698944Sobrien thread/process id after having made a procfs call. */ 48798944Sobrien 48898944Sobrienstatic void 48998944Sobrienthr_infpid (void *unused) 49098944Sobrien{ 49198944Sobrien ptid_t ptid = lwp_to_thr (inferior_ptid); 49298944Sobrien DBG2((" inferior_ptid from procfs: %s => %s", 49398944Sobrien dbgpid (inferior_ptid), dbgpid (ptid))); 49498944Sobrien inferior_ptid = ptid; 49598944Sobrien} 49698944Sobrien 49798944Sobrien/* If possible, convert inferior_ptid to a composite lwp/process id in 49898944Sobrien preparation for making a procfs call. Return success. */ 49998944Sobrien 50098944Sobrienstatic int 50198944Sobrienlwp_infpid (void) 50298944Sobrien{ 50398944Sobrien ptid_t ptid = thr_to_lwp (inferior_ptid); 50498944Sobrien DBG2((" inferior_ptid to procfs: %s => %s", 50598944Sobrien dbgpid (inferior_ptid), dbgpid (ptid))); 50698944Sobrien 50798944Sobrien if (ptid_equal (ptid, null_ptid)) 50898944Sobrien return 0; 50998944Sobrien 51098944Sobrien inferior_ptid = ptid; 51198944Sobrien infpid_cleanup = make_cleanup (thr_infpid, NULL); 51298944Sobrien return 1; 51398944Sobrien} 51498944Sobrien 51598944Sobrien/* Add to the global thread list a new user-mode thread with system id THRID, 51698944Sobrien lwp id LWPID, map address MAPP, and composite thread/process PID. */ 51798944Sobrien 51898944Sobrienstatic void 51998944Sobrienadd_thread_uw (int thrid, int lwpid, CORE_ADDR mapp, ptid_t ptid) 52098944Sobrien{ 52198944Sobrien struct thread_info *newthread; 52298944Sobrien 52398944Sobrien if ((newthread = add_thread (ptid)) == NULL) 52498944Sobrien error ("failed to create new thread structure"); 52598944Sobrien 52698944Sobrien newthread->private = xmalloc (sizeof (struct private_thread_info)); 52798944Sobrien newthread->private->stable = 1; 52898944Sobrien newthread->private->thrid = thrid; 52998944Sobrien newthread->private->lwpid = lwpid; 53098944Sobrien newthread->private->mapp = mapp; 53198944Sobrien 53298944Sobrien if (target_has_execution) 53398944Sobrien printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid)); 53498944Sobrien} 53598944Sobrien 53698944Sobrien/* notice_threads() and find_main() callback: if the thread list doesn't 53798944Sobrien already contain the thread described by ITER, add it if it's the main 53898944Sobrien thread or if !DATA. */ 53998944Sobrien 54098944Sobrienstatic int 54198944Sobriennotice_thread (iter_t *iter, void *data) 54298944Sobrien{ 54398944Sobrien int thrid = iter->map.thr_tid; 54498944Sobrien int lwpid = !iter->map.thr_lwpp ? 0 : iter->lwp.lwp_id; 54598944Sobrien ptid_t ptid = MKTID (PIDGET (inferior_ptid), thrid); 54698944Sobrien 54798944Sobrien if (!find_thread_pid (ptid) && (!data || thrid == 1)) 54898944Sobrien add_thread_uw (thrid, lwpid, iter->mapp, ptid); 54998944Sobrien 55098944Sobrien return 0; 55198944Sobrien} 55298944Sobrien 55398944Sobrien/* Add to the thread list any threads it doesn't already contain. */ 55498944Sobrien 55598944Sobrienstatic void 55698944Sobriennotice_threads (void) 55798944Sobrien{ 55898944Sobrien thread_iter (notice_thread, NULL); 55998944Sobrien} 56098944Sobrien 56198944Sobrien/* Return the address of the main thread's map. On error, return 0. */ 56298944Sobrien 56398944Sobrienstatic CORE_ADDR 56498944Sobrienfind_main (void) 56598944Sobrien{ 56698944Sobrien if (!thr_map_main) 56798944Sobrien { 56898944Sobrien struct thread_info *info; 56998944Sobrien thread_iter (notice_thread, (void *)1); 57098944Sobrien if ((info = find_thread_pid (MKTID (PIDGET (inferior_ptid), 1)))) 57198944Sobrien thr_map_main = info->private->mapp; 57298944Sobrien } 57398944Sobrien return thr_map_main; 57498944Sobrien} 57598944Sobrien 57698944Sobrien/* Attach to process specified by ARGS, then initialize for debugging it 57798944Sobrien and wait for the trace-trap that results from attaching. 57898944Sobrien 57998944Sobrien This function only gets called with uw_thread_active == 0. */ 58098944Sobrien 58198944Sobrienstatic void 58298944Sobrienuw_thread_attach (char *args, int from_tty) 58398944Sobrien{ 58498944Sobrien procfs_ops.to_attach (args, from_tty); 58598944Sobrien if (uw_thread_active) 58698944Sobrien thr_infpid (NULL); 58798944Sobrien} 58898944Sobrien 58998944Sobrien/* Detach from the process attached to by uw_thread_attach(). */ 59098944Sobrien 59198944Sobrienstatic void 59298944Sobrienuw_thread_detach (char *args, int from_tty) 59398944Sobrien{ 59498944Sobrien deactivate_uw_thread (); 59598944Sobrien base_ops.to_detach (args, from_tty); 59698944Sobrien} 59798944Sobrien 59898944Sobrien/* Tell the inferior process to continue running thread PID if >= 0 59998944Sobrien and all threads otherwise. */ 60098944Sobrien 60198944Sobrienstatic void 60298944Sobrienuw_thread_resume (ptid_t ptid, int step, enum target_signal signo) 60398944Sobrien{ 60498944Sobrien if (PIDGET (ptid) > 0) 60598944Sobrien { 60698944Sobrien ptid = thr_to_lwp (ptid); 60798944Sobrien if (ptid_equal (ptid, null_ptid)) 60898944Sobrien ptid = pid_to_ptid (-1); 60998944Sobrien } 61098944Sobrien 61198944Sobrien CALL_BASE (base_ops.to_resume (ptid, step, signo)); 61298944Sobrien} 61398944Sobrien 61498944Sobrien/* If the trap we just received from lwp PID was due to a breakpoint 61598944Sobrien on the libthread.so debugging stub, update this module's state 61698944Sobrien accordingly. */ 61798944Sobrien 61898944Sobrienstatic void 61998944Sobrienlibthread_stub (ptid_t ptid) 62098944Sobrien{ 62198944Sobrien CORE_ADDR sp, mapp, mapp_main; 62298944Sobrien enum thread_change change; 62398944Sobrien struct thread_map map; 62498944Sobrien __lwp_desc_t lwp; 62598944Sobrien int lwpid; 62698944Sobrien ptid_t tid = null_ptid; 62798944Sobrien struct thread_info *info; 62898944Sobrien 62998944Sobrien /* Check for stub breakpoint. */ 63098944Sobrien if (read_pc_pid (ptid) - DECR_PC_AFTER_BREAK != thr_brk_addr) 63198944Sobrien return; 63298944Sobrien 63398944Sobrien /* Retrieve stub args. */ 63498944Sobrien sp = read_register_pid (SP_REGNUM, ptid); 63598944Sobrien if (!base_ops.to_xfer_memory (sp + SP_ARG0, (char *)&mapp, 63698944Sobrien sizeof (mapp), 0, NULL, &base_ops)) 63798944Sobrien goto err; 63898944Sobrien if (!base_ops.to_xfer_memory (sp + SP_ARG0 + sizeof (mapp), (char *)&change, 63998944Sobrien sizeof (change), 0, NULL, &base_ops)) 64098944Sobrien goto err; 64198944Sobrien 64298944Sobrien /* create_inferior() may not have finished yet, so notice the main 64398944Sobrien thread to ensure that it's displayed first by add_thread(). */ 64498944Sobrien mapp_main = find_main (); 64598944Sobrien 64698944Sobrien /* Notice thread creation, deletion, or stability change. */ 64798944Sobrien switch (change) { 64898944Sobrien case tc_switch_begin: 64998944Sobrien if (!mapp) /* usually means main thread */ 65098944Sobrien mapp = mapp_main; 65198944Sobrien /* fall through */ 65298944Sobrien 65398944Sobrien case tc_thread_create: 65498944Sobrien case tc_thread_exit: 65598944Sobrien if (!mapp) 65698944Sobrien break; 65798944Sobrien if (!read_map (mapp, &map)) 65898944Sobrien goto err; 65998944Sobrien tid = MKTID (PIDGET (ptid), map.thr_tid); 66098944Sobrien 66198944Sobrien switch (change) { 66298944Sobrien case tc_thread_create: /* new thread */ 66398944Sobrien if (!map.thr_lwpp) 66498944Sobrien lwpid = 0; 66598944Sobrien else if (!read_lwp ((CORE_ADDR)map.thr_lwpp, &lwp)) 66698944Sobrien goto err; 66798944Sobrien else 66898944Sobrien lwpid = lwp.lwp_id; 66998944Sobrien add_thread_uw (map.thr_tid, lwpid, mapp, tid); 67098944Sobrien break; 67198944Sobrien 67298944Sobrien case tc_thread_exit: /* thread has exited */ 67398944Sobrien printf_unfiltered ("[Exited %s]\n", target_pid_to_str (tid)); 67498944Sobrien delete_thread (tid); 67598944Sobrien if (ptid_equal (tid, inferior_ptid)) 67698944Sobrien inferior_ptid = ptid; 67798944Sobrien break; 67898944Sobrien 67998944Sobrien case tc_switch_begin: /* lwp is switching threads */ 68098944Sobrien if (switchto_thread) 68198944Sobrien goto err; 68298944Sobrien if (!(switchto_thread = find_thread_pid (tid))) 68398944Sobrien goto err; 68498944Sobrien switchto_thread->private->stable = 0; 68598944Sobrien break; 68698944Sobrien 68798944Sobrien default: 68898944Sobrien break; 68998944Sobrien } 69098944Sobrien break; 69198944Sobrien 69298944Sobrien case tc_switch_complete: /* lwp has switched threads */ 69398944Sobrien case tc_cancel_complete: /* lwp didn't switch threads */ 69498944Sobrien if (!switchto_thread) 69598944Sobrien goto err; 69698944Sobrien 69798944Sobrien if (change == tc_switch_complete) 69898944Sobrien { 69998944Sobrien /* If switchto_thread is the main thread, then (a) the corresponding 70098944Sobrien tc_switch_begin probably received a null map argument and therefore 70198944Sobrien (b) it may have been a spurious switch following a tc_thread_exit. 70298944Sobrien 70398944Sobrien Therefore, explicitly query the thread's lwp before caching it in 70498944Sobrien its thread list entry. */ 70598944Sobrien 70698944Sobrien if (!read_map (switchto_thread->private->mapp, &map)) 70798944Sobrien goto err; 70898944Sobrien if (map.thr_lwpp) 70998944Sobrien { 71098944Sobrien if (!read_lwp ((CORE_ADDR)map.thr_lwpp, &lwp)) 71198944Sobrien goto err; 71298944Sobrien if ((info = find_thread_lwp (lwp.lwp_id))) 71398944Sobrien info->private->lwpid = 0; 71498944Sobrien switchto_thread->private->lwpid = lwp.lwp_id; 71598944Sobrien } 71698944Sobrien } 71798944Sobrien 71898944Sobrien switchto_thread->private->stable = 1; 71998944Sobrien switchto_thread = NULL; 72098944Sobrien break; 72198944Sobrien 72298944Sobrien case tc_invalid: 72398944Sobrien case tc_thread_suspend: 72498944Sobrien case tc_thread_suspend_pending: 72598944Sobrien case tc_thread_continue: 72698944Sobrien err: 72798944Sobrien DBG(("unexpected condition in libthread_stub()")); 72898944Sobrien break; 72998944Sobrien } 73098944Sobrien 73198944Sobrien DBG2(("libthread_stub(%s): %s %s %s", dbgpid (pid), dbgpid (tid), 73298944Sobrien dbgchange (change), tid ? dbgstate (map.thr_state) : "")); 73398944Sobrien} 73498944Sobrien 73598944Sobrien/* Wait for thread/lwp/process ID if >= 0 or for any thread otherwise. */ 73698944Sobrien 73798944Sobrienstatic ptid_t 73898944Sobrienuw_thread_wait (ptid_t ptid, struct target_waitstatus *status) 73998944Sobrien{ 74098944Sobrien if (PIDGET (ptid) > 0) 74198944Sobrien ptid = thr_to_lwp (ptid); 74298944Sobrien if (PIDGET (ptid) <= 0) 74398944Sobrien ptid = pid_to_ptid (-1); 74498944Sobrien 74598944Sobrien CALL_BASE (ptid = base_ops.to_wait (ptid, status)); 74698944Sobrien 74798944Sobrien if (status->kind == TARGET_WAITKIND_STOPPED && 74898944Sobrien status->value.sig == TARGET_SIGNAL_TRAP) 74998944Sobrien libthread_stub (ptid); 75098944Sobrien 75198944Sobrien return lwp_to_thr (ptid); 75298944Sobrien} 75398944Sobrien 75498944Sobrien/* Tell gdb about the registers in the thread/lwp/process specified by 75598944Sobrien inferior_ptid. */ 75698944Sobrien 75798944Sobrienstatic void 75898944Sobrienuw_thread_fetch_registers (int regno) 75998944Sobrien{ 76098944Sobrien int called; 76198944Sobrien struct thread_info *info; 76298944Sobrien struct thread_map map; 76398944Sobrien 76498944Sobrien TRY_BASE (base_ops.to_fetch_registers (regno), &called); 76598944Sobrien if (called) 76698944Sobrien return; 76798944Sobrien 76898944Sobrien if (!(info = find_thread_pid (inferior_ptid))) 76998944Sobrien return; 77098944Sobrien if (!read_map (info->private->mapp, &map)) 77198944Sobrien return; 77298944Sobrien 77398944Sobrien supply_gregset (&map.thr_ucontext.uc_mcontext.gregs); 77498944Sobrien supply_fpregset (&map.thr_ucontext.uc_mcontext.fpregs); 77598944Sobrien} 77698944Sobrien 77798944Sobrien/* Store gdb's current view of the register set into the thread/lwp/process 77898944Sobrien specified by inferior_ptid. */ 77998944Sobrien 78098944Sobrienstatic void 78198944Sobrienuw_thread_store_registers (int regno) 78298944Sobrien{ 78398944Sobrien CALL_BASE (base_ops.to_store_registers (regno)); 78498944Sobrien} 78598944Sobrien 78698944Sobrien/* Prepare to modify the registers array. */ 78798944Sobrien 78898944Sobrienstatic void 78998944Sobrienuw_thread_prepare_to_store (void) 79098944Sobrien{ 79198944Sobrien CALL_BASE (base_ops.to_prepare_to_store ()); 79298944Sobrien} 79398944Sobrien 79498944Sobrien/* Fork an inferior process and start debugging it. 79598944Sobrien 79698944Sobrien This function only gets called with uw_thread_active == 0. */ 79798944Sobrien 79898944Sobrienstatic void 79998944Sobrienuw_thread_create_inferior (char *exec_file, char *allargs, char **env) 80098944Sobrien{ 80198944Sobrien if (uw_thread_active) 80298944Sobrien deactivate_uw_thread (); 80398944Sobrien 80498944Sobrien procfs_ops.to_create_inferior (exec_file, allargs, env); 80598944Sobrien if (uw_thread_active) 80698944Sobrien { 80798944Sobrien find_main (); 80898944Sobrien thr_infpid (NULL); 80998944Sobrien } 81098944Sobrien} 81198944Sobrien 81298944Sobrien/* Kill and forget about the inferior process. */ 81398944Sobrien 81498944Sobrienstatic void 81598944Sobrienuw_thread_kill (void) 81698944Sobrien{ 81798944Sobrien base_ops.to_kill (); 81898944Sobrien} 81998944Sobrien 82098944Sobrien/* Clean up after the inferior exits. */ 82198944Sobrien 82298944Sobrienstatic void 82398944Sobrienuw_thread_mourn_inferior (void) 82498944Sobrien{ 82598944Sobrien deactivate_uw_thread (); 82698944Sobrien base_ops.to_mourn_inferior (); 82798944Sobrien} 82898944Sobrien 82998944Sobrien/* Return whether this module can attach to and run processes. 83098944Sobrien 83198944Sobrien This function only gets called with uw_thread_active == 0. */ 83298944Sobrien 83398944Sobrienstatic int 83498944Sobrienuw_thread_can_run (void) 83598944Sobrien{ 83698944Sobrien return procfs_suppress_run; 83798944Sobrien} 83898944Sobrien 83998944Sobrien/* Return whether thread PID is still valid. */ 84098944Sobrien 84198944Sobrienstatic int 84298944Sobrienuw_thread_alive (ptid_t ptid) 84398944Sobrien{ 84498944Sobrien if (!ISTID (ptid)) 84598944Sobrien return base_ops.to_thread_alive (ptid); 84698944Sobrien 84798944Sobrien /* If it's in the thread list, it's valid, because otherwise 84898944Sobrien libthread_stub() would have deleted it. */ 84998944Sobrien return in_thread_list (ptid); 85098944Sobrien} 85198944Sobrien 85298944Sobrien/* Add to the thread list any threads and lwps it doesn't already contain. */ 85398944Sobrien 85498944Sobrienstatic void 85598944Sobrienuw_thread_find_new_threads (void) 85698944Sobrien{ 85798944Sobrien CALL_BASE (if (base_ops.to_find_new_threads) 85898944Sobrien base_ops.to_find_new_threads ()); 85998944Sobrien notice_threads (); 86098944Sobrien} 86198944Sobrien 86298944Sobrien/* Return a string for pretty-printing PID in "info threads" output. 86398944Sobrien This may be called by either procfs.c or by generic gdb. */ 86498944Sobrien 86598944Sobrienstatic char * 86698944Sobrienuw_thread_pid_to_str (ptid_t ptid) 86798944Sobrien{ 86898944Sobrien#define FMT "Thread %ld" 86998944Sobrien static char buf[sizeof (FMT) + 3 * sizeof (long)]; 87098944Sobrien 87198944Sobrien if (!ISTID (ptid)) 87298944Sobrien /* core_ops says "process foo", so call procfs_ops explicitly. */ 87398944Sobrien return procfs_ops.to_pid_to_str (ptid); 87498944Sobrien 87598944Sobrien sprintf (buf, FMT, TIDGET (ptid)); 87698944Sobrien#undef FMT 87798944Sobrien return buf; 87898944Sobrien} 87998944Sobrien 88098944Sobrien/* Return a string displaying INFO state information in "info threads" 88198944Sobrien output. */ 88298944Sobrien 88398944Sobrienstatic char * 88498944Sobrienuw_extra_thread_info (struct thread_info *info) 88598944Sobrien{ 88698944Sobrien static char buf[80]; 88798944Sobrien struct thread_map map; 88898944Sobrien __lwp_desc_t lwp; 88998944Sobrien int lwpid; 89098944Sobrien char *name; 89198944Sobrien 89298944Sobrien if (!ISTID (info->ptid)) 89398944Sobrien return NULL; 89498944Sobrien 89598944Sobrien if (!info->private->stable) 89698944Sobrien return "switching"; 89798944Sobrien 89898944Sobrien if (!read_map (info->private->mapp, &map)) 89998944Sobrien return NULL; 90098944Sobrien 90198944Sobrien if (!map.thr_lwpp || !read_lwp ((CORE_ADDR)map.thr_lwpp, &lwp)) 90298944Sobrien lwpid = 0; 90398944Sobrien else 90498944Sobrien lwpid = lwp.lwp_id; 90598944Sobrien 90698944Sobrien switch (map.thr_state) { 90798944Sobrien case TS_ONPROC: name = "running"; break; 90898944Sobrien case TS_SLEEP: name = "sleeping"; break; 90998944Sobrien case TS_RUNNABLE: name = "runnable"; break; 91098944Sobrien case TS_ZOMBIE: name = "zombie"; break; 91198944Sobrien case TS_SUSPENDED: name = "suspended"; break; 91298944Sobrien#ifdef TS_FORK 91398944Sobrien case TS_FORK: name = "forking"; break; 91498944Sobrien#endif 91598944Sobrien default: name = "confused"; break; 91698944Sobrien } 91798944Sobrien 91898944Sobrien if (!lwpid) 91998944Sobrien return name; 92098944Sobrien 92198944Sobrien sprintf (buf, "%s, LWP %d", name, lwpid); 92298944Sobrien return buf; 92398944Sobrien} 92498944Sobrien 92598944Sobrien/* Check whether libthread.so has just been loaded, and if so, try to 92698944Sobrien initialize user-space thread debugging support. 92798944Sobrien 92898944Sobrien libthread.so loading happens while (a) an inferior process is being 92998944Sobrien started by procfs and (b) a core image is being loaded. 93098944Sobrien 93198944Sobrien This function often gets called with uw_thread_active == 0. */ 93298944Sobrien 93398944Sobrienstatic void 93498944Sobrienlibthread_init (void) 93598944Sobrien{ 93698944Sobrien struct minimal_symbol *ms; 93798944Sobrien struct thread_debug debug; 93898944Sobrien CORE_ADDR onp; 93998944Sobrien struct breakpoint *b; 94098944Sobrien int one = 1; 94198944Sobrien 94298944Sobrien /* Don't initialize twice. */ 94398944Sobrien if (uw_thread_active) 94498944Sobrien return; 94598944Sobrien 94698944Sobrien /* Check whether libthread.so has been loaded. */ 94798944Sobrien if (!(ms = lookup_minimal_symbol ("_thr_debug", NULL, NULL))) 94898944Sobrien return; 94998944Sobrien 95098944Sobrien /* Cache _thr_debug's address. */ 95198944Sobrien if (!(thr_debug_addr = SYMBOL_VALUE_ADDRESS (ms))) 95298944Sobrien return; 95398944Sobrien 95498944Sobrien /* Initialize base_ops.to_xfer_memory(). */ 95598944Sobrien base_ops = current_target; 95698944Sobrien 95798944Sobrien /* Load _thr_debug's current contents. */ 95898944Sobrien if (!read_thr_debug (&debug)) 95998944Sobrien return; 96098944Sobrien 96198944Sobrien /* User code (e.g. my test programs) may dereference _thr_debug, 96298944Sobrien making it availble to GDB before shared libs are loaded. */ 96398944Sobrien if (!debug.thr_map) 96498944Sobrien return; 96598944Sobrien 96698944Sobrien /* libthread.so has been loaded, and the current_target should now 96798944Sobrien reflect core_ops or procfs_ops. */ 96898944Sobrien push_target (&uw_thread_ops); /* must precede notice_threads() */ 96998944Sobrien uw_thread_active = 1; 97098944Sobrien 97198944Sobrien if (!target_has_execution) 97298944Sobrien 97398944Sobrien /* Locate threads in core file. */ 97498944Sobrien notice_threads (); 97598944Sobrien 97698944Sobrien else 97798944Sobrien { 97898944Sobrien /* Set a breakpoint on the stub function provided by libthread.so. */ 97998944Sobrien thr_brk_addr = (CORE_ADDR)debug.thr_brk; 98098944Sobrien if (!(b = create_thread_event_breakpoint (thr_brk_addr))) 98198944Sobrien goto err; 98298944Sobrien 98398944Sobrien /* Activate the stub function. */ 98498944Sobrien onp = (CORE_ADDR)&((struct thread_debug *)thr_debug_addr)->thr_debug_on; 98598944Sobrien if (!base_ops.to_xfer_memory ((CORE_ADDR)onp, (char *)&one, 98698944Sobrien sizeof (one), 1, NULL, &base_ops)) 98798944Sobrien { 98898944Sobrien delete_breakpoint (b); 98998944Sobrien goto err; 99098944Sobrien } 99198944Sobrien 99298944Sobrien /* Prepare for finding the main thread, which doesn't yet exist. */ 99398944Sobrien thr_map_main = 0; 99498944Sobrien } 99598944Sobrien 99698944Sobrien return; 99798944Sobrien 99898944Sobrien err: 99998944Sobrien warning ("uw-thread: unable to initialize user-mode thread debugging\n"); 100098944Sobrien deactivate_uw_thread (); 100198944Sobrien} 100298944Sobrien 100398944Sobrien/* target_new_objfile_hook callback. 100498944Sobrien 100598944Sobrien If OBJFILE is non-null, check whether libthread.so was just loaded, 100698944Sobrien and if so, prepare for user-mode thread debugging. 100798944Sobrien 100898944Sobrien If OBJFILE is null, libthread.so has gone away, so stop debugging 100998944Sobrien user-mode threads. 101098944Sobrien 101198944Sobrien This function often gets called with uw_thread_active == 0. */ 101298944Sobrien 101398944Sobrienstatic void 101498944Sobrienuw_thread_new_objfile (struct objfile *objfile) 101598944Sobrien{ 101698944Sobrien if (objfile) 101798944Sobrien libthread_init (); 101898944Sobrien 101998944Sobrien else if (uw_thread_active) 102098944Sobrien deactivate_uw_thread (); 102198944Sobrien 102298944Sobrien if (target_new_objfile_chain) 102398944Sobrien target_new_objfile_chain (objfile); 102498944Sobrien} 102598944Sobrien 102698944Sobrien/* Initialize uw_thread_ops. */ 102798944Sobrien 102898944Sobrienstatic void 102998944Sobrieninit_uw_thread_ops (void) 103098944Sobrien{ 103198944Sobrien uw_thread_ops.to_shortname = "unixware-threads"; 103298944Sobrien uw_thread_ops.to_longname = "UnixWare threads and pthread."; 103398944Sobrien uw_thread_ops.to_doc = "UnixWare threads and pthread support."; 103498944Sobrien uw_thread_ops.to_attach = uw_thread_attach; 103598944Sobrien uw_thread_ops.to_detach = uw_thread_detach; 103698944Sobrien uw_thread_ops.to_resume = uw_thread_resume; 103798944Sobrien uw_thread_ops.to_wait = uw_thread_wait; 103898944Sobrien uw_thread_ops.to_fetch_registers = uw_thread_fetch_registers; 103998944Sobrien uw_thread_ops.to_store_registers = uw_thread_store_registers; 104098944Sobrien uw_thread_ops.to_prepare_to_store = uw_thread_prepare_to_store; 104198944Sobrien uw_thread_ops.to_create_inferior = uw_thread_create_inferior; 104298944Sobrien uw_thread_ops.to_kill = uw_thread_kill; 104398944Sobrien uw_thread_ops.to_mourn_inferior = uw_thread_mourn_inferior; 104498944Sobrien uw_thread_ops.to_can_run = uw_thread_can_run; 104598944Sobrien uw_thread_ops.to_thread_alive = uw_thread_alive; 104698944Sobrien uw_thread_ops.to_find_new_threads = uw_thread_find_new_threads; 104798944Sobrien uw_thread_ops.to_pid_to_str = uw_thread_pid_to_str; 104898944Sobrien uw_thread_ops.to_extra_thread_info = uw_extra_thread_info; 104998944Sobrien uw_thread_ops.to_stratum = thread_stratum; 105098944Sobrien uw_thread_ops.to_magic = OPS_MAGIC; 105198944Sobrien} 105298944Sobrien 105398944Sobrien/* Module startup initialization function, automagically called by 105498944Sobrien init.c. */ 105598944Sobrien 105698944Sobrienvoid 105798944Sobrien_initialize_uw_thread (void) 105898944Sobrien{ 105998944Sobrien init_uw_thread_ops (); 106098944Sobrien add_target (&uw_thread_ops); 106198944Sobrien 106298944Sobrien procfs_suppress_run = 1; 106398944Sobrien 106498944Sobrien /* Notice when libthread.so gets loaded. */ 106598944Sobrien target_new_objfile_chain = target_new_objfile_hook; 106698944Sobrien target_new_objfile_hook = uw_thread_new_objfile; 106798944Sobrien} 1068