| /* Low level Unix child interface to ttrace, for GDB when running under HP-UX. |
| Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, |
| 1999, 2000, 2001, 2003 |
| Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include "defs.h" |
| #include "frame.h" |
| #include "inferior.h" |
| #include "target.h" |
| #include "gdb_string.h" |
| #include "gdb_wait.h" |
| #include "command.h" |
| |
| /* We need pstat functionality so that we can get the exec file |
| for a process we attach to. |
| |
| According to HP, we should use the 64bit interfaces, so we |
| define _PSTAT64 to achieve this. */ |
| #define _PSTAT64 |
| #include <sys/pstat.h> |
| |
| /* Some hackery to work around a use of the #define name NO_FLAGS |
| * in both gdb and HPUX (bfd.h and /usr/include/machine/vmparam.h). |
| */ |
| #ifdef NO_FLAGS |
| #define INFTTRACE_TEMP_HACK NO_FLAGS |
| #undef NO_FLAGS |
| #endif |
| |
| #ifdef USG |
| #include <sys/types.h> |
| #endif |
| |
| #include <sys/param.h> |
| #include <sys/dir.h> |
| #include <signal.h> |
| #include <sys/ioctl.h> |
| |
| #include <sys/ttrace.h> |
| #include <sys/mman.h> |
| |
| #ifndef NO_PTRACE_H |
| #ifdef PTRACE_IN_WRONG_PLACE |
| #include <ptrace.h> |
| #else |
| #include <sys/ptrace.h> |
| #endif |
| #endif /* NO_PTRACE_H */ |
| |
| /* Second half of the hackery above. Non-ANSI C, so |
| * we can't use "#error", alas. |
| */ |
| #ifdef NO_FLAGS |
| #if (NO_FLAGS != INFTTRACE_TEMP_HACK ) |
| /* #error "Hackery to remove warning didn't work right" */ |
| #else |
| /* Ok, new def'n of NO_FLAGS is same as old one; no action needed. */ |
| #endif |
| #else |
| /* #error "Didn't get expected re-definition of NO_FLAGS" */ |
| #define NO_FLAGS INFTTRACE_TEMP_HACK |
| #endif |
| |
| #if !defined (PT_SETTRC) |
| #define PT_SETTRC 0 /* Make process traceable by parent */ |
| #endif |
| #if !defined (PT_READ_I) |
| #define PT_READ_I 1 /* Read word from text space */ |
| #endif |
| #if !defined (PT_READ_D) |
| #define PT_READ_D 2 /* Read word from data space */ |
| #endif |
| #if !defined (PT_READ_U) |
| #define PT_READ_U 3 /* Read word from kernel user struct */ |
| #endif |
| #if !defined (PT_WRITE_I) |
| #define PT_WRITE_I 4 /* Write word to text space */ |
| #endif |
| #if !defined (PT_WRITE_D) |
| #define PT_WRITE_D 5 /* Write word to data space */ |
| #endif |
| #if !defined (PT_WRITE_U) |
| #define PT_WRITE_U 6 /* Write word to kernel user struct */ |
| #endif |
| #if !defined (PT_CONTINUE) |
| #define PT_CONTINUE 7 /* Continue after signal */ |
| #endif |
| #if !defined (PT_STEP) |
| #define PT_STEP 9 /* Set flag for single stepping */ |
| #endif |
| #if !defined (PT_KILL) |
| #define PT_KILL 8 /* Send child a SIGKILL signal */ |
| #endif |
| |
| #ifndef PT_ATTACH |
| #define PT_ATTACH PTRACE_ATTACH |
| #endif |
| #ifndef PT_DETACH |
| #define PT_DETACH PTRACE_DETACH |
| #endif |
| |
| #include "gdbcore.h" |
| #ifndef NO_SYS_FILE |
| #include <sys/file.h> |
| #endif |
| |
| /* This semaphore is used to coordinate the child and parent processes |
| after a fork(), and before an exec() by the child. See parent_attach_all |
| for details. |
| */ |
| typedef struct |
| { |
| int parent_channel[2]; /* Parent "talks" to [1], child "listens" to [0] */ |
| int child_channel[2]; /* Child "talks" to [1], parent "listens" to [0] */ |
| } |
| startup_semaphore_t; |
| |
| #define SEM_TALK (1) |
| #define SEM_LISTEN (0) |
| |
| static startup_semaphore_t startup_semaphore; |
| |
| /* See can_touch_threads_of_process for details. */ |
| static int vforking_child_pid = 0; |
| static int vfork_in_flight = 0; |
| |
| /* To support PREPARE_TO_PROCEED (hppa_prepare_to_proceed). |
| */ |
| static pid_t old_gdb_pid = 0; |
| static pid_t reported_pid = 0; |
| static int reported_bpt = 0; |
| |
| /* 1 if ok as results of a ttrace or ttrace_wait call, 0 otherwise. |
| */ |
| #define TT_OK( _status, _errno ) \ |
| (((_status) == 1) && ((_errno) == 0)) |
| |
| #define TTRACE_ARG_TYPE uint64_t |
| |
| /* When supplied as the "addr" operand, ttrace interprets this |
| to mean, "from the current address". |
| */ |
| #define TT_USE_CURRENT_PC ((TTRACE_ARG_TYPE) TT_NOPC) |
| |
| /* When supplied as the "addr", "data" or "addr2" operand for most |
| requests, ttrace interprets this to mean, "pay no heed to this |
| argument". |
| */ |
| #define TT_NIL ((TTRACE_ARG_TYPE) TT_NULLARG) |
| |
| /* This is capable of holding the value of a 32-bit register. The |
| value is always left-aligned in the buffer; i.e., [0] contains |
| the most-significant byte of the register's value, and [sizeof(reg)] |
| contains the least-significant value. |
| |
| ??rehrauer: Yes, this assumes that an int is 32-bits on HP-UX, and |
| that registers are 32-bits on HP-UX. The latter assumption changes |
| with PA2.0. |
| */ |
| typedef int register_value_t; |
| |
| /******************************************************************** |
| |
| How this works: |
| |
| 1. Thread numbers |
| |
| The rest of GDB sees threads as being things with different |
| "pid" (process id) values. See "thread.c" for details. The |
| separate threads will be seen and reacted to if infttrace passes |
| back different pid values (for _events_). See wait_for_inferior |
| in inftarg.c. |
| |
| So infttrace is going to use thread ids externally, pretending |
| they are process ids, and keep track internally so that it can |
| use the real process id (and thread id) when calling ttrace. |
| |
| The data structure that supports this is a linked list of the |
| current threads. Since at some date infttrace will have to |
| deal with multiple processes, each list element records its |
| corresponding pid, rather than having a single global. |
| |
| Note that the list is only approximately current; that's ok, as |
| it's up to date when we need it (we hope!). Also, it can contain |
| dead threads, as there's no harm if it does. |
| |
| The approach taken here is to bury the translation from external |
| to internal inside "call_ttrace" and a few other places. |
| |
| There are some wrinkles: |
| |
| o When GDB forks itself to create the debug target process, |
| there's only a pid of 0 around in the child, so the |
| TT_PROC_SETTRC operation uses a more direct call to ttrace; |
| Similiarly, the initial setting of the event mask happens |
| early as well, and so is also special-cased, and an attach |
| uses a real pid; |
| |
| o We define an unthreaded application as having a "pseudo" |
| thread; |
| |
| o To keep from confusing the rest of GDB, we don't switch |
| the PID for the pseudo thread to a TID. A table will help: |
| |
| Rest of GDB sees these PIDs: pid tid1 tid2 tid3 ... |
| |
| Our thread list stores: pid pid pid pid ... |
| tid0 tid1 tid2 tid3 |
| |
| Ttrace sees these TIDS: tid0 tid1 tid2 tid3 ... |
| |
| Both pid and tid0 will map to tid0, as there are infttrace.c-internal |
| calls to ttrace using tid0. |
| |
| 2. Step and Continue |
| |
| Since we're implementing the "stop the world" model, sub-model |
| "other threads run during step", we have some stuff to do: |
| |
| o User steps require continuing all threads other than the |
| one the user is stepping; |
| |
| o Internal debugger steps (such as over a breakpoint or watchpoint, |
| but not out of a library load thunk) require stepping only |
| the selected thread; this means that we have to report the |
| step finish on that thread, which can lead to complications; |
| |
| o When a thread is created, it is created running, rather |
| than stopped--so we have to stop it. |
| |
| The OS doesn't guarantee the stopped thread list will be stable, |
| no does it guarantee where on the stopped thread list a thread |
| that is single-stepped will wind up: it's possible that it will |
| be off the list for a while, it's possible the step will complete |
| and it will be re-posted to the end... |
| |
| This means we have to scan the stopped thread list, build up |
| a work-list, and then run down the work list; we can't do the |
| step/continue during the scan. |
| |
| 3. Buffering events |
| |
| Then there's the issue of waiting for an event. We do this by |
| noticing how many events are reported at the end of each wait. |
| From then on, we "fake" all resumes and steps, returning instantly, |
| and don't do another wait. Once all pending events are reported, |
| we can really resume again. |
| |
| To keep this hidden, all the routines which know about tids and |
| pids or real events and simulated ones are static (file-local). |
| |
| This code can make lots of calls to ttrace, in particular it |
| can spin down the list of thread states more than once. If this |
| becomes a performance hit, the spin could be done once and the |
| various "tsp" blocks saved, keeping all later spins in this |
| process. |
| |
| The O/S doesn't promise to keep the list straight, and so we must |
| re-scan a lot. By observation, it looks like a single-step/wait |
| puts the stepped thread at the end of the list but doesn't change |
| it otherwise. |
| |
| **************************************************************** |
| */ |
| |
| /* Uncomment these to turn on various debugging output */ |
| /* #define THREAD_DEBUG */ |
| /* #define WAIT_BUFFER_DEBUG */ |
| /* #define PARANOIA */ |
| |
| |
| #define INFTTRACE_ALL_THREADS (-1) |
| #define INFTTRACE_STEP (1) |
| #define INFTTRACE_CONTINUE (0) |
| |
| /* FIX: this is used in inftarg.c/child_wait, in a hack. |
| */ |
| extern int not_same_real_pid; |
| |
| /* This is used to count buffered events. |
| */ |
| static unsigned int more_events_left = 0; |
| |
| /* Process state. |
| */ |
| typedef enum process_state_enum |
| { |
| STOPPED, |
| FAKE_STEPPING, |
| FAKE_CONTINUE, /* For later use */ |
| RUNNING, |
| FORKING, |
| VFORKING |
| } |
| process_state_t; |
| |
| static process_state_t process_state = STOPPED; |
| |
| /* User-specified stepping modality. |
| */ |
| typedef enum stepping_mode_enum |
| { |
| DO_DEFAULT, /* ...which is a continue! */ |
| DO_STEP, |
| DO_CONTINUE |
| } |
| stepping_mode_t; |
| |
| /* Action to take on an attach, depends on |
| * what kind (user command, fork, vfork). |
| * |
| * At the moment, this is either: |
| * |
| * o continue with a SIGTRAP signal, or |
| * |
| * o leave stopped. |
| */ |
| typedef enum attach_continue_enum |
| { |
| DO_ATTACH_CONTINUE, |
| DONT_ATTACH_CONTINUE |
| } |
| attach_continue_t; |
| |
| /* This flag is true if we are doing a step-over-bpt |
| * with buffered events. We will have to be sure to |
| * report the right thread, as otherwise the spaghetti |
| * code in "infrun.c/wait_for_inferior" will get |
| * confused. |
| */ |
| static int doing_fake_step = 0; |
| static lwpid_t fake_step_tid = 0; |
| |
| |
| /**************************************************** |
| * Thread information structure routines and types. * |
| **************************************************** |
| */ |
| typedef |
| struct thread_info_struct |
| { |
| int am_pseudo; /* This is a pseudo-thread for the process. */ |
| int pid; /* Process ID */ |
| lwpid_t tid; /* Thread ID */ |
| int handled; /* 1 if a buffered event was handled. */ |
| int seen; /* 1 if this thread was seen on a traverse. */ |
| int terminated; /* 1 if thread has terminated. */ |
| int have_signal; /* 1 if signal to be sent */ |
| enum target_signal signal_value; /* Signal to send */ |
| int have_start; /* 1 if alternate starting address */ |
| stepping_mode_t stepping_mode; /* Whether to step or continue */ |
| CORE_ADDR start; /* Where to start */ |
| int have_state; /* 1 if the event state has been set */ |
| ttstate_t last_stop_state; /* The most recently-waited event for this thread. */ |
| struct thread_info_struct |
| *next; /* All threads are linked via this field. */ |
| struct thread_info_struct |
| *next_pseudo; /* All pseudo-threads are linked via this field. */ |
| } |
| thread_info; |
| |
| typedef |
| struct thread_info_header_struct |
| { |
| int count; |
| thread_info *head; |
| thread_info *head_pseudo; |
| |
| } |
| thread_info_header; |
| |
| static thread_info_header thread_head = |
| {0, NULL, NULL}; |
| static thread_info_header deleted_threads = |
| {0, NULL, NULL}; |
| |
| static ptid_t saved_real_ptid; |
| |
| |
| /************************************************* |
| * Debugging support functions * |
| ************************************************* |
| */ |
| CORE_ADDR |
| get_raw_pc (lwpid_t ttid) |
| { |
| unsigned long pc_val; |
| int offset; |
| int res; |
| |
| offset = register_addr (PC_REGNUM, U_REGS_OFFSET); |
| res = read_from_register_save_state ( |
| ttid, |
| (TTRACE_ARG_TYPE) offset, |
| (char *) &pc_val, |
| sizeof (pc_val)); |
| if (res <= 0) |
| { |
| return (CORE_ADDR) pc_val; |
| } |
| else |
| { |
| return (CORE_ADDR) 0; |
| } |
| } |
| |
| static char * |
| get_printable_name_of_stepping_mode (stepping_mode_t mode) |
| { |
| switch (mode) |
| { |
| case DO_DEFAULT: |
| return "DO_DEFAULT"; |
| case DO_STEP: |
| return "DO_STEP"; |
| case DO_CONTINUE: |
| return "DO_CONTINUE"; |
| default: |
| return "?unknown mode?"; |
| } |
| } |
| |
| /* This function returns a pointer to a string describing the |
| * ttrace event being reported. |
| */ |
| char * |
| get_printable_name_of_ttrace_event (ttevents_t event) |
| { |
| /* This enumeration is "gappy", so don't use a table. */ |
| switch (event) |
| { |
| |
| case TTEVT_NONE: |
| return "TTEVT_NONE"; |
| case TTEVT_SIGNAL: |
| return "TTEVT_SIGNAL"; |
| case TTEVT_FORK: |
| return "TTEVT_FORK"; |
| case TTEVT_EXEC: |
| return "TTEVT_EXEC"; |
| case TTEVT_EXIT: |
| return "TTEVT_EXIT"; |
| case TTEVT_VFORK: |
| return "TTEVT_VFORK"; |
| case TTEVT_SYSCALL_RETURN: |
| return "TTEVT_SYSCALL_RETURN"; |
| case TTEVT_LWP_CREATE: |
| return "TTEVT_LWP_CREATE"; |
| case TTEVT_LWP_TERMINATE: |
| return "TTEVT_LWP_TERMINATE"; |
| case TTEVT_LWP_EXIT: |
| return "TTEVT_LWP_EXIT"; |
| case TTEVT_LWP_ABORT_SYSCALL: |
| return "TTEVT_LWP_ABORT_SYSCALL"; |
| case TTEVT_SYSCALL_ENTRY: |
| return "TTEVT_SYSCALL_ENTRY"; |
| case TTEVT_SYSCALL_RESTART: |
| return "TTEVT_SYSCALL_RESTART"; |
| default: |
| return "?new event?"; |
| } |
| } |
| |
| |
| /* This function translates the ttrace request enumeration into |
| * a character string that is its printable (aka "human readable") |
| * name. |
| */ |
| char * |
| get_printable_name_of_ttrace_request (ttreq_t request) |
| { |
| if (!IS_TTRACE_REQ (request)) |
| return "?bad req?"; |
| |
| /* This enumeration is "gappy", so don't use a table. */ |
| switch (request) |
| { |
| case TT_PROC_SETTRC: |
| return "TT_PROC_SETTRC"; |
| case TT_PROC_ATTACH: |
| return "TT_PROC_ATTACH"; |
| case TT_PROC_DETACH: |
| return "TT_PROC_DETACH"; |
| case TT_PROC_RDTEXT: |
| return "TT_PROC_RDTEXT"; |
| case TT_PROC_WRTEXT: |
| return "TT_PROC_WRTEXT"; |
| case TT_PROC_RDDATA: |
| return "TT_PROC_RDDATA"; |
| case TT_PROC_WRDATA: |
| return "TT_PROC_WRDATA"; |
| case TT_PROC_STOP: |
| return "TT_PROC_STOP"; |
| case TT_PROC_CONTINUE: |
| return "TT_PROC_CONTINUE"; |
| case TT_PROC_GET_PATHNAME: |
| return "TT_PROC_GET_PATHNAME"; |
| case TT_PROC_GET_EVENT_MASK: |
| return "TT_PROC_GET_EVENT_MASK"; |
| case TT_PROC_SET_EVENT_MASK: |
| return "TT_PROC_SET_EVENT_MASK"; |
| case TT_PROC_GET_FIRST_LWP_STATE: |
| return "TT_PROC_GET_FIRST_LWP_STATE"; |
| case TT_PROC_GET_NEXT_LWP_STATE: |
| return "TT_PROC_GET_NEXT_LWP_STATE"; |
| case TT_PROC_EXIT: |
| return "TT_PROC_EXIT"; |
| case TT_PROC_GET_MPROTECT: |
| return "TT_PROC_GET_MPROTECT"; |
| case TT_PROC_SET_MPROTECT: |
| return "TT_PROC_SET_MPROTECT"; |
| case TT_PROC_SET_SCBM: |
| return "TT_PROC_SET_SCBM"; |
| case TT_LWP_STOP: |
| return "TT_LWP_STOP"; |
| case TT_LWP_CONTINUE: |
| return "TT_LWP_CONTINUE"; |
| case TT_LWP_SINGLE: |
| return "TT_LWP_SINGLE"; |
| case TT_LWP_RUREGS: |
| return "TT_LWP_RUREGS"; |
| case TT_LWP_WUREGS: |
| return "TT_LWP_WUREGS"; |
| case TT_LWP_GET_EVENT_MASK: |
| return "TT_LWP_GET_EVENT_MASK"; |
| case TT_LWP_SET_EVENT_MASK: |
| return "TT_LWP_SET_EVENT_MASK"; |
| case TT_LWP_GET_STATE: |
| return "TT_LWP_GET_STATE"; |
| default: |
| return "?new req?"; |
| } |
| } |
| |
| |
| /* This function translates the process state enumeration into |
| * a character string that is its printable (aka "human readable") |
| * name. |
| */ |
| static char * |
| get_printable_name_of_process_state (process_state_t process_state) |
| { |
| switch (process_state) |
| { |
| case STOPPED: |
| return "STOPPED"; |
| case FAKE_STEPPING: |
| return "FAKE_STEPPING"; |
| case RUNNING: |
| return "RUNNING"; |
| case FORKING: |
| return "FORKING"; |
| case VFORKING: |
| return "VFORKING"; |
| default: |
| return "?some unknown state?"; |
| } |
| } |
| |
| /* Set a ttrace thread state to a safe, initial state. |
| */ |
| static void |
| clear_ttstate_t (ttstate_t *tts) |
| { |
| tts->tts_pid = 0; |
| tts->tts_lwpid = 0; |
| tts->tts_user_tid = 0; |
| tts->tts_event = TTEVT_NONE; |
| } |
| |
| /* Copy ttrace thread state TTS_FROM into TTS_TO. |
| */ |
| static void |
| copy_ttstate_t (ttstate_t *tts_to, ttstate_t *tts_from) |
| { |
| memcpy ((char *) tts_to, (char *) tts_from, sizeof (*tts_to)); |
| } |
| |
| /* Are there any live threads we know about? |
| */ |
| static int |
| any_thread_records (void) |
| { |
| return (thread_head.count > 0); |
| } |
| |
| /* Create, fill in and link in a thread descriptor. |
| */ |
| static thread_info * |
| create_thread_info (int pid, lwpid_t tid) |
| { |
| thread_info *new_p; |
| thread_info *p; |
| int thread_count_of_pid; |
| |
| new_p = xmalloc (sizeof (thread_info)); |
| new_p->pid = pid; |
| new_p->tid = tid; |
| new_p->have_signal = 0; |
| new_p->have_start = 0; |
| new_p->have_state = 0; |
| clear_ttstate_t (&new_p->last_stop_state); |
| new_p->am_pseudo = 0; |
| new_p->handled = 0; |
| new_p->seen = 0; |
| new_p->terminated = 0; |
| new_p->next = NULL; |
| new_p->next_pseudo = NULL; |
| new_p->stepping_mode = DO_DEFAULT; |
| |
| if (0 == thread_head.count) |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("First thread, pid %d tid %d!\n", pid, tid); |
| #endif |
| saved_real_ptid = inferior_ptid; |
| } |
| else |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Subsequent thread, pid %d tid %d\n", pid, tid); |
| #endif |
| } |
| |
| /* Another day, another thread... |
| */ |
| thread_head.count++; |
| |
| /* The new thread always goes at the head of the list. |
| */ |
| new_p->next = thread_head.head; |
| thread_head.head = new_p; |
| |
| /* Is this the "pseudo" thread of a process? It is if there's |
| * no other thread for this process on the list. (Note that this |
| * accomodates multiple processes, such as we see even for simple |
| * cases like forking "non-threaded" programs.) |
| */ |
| p = thread_head.head; |
| thread_count_of_pid = 0; |
| while (p) |
| { |
| if (p->pid == new_p->pid) |
| thread_count_of_pid++; |
| p = p->next; |
| } |
| |
| /* Did we see any other threads for this pid? (Recall that we just |
| * added this thread to the list...) |
| */ |
| if (thread_count_of_pid == 1) |
| { |
| new_p->am_pseudo = 1; |
| new_p->next_pseudo = thread_head.head_pseudo; |
| thread_head.head_pseudo = new_p; |
| } |
| |
| return new_p; |
| } |
| |
| /* Get rid of our thread info. |
| */ |
| static void |
| clear_thread_info (void) |
| { |
| thread_info *p; |
| thread_info *q; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Clearing all thread info\n"); |
| #endif |
| |
| p = thread_head.head; |
| while (p) |
| { |
| q = p; |
| p = p->next; |
| xfree (q); |
| } |
| |
| thread_head.head = NULL; |
| thread_head.head_pseudo = NULL; |
| thread_head.count = 0; |
| |
| p = deleted_threads.head; |
| while (p) |
| { |
| q = p; |
| p = p->next; |
| xfree (q); |
| } |
| |
| deleted_threads.head = NULL; |
| deleted_threads.head_pseudo = NULL; |
| deleted_threads.count = 0; |
| |
| /* No threads, so can't have pending events. |
| */ |
| more_events_left = 0; |
| } |
| |
| /* Given a tid, find the thread block for it. |
| */ |
| static thread_info * |
| find_thread_info (lwpid_t tid) |
| { |
| thread_info *p; |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| if (p->tid == tid) |
| { |
| return p; |
| } |
| } |
| |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| if (p->tid == tid) |
| { |
| return p; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* For any but the pseudo thread, this maps to the |
| * thread ID. For the pseudo thread, if you pass either |
| * the thread id or the PID, you get the pseudo thread ID. |
| * |
| * We have to be prepared for core gdb to ask about |
| * deleted threads. We do the map, but we don't like it. |
| */ |
| static lwpid_t |
| map_from_gdb_tid (lwpid_t gdb_tid) |
| { |
| thread_info *p; |
| |
| /* First assume gdb_tid really is a tid, and try to find a |
| * matching entry on the threads list. |
| */ |
| for (p = thread_head.head; p; p = p->next) |
| { |
| if (p->tid == gdb_tid) |
| return gdb_tid; |
| } |
| |
| /* It doesn't appear to be a tid; perhaps it's really a pid? |
| * Try to find a "pseudo" thread entry on the threads list. |
| */ |
| for (p = thread_head.head_pseudo; p != NULL; p = p->next_pseudo) |
| { |
| if (p->pid == gdb_tid) |
| return p->tid; |
| } |
| |
| /* Perhaps it's the tid of a deleted thread we may still |
| * have some knowledge of? |
| */ |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| if (p->tid == gdb_tid) |
| return gdb_tid; |
| } |
| |
| /* Or perhaps it's the pid of a deleted process we may still |
| * have knowledge of? |
| */ |
| for (p = deleted_threads.head_pseudo; p != NULL; p = p->next_pseudo) |
| { |
| if (p->pid == gdb_tid) |
| return p->tid; |
| } |
| |
| return 0; /* Error? */ |
| } |
| |
| /* Map the other way: from a real tid to the |
| * "pid" known by core gdb. This tid may be |
| * for a thread that just got deleted, so we |
| * also need to consider deleted threads. |
| */ |
| static lwpid_t |
| map_to_gdb_tid (lwpid_t real_tid) |
| { |
| thread_info *p; |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| if (p->tid == real_tid) |
| { |
| if (p->am_pseudo) |
| return p->pid; |
| else |
| return real_tid; |
| } |
| } |
| |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| if (p->tid == real_tid) |
| if (p->am_pseudo) |
| return p->pid; /* Error? */ |
| else |
| return real_tid; |
| } |
| |
| return 0; /* Error? Never heard of this thread! */ |
| } |
| |
| /* Do any threads have saved signals? |
| */ |
| static int |
| saved_signals_exist (void) |
| { |
| thread_info *p; |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| if (p->have_signal) |
| { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Is this the tid for the zero-th thread? |
| */ |
| static int |
| is_pseudo_thread (lwpid_t tid) |
| { |
| thread_info *p = find_thread_info (tid); |
| if (NULL == p || p->terminated) |
| return 0; |
| else |
| return p->am_pseudo; |
| } |
| |
| /* Is this thread terminated? |
| */ |
| static int |
| is_terminated (lwpid_t tid) |
| { |
| thread_info *p = find_thread_info (tid); |
| |
| if (NULL != p) |
| return p->terminated; |
| |
| return 0; |
| } |
| |
| /* Is this pid a real PID or a TID? |
| */ |
| static int |
| is_process_id (int pid) |
| { |
| lwpid_t tid; |
| thread_info *tinfo; |
| pid_t this_pid; |
| int this_pid_count; |
| |
| /* What does PID really represent? |
| */ |
| tid = map_from_gdb_tid (pid); |
| if (tid <= 0) |
| return 0; /* Actually, is probably an error... */ |
| |
| tinfo = find_thread_info (tid); |
| |
| /* Does it appear to be a true thread? |
| */ |
| if (!tinfo->am_pseudo) |
| return 0; |
| |
| /* Else, it looks like it may be a process. See if there's any other |
| * threads with the same process ID, though. If there are, then TID |
| * just happens to be the first thread of several for this process. |
| */ |
| this_pid = tinfo->pid; |
| this_pid_count = 0; |
| for (tinfo = thread_head.head; tinfo; tinfo = tinfo->next) |
| { |
| if (tinfo->pid == this_pid) |
| this_pid_count++; |
| } |
| |
| return (this_pid_count == 1); |
| } |
| |
| |
| /* Add a thread to our info. Prevent duplicate entries. |
| */ |
| static thread_info * |
| add_tthread (int pid, lwpid_t tid) |
| { |
| thread_info *p; |
| |
| p = find_thread_info (tid); |
| if (NULL == p) |
| p = create_thread_info (pid, tid); |
| |
| return p; |
| } |
| |
| /* Notice that a thread was deleted. |
| */ |
| static void |
| del_tthread (lwpid_t tid) |
| { |
| thread_info *p; |
| thread_info *chase; |
| |
| if (thread_head.count <= 0) |
| { |
| error ("Internal error in thread database."); |
| return; |
| } |
| |
| chase = NULL; |
| for (p = thread_head.head; p; p = p->next) |
| { |
| if (p->tid == tid) |
| { |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Delete here: %d \n", tid); |
| #endif |
| |
| if (p->am_pseudo) |
| { |
| /* |
| * Deleting a main thread is ok if we're doing |
| * a parent-follow on a child; this is odd but |
| * not wrong. It apparently _doesn't_ happen |
| * on the child-follow, as we don't just delete |
| * the pseudo while keeping the rest of the |
| * threads around--instead, we clear out the whole |
| * thread list at once. |
| */ |
| thread_info *q; |
| thread_info *q_chase; |
| |
| q_chase = NULL; |
| for (q = thread_head.head_pseudo; q; q = q->next) |
| { |
| if (q == p) |
| { |
| /* Remove from pseudo list. |
| */ |
| if (q_chase == NULL) |
| thread_head.head_pseudo = p->next_pseudo; |
| else |
| q_chase->next = p->next_pseudo; |
| } |
| else |
| q_chase = q; |
| } |
| } |
| |
| /* Remove from live list. |
| */ |
| thread_head.count--; |
| |
| if (NULL == chase) |
| thread_head.head = p->next; |
| else |
| chase->next = p->next; |
| |
| /* Add to deleted thread list. |
| */ |
| p->next = deleted_threads.head; |
| deleted_threads.head = p; |
| deleted_threads.count++; |
| if (p->am_pseudo) |
| { |
| p->next_pseudo = deleted_threads.head_pseudo; |
| deleted_threads.head_pseudo = p; |
| } |
| p->terminated = 1; |
| |
| return; |
| } |
| |
| else |
| chase = p; |
| } |
| } |
| |
| /* Get the pid for this tid. (Has to be a real TID!). |
| */ |
| static int |
| get_pid_for (lwpid_t tid) |
| { |
| thread_info *p; |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| if (p->tid == tid) |
| { |
| return p->pid; |
| } |
| } |
| |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| if (p->tid == tid) |
| { |
| return p->pid; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Note that this thread's current event has been handled. |
| */ |
| static void |
| set_handled (int pid, lwpid_t tid) |
| { |
| thread_info *p; |
| |
| p = find_thread_info (tid); |
| if (NULL == p) |
| p = add_tthread (pid, tid); |
| |
| p->handled = 1; |
| } |
| |
| /* Was this thread's current event handled? |
| */ |
| static int |
| was_handled (lwpid_t tid) |
| { |
| thread_info *p; |
| |
| p = find_thread_info (tid); |
| if (NULL != p) |
| return p->handled; |
| |
| return 0; /* New threads have not been handled */ |
| } |
| |
| /* Set this thread to unhandled. |
| */ |
| static void |
| clear_handled (lwpid_t tid) |
| { |
| thread_info *p; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("clear_handled %d\n", (int) tid); |
| #endif |
| |
| p = find_thread_info (tid); |
| if (p == NULL) |
| error ("Internal error: No thread state to clear?"); |
| |
| p->handled = 0; |
| } |
| |
| /* Set all threads to unhandled. |
| */ |
| static void |
| clear_all_handled (void) |
| { |
| thread_info *p; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("clear_all_handled\n"); |
| #endif |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| p->handled = 0; |
| } |
| |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| p->handled = 0; |
| } |
| } |
| |
| /* Set this thread to default stepping mode. |
| */ |
| static void |
| clear_stepping_mode (lwpid_t tid) |
| { |
| thread_info *p; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("clear_stepping_mode %d\n", (int) tid); |
| #endif |
| |
| p = find_thread_info (tid); |
| if (p == NULL) |
| error ("Internal error: No thread state to clear?"); |
| |
| p->stepping_mode = DO_DEFAULT; |
| } |
| |
| /* Set all threads to do default continue on resume. |
| */ |
| static void |
| clear_all_stepping_mode (void) |
| { |
| thread_info *p; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("clear_all_stepping_mode\n"); |
| #endif |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| p->stepping_mode = DO_DEFAULT; |
| } |
| |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| p->stepping_mode = DO_DEFAULT; |
| } |
| } |
| |
| /* Set all threads to unseen on this pass. |
| */ |
| static void |
| set_all_unseen (void) |
| { |
| thread_info *p; |
| |
| for (p = thread_head.head; p; p = p->next) |
| { |
| p->seen = 0; |
| } |
| } |
| |
| #if (defined( THREAD_DEBUG ) || defined( PARANOIA )) |
| /* debugging routine. |
| */ |
| static void |
| print_tthread (thread_info *p) |
| { |
| printf (" Thread pid %d, tid %d", p->pid, p->tid); |
| if (p->have_state) |
| printf (", event is %s", |
| get_printable_name_of_ttrace_event (p->last_stop_state.tts_event)); |
| |
| if (p->am_pseudo) |
| printf (", pseudo thread"); |
| |
| if (p->have_signal) |
| printf (", have signal 0x%x", p->signal_value); |
| |
| if (p->have_start) |
| printf (", have start at 0x%x", p->start); |
| |
| printf (", step is %s", get_printable_name_of_stepping_mode (p->stepping_mode)); |
| |
| if (p->handled) |
| printf (", handled"); |
| else |
| printf (", not handled"); |
| |
| if (p->seen) |
| printf (", seen"); |
| else |
| printf (", not seen"); |
| |
| printf ("\n"); |
| } |
| |
| static void |
| print_tthreads (void) |
| { |
| thread_info *p; |
| |
| if (thread_head.count == 0) |
| printf ("Thread list is empty\n"); |
| else |
| { |
| printf ("Thread list has "); |
| if (thread_head.count == 1) |
| printf ("1 entry:\n"); |
| else |
| printf ("%d entries:\n", thread_head.count); |
| for (p = thread_head.head; p; p = p->next) |
| { |
| print_tthread (p); |
| } |
| } |
| |
| if (deleted_threads.count == 0) |
| printf ("Deleted thread list is empty\n"); |
| else |
| { |
| printf ("Deleted thread list has "); |
| if (deleted_threads.count == 1) |
| printf ("1 entry:\n"); |
| else |
| printf ("%d entries:\n", deleted_threads.count); |
| |
| for (p = deleted_threads.head; p; p = p->next) |
| { |
| print_tthread (p); |
| } |
| } |
| } |
| #endif |
| |
| /* Update the thread list based on the "seen" bits. |
| */ |
| static void |
| update_thread_list (void) |
| { |
| thread_info *p; |
| thread_info *chase; |
| |
| chase = NULL; |
| for (p = thread_head.head; p; p = p->next) |
| { |
| /* Is this an "unseen" thread which really happens to be a process? |
| If so, is it inferior_ptid and is a vfork in flight? If yes to |
| all, then DON'T REMOVE IT! We're in the midst of moving a vfork |
| operation, which is a multiple step thing, to the point where we |
| can touch the parent again. We've most likely stopped to examine |
| the child at a late stage in the vfork, and if we're not following |
| the child, we'd best not treat the parent as a dead "thread"... |
| */ |
| if ((!p->seen) && p->am_pseudo && vfork_in_flight |
| && (p->pid != vforking_child_pid)) |
| p->seen = 1; |
| |
| if (!p->seen) |
| { |
| /* Remove this one |
| */ |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Delete unseen thread: %d \n", p->tid); |
| #endif |
| del_tthread (p->tid); |
| } |
| } |
| } |
| |
| |
| |
| /************************************************ |
| * O/S call wrappers * |
| ************************************************ |
| */ |
| |
| /* This function simply calls ttrace with the given arguments. |
| * It exists so that all calls to ttrace are isolated. All |
| * parameters should be as specified by "man 2 ttrace". |
| * |
| * No other "raw" calls to ttrace should exist in this module. |
| */ |
| static int |
| call_real_ttrace (ttreq_t request, pid_t pid, lwpid_t tid, TTRACE_ARG_TYPE addr, |
| TTRACE_ARG_TYPE data, TTRACE_ARG_TYPE addr2) |
| { |
| int tt_status; |
| |
| errno = 0; |
| tt_status = ttrace (request, pid, tid, addr, data, addr2); |
| |
| #ifdef THREAD_DEBUG |
| if (errno) |
| { |
| /* Don't bother for a known benign error: if you ask for the |
| * first thread state, but there is only one thread and it's |
| * not stopped, ttrace complains. |
| * |
| * We have this inside the #ifdef because our caller will do |
| * this check for real. |
| */ |
| if (request != TT_PROC_GET_FIRST_LWP_STATE |
| || errno != EPROTO) |
| { |
| if (debug_on) |
| printf ("TT fail for %s, with pid %d, tid %d, status %d \n", |
| get_printable_name_of_ttrace_request (request), |
| pid, tid, tt_status); |
| } |
| } |
| #endif |
| |
| #if 0 |
| /* ??rehrauer: It would probably be most robust to catch and report |
| * failed requests here. However, some clients of this interface |
| * seem to expect to catch & deal with them, so we'd best not. |
| */ |
| if (errno) |
| { |
| strcpy (reason_for_failure, "ttrace ("); |
| strcat (reason_for_failure, get_printable_name_of_ttrace_request (request)); |
| strcat (reason_for_failure, ")"); |
| printf ("ttrace error, errno = %d\n", errno); |
| perror_with_name (reason_for_failure); |
| } |
| #endif |
| |
| return tt_status; |
| } |
| |
| |
| /* This function simply calls ttrace_wait with the given arguments. |
| * It exists so that all calls to ttrace_wait are isolated. |
| * |
| * No "raw" calls to ttrace_wait should exist elsewhere. |
| */ |
| static int |
| call_real_ttrace_wait (int pid, lwpid_t tid, ttwopt_t option, ttstate_t *tsp, |
| size_t tsp_size) |
| { |
| int ttw_status; |
| thread_info *tinfo = NULL; |
| |
| errno = 0; |
| ttw_status = ttrace_wait (pid, tid, option, tsp, tsp_size); |
| |
| if (errno) |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("TW fail with pid %d, tid %d \n", pid, tid); |
| #endif |
| |
| perror_with_name ("ttrace wait"); |
| } |
| |
| return ttw_status; |
| } |
| |
| |
| /* A process may have one or more kernel threads, of which all or |
| none may be stopped. This function returns the ID of the first |
| kernel thread in a stopped state, or 0 if none are stopped. |
| |
| This function can be used with get_process_next_stopped_thread_id |
| to iterate over the IDs of all stopped threads of this process. |
| */ |
| static lwpid_t |
| get_process_first_stopped_thread_id (int pid, ttstate_t *thread_state) |
| { |
| int tt_status; |
| |
| tt_status = call_real_ttrace (TT_PROC_GET_FIRST_LWP_STATE, |
| (pid_t) pid, |
| (lwpid_t) TT_NIL, |
| (TTRACE_ARG_TYPE) thread_state, |
| (TTRACE_ARG_TYPE) sizeof (*thread_state), |
| TT_NIL); |
| |
| if (errno) |
| { |
| if (errno == EPROTO) |
| { |
| /* This is an error we can handle: there isn't any stopped |
| * thread. This happens when we're re-starting the application |
| * and it has only one thread. GET_NEXT handles the case of |
| * no more stopped threads well; GET_FIRST doesn't. (A ttrace |
| * "feature".) |
| */ |
| tt_status = 1; |
| errno = 0; |
| return 0; |
| } |
| else |
| perror_with_name ("ttrace"); |
| } |
| |
| if (tt_status < 0) |
| /* Failed somehow. |
| */ |
| return 0; |
| |
| return thread_state->tts_lwpid; |
| } |
| |
| |
| /* This function returns the ID of the "next" kernel thread in a |
| stopped state, or 0 if there are none. "Next" refers to the |
| thread following that of the last successful call to this |
| function or to get_process_first_stopped_thread_id, using |
| the value of thread_state returned by that call. |
| |
| This function can be used with get_process_first_stopped_thread_id |
| to iterate over the IDs of all stopped threads of this process. |
| */ |
| static lwpid_t |
| get_process_next_stopped_thread_id (int pid, ttstate_t *thread_state) |
| { |
| int tt_status; |
| |
| tt_status = call_real_ttrace ( |
| TT_PROC_GET_NEXT_LWP_STATE, |
| (pid_t) pid, |
| (lwpid_t) TT_NIL, |
| (TTRACE_ARG_TYPE) thread_state, |
| (TTRACE_ARG_TYPE) sizeof (*thread_state), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| if (tt_status < 0) |
| /* Failed |
| */ |
| return 0; |
| |
| else if (tt_status == 0) |
| { |
| /* End of list, no next state. Don't return the |
| * tts_lwpid, as it's a meaningless "240". |
| * |
| * This is an HPUX "feature". |
| */ |
| return 0; |
| } |
| |
| return thread_state->tts_lwpid; |
| } |
| |
| /* ??rehrauer: Eventually this function perhaps should be calling |
| pid_to_thread_id. However, that function currently does nothing |
| for HP-UX. Even then, I'm not clear whether that function |
| will return a "kernel" thread ID, or a "user" thread ID. If |
| the former, we can just call it here. If the latter, we must |
| map from the "user" tid to a "kernel" tid. |
| |
| NOTE: currently not called. |
| */ |
| static lwpid_t |
| get_active_tid_of_pid (int pid) |
| { |
| ttstate_t thread_state; |
| |
| return get_process_first_stopped_thread_id (pid, &thread_state); |
| } |
| |
| /* This function returns 1 if tt_request is a ttrace request that |
| * operates upon all threads of a (i.e., the entire) process. |
| */ |
| int |
| is_process_ttrace_request (ttreq_t tt_request) |
| { |
| return IS_TTRACE_PROCREQ (tt_request); |
| } |
| |
| |
| /* This function translates a thread ttrace request into |
| * the equivalent process request for a one-thread process. |
| */ |
| static ttreq_t |
| make_process_version (ttreq_t request) |
| { |
| if (!IS_TTRACE_REQ (request)) |
| { |
| error ("Internal error, bad ttrace request made\n"); |
| return -1; |
| } |
| |
| switch (request) |
| { |
| case TT_LWP_STOP: |
| return TT_PROC_STOP; |
| |
| case TT_LWP_CONTINUE: |
| return TT_PROC_CONTINUE; |
| |
| case TT_LWP_GET_EVENT_MASK: |
| return TT_PROC_GET_EVENT_MASK; |
| |
| case TT_LWP_SET_EVENT_MASK: |
| return TT_PROC_SET_EVENT_MASK; |
| |
| case TT_LWP_SINGLE: |
| case TT_LWP_RUREGS: |
| case TT_LWP_WUREGS: |
| case TT_LWP_GET_STATE: |
| return -1; /* No equivalent */ |
| |
| default: |
| return request; |
| } |
| } |
| |
| |
| /* This function translates the "pid" used by the rest of |
| * gdb to a real pid and a tid. It then calls "call_real_ttrace" |
| * with the given arguments. |
| * |
| * In general, other parts of this module should call this |
| * function when they are dealing with external users, who only |
| * have tids to pass (but they call it "pid" for historical |
| * reasons). |
| */ |
| static int |
| call_ttrace (ttreq_t request, int gdb_tid, TTRACE_ARG_TYPE addr, |
| TTRACE_ARG_TYPE data, TTRACE_ARG_TYPE addr2) |
| { |
| lwpid_t real_tid; |
| int real_pid; |
| ttreq_t new_request; |
| int tt_status; |
| char reason_for_failure[100]; /* Arbitrary size, should be big enough. */ |
| |
| #ifdef THREAD_DEBUG |
| int is_interesting = 0; |
| |
| if (TT_LWP_RUREGS == request) |
| { |
| is_interesting = 1; /* Adjust code here as desired */ |
| } |
| |
| if (is_interesting && 0 && debug_on) |
| { |
| if (!is_process_ttrace_request (request)) |
| { |
| printf ("TT: Thread request, tid is %d", gdb_tid); |
| printf ("== SINGLE at %x", addr); |
| } |
| else |
| { |
| printf ("TT: Process request, tid is %d\n", gdb_tid); |
| printf ("==! SINGLE at %x", addr); |
| } |
| } |
| #endif |
| |
| /* The initial SETTRC and SET_EVENT_MASK calls (and all others |
| * which happen before any threads get set up) should go |
| * directly to "call_real_ttrace", so they don't happen here. |
| * |
| * But hardware watchpoints do a SET_EVENT_MASK, so we can't |
| * rule them out.... |
| */ |
| #ifdef THREAD_DEBUG |
| if (request == TT_PROC_SETTRC && debug_on) |
| printf ("Unexpected call for TT_PROC_SETTRC\n"); |
| #endif |
| |
| /* Sometimes we get called with a bogus tid (e.g., if a |
| * thread has terminated, we return 0; inftarg later asks |
| * whether the thread has exited/forked/vforked). |
| */ |
| if (gdb_tid == 0) |
| { |
| errno = ESRCH; /* ttrace's response would probably be "No such process". */ |
| return -1; |
| } |
| |
| /* All other cases should be able to expect that there are |
| * thread records. |
| */ |
| if (!any_thread_records ()) |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| warning ("No thread records for ttrace call"); |
| #endif |
| errno = ESRCH; /* ttrace's response would be "No such process". */ |
| return -1; |
| } |
| |
| /* OK, now the task is to translate the incoming tid into |
| * a pid/tid pair. |
| */ |
| real_tid = map_from_gdb_tid (gdb_tid); |
| real_pid = get_pid_for (real_tid); |
| |
| /* Now check the result. "Real_pid" is NULL if our list |
| * didn't find it. We have some tricks we can play to fix |
| * this, however. |
| */ |
| if (0 == real_pid) |
| { |
| ttstate_t thread_state; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("No saved pid for tid %d\n", gdb_tid); |
| #endif |
| |
| if (is_process_ttrace_request (request)) |
| { |
| |
| /* Ok, we couldn't get a tid. Try to translate to |
| * the equivalent process operation. We expect this |
| * NOT to happen, so this is a desparation-type |
| * move. It can happen if there is an internal |
| * error and so no "wait()" call is ever done. |
| */ |
| new_request = make_process_version (request); |
| if (new_request == -1) |
| { |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("...and couldn't make process version of thread operation\n"); |
| #endif |
| |
| /* Use hacky saved pid, which won't always be correct |
| * in the multi-process future. Use tid as thread, |
| * probably dooming this to failure. FIX! |
| */ |
| if (! ptid_equal (saved_real_ptid, null_ptid)) |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("...using saved pid %d\n", |
| PIDGET (saved_real_ptid)); |
| #endif |
| |
| real_pid = PIDGET (saved_real_ptid); |
| real_tid = gdb_tid; |
| } |
| |
| else |
| error ("Unable to perform thread operation"); |
| } |
| |
| else |
| { |
| /* Sucessfully translated this to a process request, |
| * which needs no thread value. |
| */ |
| real_pid = gdb_tid; |
| real_tid = 0; |
| request = new_request; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| { |
| printf ("Translated thread request to process request\n"); |
| if (ptid_equal (saved_real_ptid, null_ptid)) |
| printf ("...but there's no saved pid\n"); |
| |
| else |
| { |
| if (gdb_tid != PIDGET (saved_real_ptid)) |
| printf ("...but have the wrong pid (%d rather than %d)\n", |
| gdb_tid, PIDGET (saved_real_ptid)); |
| } |
| } |
| #endif |
| } /* Translated to a process request */ |
| } /* Is a process request */ |
| |
| else |
| { |
| /* We have to have a thread. Ooops. |
| */ |
| error ("Thread request with no threads (%s)", |
| get_printable_name_of_ttrace_request (request)); |
| } |
| } |
| |
| /* Ttrace doesn't like to see tid values on process requests, |
| * even if we have the right one. |
| */ |
| if (is_process_ttrace_request (request)) |
| { |
| real_tid = 0; |
| } |
| |
| #ifdef THREAD_DEBUG |
| if (is_interesting && 0 && debug_on) |
| { |
| printf (" now tid %d, pid %d\n", real_tid, real_pid); |
| printf (" request is %s\n", get_printable_name_of_ttrace_request (request)); |
| } |
| #endif |
| |
| /* Finally, the (almost) real call. |
| */ |
| tt_status = call_real_ttrace (request, real_pid, real_tid, addr, data, addr2); |
| |
| #ifdef THREAD_DEBUG |
| if (is_interesting && debug_on) |
| { |
| if (!TT_OK (tt_status, errno) |
| && !(tt_status == 0 & errno == 0)) |
| printf (" got error (errno==%d, status==%d)\n", errno, tt_status); |
| } |
| #endif |
| |
| return tt_status; |
| } |
| |
| |
| /* Stop all the threads of a process. |
| |
| * NOTE: use of TT_PROC_STOP can cause a thread with a real event |
| * to get a TTEVT_NONE event, discarding the old event. Be |
| * very careful, and only call TT_PROC_STOP when you mean it! |
| */ |
| static void |
| stop_all_threads_of_process (pid_t real_pid) |
| { |
| int ttw_status; |
| |
| ttw_status = call_real_ttrace (TT_PROC_STOP, |
| (pid_t) real_pid, |
| (lwpid_t) TT_NIL, |
| (TTRACE_ARG_TYPE) TT_NIL, |
| (TTRACE_ARG_TYPE) TT_NIL, |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace stop of other threads"); |
| } |
| |
| |
| /* Under some circumstances, it's unsafe to attempt to stop, or even |
| query the state of, a process' threads. |
| |
| In ttrace-based HP-UX, an example is a vforking child process. The |
| vforking parent and child are somewhat fragile, w/r/t what we can do |
| what we can do to them with ttrace, until after the child exits or |
| execs, or until the parent's vfork event is delivered. Until that |
| time, we must not try to stop the process' threads, or inquire how |
| many there are, or even alter its data segments, or it typically dies |
| with a SIGILL. Sigh. |
| |
| This function returns 1 if this stopped process, and the event that |
| we're told was responsible for its current stopped state, cannot safely |
| have its threads examined. |
| */ |
| #define CHILD_VFORKED(evt,pid) \ |
| (((evt) == TTEVT_VFORK) && ((pid) != PIDGET (inferior_ptid))) |
| #define CHILD_URPED(evt,pid) \ |
| ((((evt) == TTEVT_EXEC) || ((evt) == TTEVT_EXIT)) && ((pid) != vforking_child_pid)) |
| #define PARENT_VFORKED(evt,pid) \ |
| (((evt) == TTEVT_VFORK) && ((pid) == PIDGET (inferior_ptid))) |
| |
| static int |
| can_touch_threads_of_process (int pid, ttevents_t stopping_event) |
| { |
| if (CHILD_VFORKED (stopping_event, pid)) |
| { |
| vforking_child_pid = pid; |
| vfork_in_flight = 1; |
| } |
| |
| else if (vfork_in_flight && |
| (PARENT_VFORKED (stopping_event, pid) || |
| CHILD_URPED (stopping_event, pid))) |
| { |
| vfork_in_flight = 0; |
| vforking_child_pid = 0; |
| } |
| |
| return !vfork_in_flight; |
| } |
| |
| |
| /* If we can find an as-yet-unhandled thread state of a |
| * stopped thread of this process return 1 and set "tsp". |
| * Return 0 if we can't. |
| * |
| * If this function is used when the threads of PIS haven't |
| * been stopped, undefined behaviour is guaranteed! |
| */ |
| static int |
| select_stopped_thread_of_process (int pid, ttstate_t *tsp) |
| { |
| lwpid_t candidate_tid, tid; |
| ttstate_t candidate_tstate, tstate; |
| |
| /* If we're not allowed to touch the process now, then just |
| * return the current value of *TSP. |
| * |
| * This supports "vfork". It's ok, really, to double the |
| * current event (the child EXEC, we hope!). |
| */ |
| if (!can_touch_threads_of_process (pid, tsp->tts_event)) |
| return 1; |
| |
| /* Decide which of (possibly more than one) events to |
| * return as the first one. We scan them all so that |
| * we always return the result of a fake-step first. |
| */ |
| candidate_tid = 0; |
| for (tid = get_process_first_stopped_thread_id (pid, &tstate); |
| tid != 0; |
| tid = get_process_next_stopped_thread_id (pid, &tstate)) |
| { |
| /* TTEVT_NONE events are uninteresting to our clients. They're |
| * an artifact of our "stop the world" model--the thread is |
| * stopped because we stopped it. |
| */ |
| if (tstate.tts_event == TTEVT_NONE) |
| { |
| set_handled (pid, tstate.tts_lwpid); |
| } |
| |
| /* Did we just single-step a single thread, without letting any |
| * of the others run? Is this an event for that thread? |
| * |
| * If so, we believe our client would prefer to see this event |
| * over any others. (Typically the client wants to just push |
| * one thread a little farther forward, and then go around |
| * checking for what all threads are doing.) |
| */ |
| else if (doing_fake_step && (tstate.tts_lwpid == fake_step_tid)) |
| { |
| #ifdef WAIT_BUFFER_DEBUG |
| /* It's possible here to see either a SIGTRAP (due to |
| * successful completion of a step) or a SYSCALL_ENTRY |
| * (due to a step completion with active hardware |
| * watchpoints). |
| */ |
| if (debug_on) |
| printf ("Ending fake step with tid %d, state %s\n", |
| tstate.tts_lwpid, |
| get_printable_name_of_ttrace_event (tstate.tts_event)); |
| #endif |
| |
| /* Remember this one, and throw away any previous |
| * candidate. |
| */ |
| candidate_tid = tstate.tts_lwpid; |
| candidate_tstate = tstate; |
| } |
| |
| #ifdef FORGET_DELETED_BPTS |
| |
| /* We can't just do this, as if we do, and then wind |
| * up the loop with no unhandled events, we need to |
| * handle that case--the appropriate reaction is to |
| * just continue, but there's no easy way to do that. |
| * |
| * Better to put this in the ttrace_wait call--if, when |
| * we fake a wait, we update our events based on the |
| * breakpoint_here_pc call and find there are no more events, |
| * then we better continue and so on. |
| * |
| * Or we could put it in the next/continue fake. |
| * But it has to go in the buffering code, not in the |
| * real go/wait code. |
| */ |
| else if ((TTEVT_SIGNAL == tstate.tts_event) |
| && (5 == tstate.tts_u.tts_signal.tts_signo) |
| && (0 != get_raw_pc (tstate.tts_lwpid)) |
| && !breakpoint_here_p (get_raw_pc (tstate.tts_lwpid))) |
| { |
| /* |
| * If the user deleted a breakpoint while this |
| * breakpoint-hit event was buffered, we can forget |
| * it now. |
| */ |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Forgetting deleted bp hit for thread %d\n", |
| tstate.tts_lwpid); |
| #endif |
| |
| set_handled (pid, tstate.tts_lwpid); |
| } |
| #endif |
| |
| /* Else, is this the first "unhandled" event? If so, |
| * we believe our client wants to see it (if we don't |
| * see a fake-step later on in the scan). |
| */ |
| else if (!was_handled (tstate.tts_lwpid) && candidate_tid == 0) |
| { |
| candidate_tid = tstate.tts_lwpid; |
| candidate_tstate = tstate; |
| } |
| |
| /* This is either an event that has already been "handled", |
| * and thus we believe is uninteresting to our client, or we |
| * already have a candidate event. Ignore it... |
| */ |
| } |
| |
| /* What do we report? |
| */ |
| if (doing_fake_step) |
| { |
| if (candidate_tid == fake_step_tid) |
| { |
| /* Fake step. |
| */ |
| tstate = candidate_tstate; |
| } |
| else |
| { |
| warning ("Internal error: fake-step failed to complete."); |
| return 0; |
| } |
| } |
| else if (candidate_tid != 0) |
| { |
| /* Found a candidate unhandled event. |
| */ |
| tstate = candidate_tstate; |
| } |
| else if (tid != 0) |
| { |
| warning ("Internal error in call of ttrace_wait."); |
| return 0; |
| } |
| else |
| { |
| warning ("Internal error: no unhandled thread event to select"); |
| return 0; |
| } |
| |
| copy_ttstate_t (tsp, &tstate); |
| return 1; |
| } /* End of select_stopped_thread_of_process */ |
| |
| #ifdef PARANOIA |
| /* Check our internal thread data against the real thing. |
| */ |
| static void |
| check_thread_consistency (pid_t real_pid) |
| { |
| int tid; /* really lwpid_t */ |
| ttstate_t tstate; |
| thread_info *p; |
| |
| /* Spin down the O/S list of threads, checking that they |
| * match what we've got. |
| */ |
| for (tid = get_process_first_stopped_thread_id (real_pid, &tstate); |
| tid != 0; |
| tid = get_process_next_stopped_thread_id (real_pid, &tstate)) |
| { |
| |
| p = find_thread_info (tid); |
| |
| if (NULL == p) |
| { |
| warning ("No internal thread data for thread %d.", tid); |
| continue; |
| } |
| |
| if (!p->seen) |
| { |
| warning ("Inconsistent internal thread data for thread %d.", tid); |
| } |
| |
| if (p->terminated) |
| { |
| warning ("Thread %d is not terminated, internal error.", tid); |
| continue; |
| } |
| |
| |
| #define TT_COMPARE( fld ) \ |
| tstate.fld != p->last_stop_state.fld |
| |
| if (p->have_state) |
| { |
| if (TT_COMPARE (tts_pid) |
| || TT_COMPARE (tts_lwpid) |
| || TT_COMPARE (tts_user_tid) |
| || TT_COMPARE (tts_event) |
| || TT_COMPARE (tts_flags) |
| || TT_COMPARE (tts_scno) |
| || TT_COMPARE (tts_scnargs)) |
| { |
| warning ("Internal thread data for thread %d is wrong.", tid); |
| continue; |
| } |
| } |
| } |
| } |
| #endif /* PARANOIA */ |
| |
| |
| /* This function wraps calls to "call_real_ttrace_wait" so |
| * that a actual wait is only done when all pending events |
| * have been reported. |
| * |
| * Note that typically it is called with a pid of "0", i.e. |
| * the "don't care" value. |
| * |
| * Return value is the status of the pseudo wait. |
| */ |
| static int |
| call_ttrace_wait (int pid, ttwopt_t option, ttstate_t *tsp, size_t tsp_size) |
| { |
| /* This holds the actual, for-real, true process ID. |
| */ |
| static int real_pid; |
| |
| /* As an argument to ttrace_wait, zero pid |
| * means "Any process", and zero tid means |
| * "Any thread of the specified process". |
| */ |
| int wait_pid = 0; |
| lwpid_t wait_tid = 0; |
| lwpid_t real_tid; |
| |
| int ttw_status = 0; /* To be returned */ |
| |
| thread_info *tinfo = NULL; |
| |
| if (pid != 0) |
| { |
| /* Unexpected case. |
| */ |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("TW: Pid to wait on is %d\n", pid); |
| #endif |
| |
| if (!any_thread_records ()) |
| error ("No thread records for ttrace call w. specific pid"); |
| |
| /* OK, now the task is to translate the incoming tid into |
| * a pid/tid pair. |
| */ |
| real_tid = map_from_gdb_tid (pid); |
| real_pid = get_pid_for (real_tid); |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("==TW: real pid %d, real tid %d\n", real_pid, real_tid); |
| #endif |
| } |
| |
| |
| /* Sanity checks and set-up. |
| * Process State |
| * |
| * Stopped Running Fake-step (v)Fork |
| * \________________________________________ |
| * | |
| * No buffered events | error wait wait wait |
| * | |
| * Buffered events | debuffer error wait debuffer (?) |
| * |
| */ |
| if (more_events_left == 0) |
| { |
| |
| if (process_state == RUNNING) |
| { |
| /* OK--normal call of ttrace_wait with no buffered events. |
| */ |
| ; |
| } |
| else if (process_state == FAKE_STEPPING) |
| { |
| /* Ok--call of ttrace_wait to support |
| * fake stepping with no buffered events. |
| * |
| * But we better be fake-stepping! |
| */ |
| if (!doing_fake_step) |
| { |
| warning ("Inconsistent thread state."); |
| } |
| } |
| else if ((process_state == FORKING) |
| || (process_state == VFORKING)) |
| { |
| /* Ok--there are two processes, so waiting |
| * for the second while the first is stopped |
| * is ok. Handled bits stay as they were. |
| */ |
| ; |
| } |
| else if (process_state == STOPPED) |
| { |
| warning ("Process not running at wait call."); |
| } |
| else |
| /* No known state. |
| */ |
| warning ("Inconsistent process state."); |
| } |
| |
| else |
| { |
| /* More events left |
| */ |
| if (process_state == STOPPED) |
| { |
| /* OK--buffered events being unbuffered. |
| */ |
| ; |
| } |
| else if (process_state == RUNNING) |
| { |
| /* An error--shouldn't have buffered events |
| * when running. |
| */ |
| warning ("Trying to continue with buffered events:"); |
| } |
| else if (process_state == FAKE_STEPPING) |
| { |
| /* |
| * Better be fake-stepping! |
| */ |
| if (!doing_fake_step) |
| { |
| warning ("Losing buffered thread events!\n"); |
| } |
| } |
| else if ((process_state == FORKING) |
| || (process_state == VFORKING)) |
| { |
| /* Ok--there are two processes, so waiting |
| * for the second while the first is stopped |
| * is ok. Handled bits stay as they were. |
| */ |
| ; |
| } |
| else |
| warning ("Process in unknown state with buffered events."); |
| } |
| |
| /* Sometimes we have to wait for a particular thread |
| * (if we're stepping over a bpt). In that case, we |
| * _know_ it's going to complete the single-step we |
| * asked for (because we're only doing the step under |
| * certain very well-understood circumstances), so it |
| * can't block. |
| */ |
| if (doing_fake_step) |
| { |
| wait_tid = fake_step_tid; |
| wait_pid = get_pid_for (fake_step_tid); |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Doing a wait after a fake-step for %d, pid %d\n", |
| wait_tid, wait_pid); |
| #endif |
| } |
| |
| if (more_events_left == 0 /* No buffered events, need real ones. */ |
| || process_state != STOPPED) |
| { |
| /* If there are no buffered events, and so we need |
| * real ones, or if we are FORKING, VFORKING, |
| * FAKE_STEPPING or RUNNING, and thus have to do |
| * a real wait, then do a real wait. |
| */ |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| /* Normal case... */ |
| if (debug_on) |
| printf ("TW: do it for real; pid %d, tid %d\n", wait_pid, wait_tid); |
| #endif |
| |
| /* The actual wait call. |
| */ |
| ttw_status = call_real_ttrace_wait (wait_pid, wait_tid, option, tsp, tsp_size); |
| |
| /* Note that the routines we'll call will be using "call_real_ttrace", |
| * not "call_ttrace", and thus need the real pid rather than the pseudo-tid |
| * the rest of the world uses (which is actually the tid). |
| */ |
| real_pid = tsp->tts_pid; |
| |
| /* For most events: Stop the world! |
| |
| * It's sometimes not safe to stop all threads of a process. |
| * Sometimes it's not even safe to ask for the thread state |
| * of a process! |
| */ |
| if (can_touch_threads_of_process (real_pid, tsp->tts_event)) |
| { |
| /* If we're really only stepping a single thread, then don't |
| * try to stop all the others -- we only do this single-stepping |
| * business when all others were already stopped...and the stop |
| * would mess up other threads' events. |
| * |
| * Similiarly, if there are other threads with events, |
| * don't do the stop. |
| */ |
| if (!doing_fake_step) |
| { |
| if (more_events_left > 0) |
| warning ("Internal error in stopping process"); |
| |
| stop_all_threads_of_process (real_pid); |
| |
| /* At this point, we could scan and update_thread_list(), |
| * and only use the local list for the rest of the |
| * module! We'd get rid of the scans in the various |
| * continue routines (adding one in attach). It'd |
| * be great--UPGRADE ME! |
| */ |
| } |
| } |
| |
| #ifdef PARANOIA |
| else if (debug_on) |
| { |
| if (more_events_left > 0) |
| printf ("== Can't stop process; more events!\n"); |
| else |
| printf ("== Can't stop process!\n"); |
| } |
| #endif |
| |
| process_state = STOPPED; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Process set to STOPPED\n"); |
| #endif |
| } |
| |
| else |
| { |
| /* Fake a call to ttrace_wait. The process must be |
| * STOPPED, as we aren't going to do any wait. |
| */ |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("TW: fake it\n"); |
| #endif |
| |
| if (process_state != STOPPED) |
| { |
| warning ("Process not stopped at wait call, in state '%s'.\n", |
| get_printable_name_of_process_state (process_state)); |
| } |
| |
| if (doing_fake_step) |
| error ("Internal error in stepping over breakpoint"); |
| |
| ttw_status = 0; /* Faking it is always successful! */ |
| } /* End of fake or not? if */ |
| |
| /* Pick an event to pass to our caller. Be paranoid. |
| */ |
| if (!select_stopped_thread_of_process (real_pid, tsp)) |
| warning ("Can't find event, using previous event."); |
| |
| else if (tsp->tts_event == TTEVT_NONE) |
| warning ("Internal error: no thread has a real event."); |
| |
| else if (doing_fake_step) |
| { |
| if (fake_step_tid != tsp->tts_lwpid) |
| warning ("Internal error in stepping over breakpoint."); |
| |
| /* This wait clears the (current) fake-step if there was one. |
| */ |
| doing_fake_step = 0; |
| fake_step_tid = 0; |
| } |
| |
| /* We now have a correct tsp and ttw_status for the thread |
| * which we want to report. So it's "handled"! This call |
| * will add it to our list if it's not there already. |
| */ |
| set_handled (real_pid, tsp->tts_lwpid); |
| |
| /* Save a copy of the ttrace state of this thread, in our local |
| thread descriptor. |
| |
| This caches the state. The implementation of queries like |
| hpux_has_execd can then use this cached state, rather than |
| be forced to make an explicit ttrace call to get it. |
| |
| (Guard against the condition that this is the first time we've |
| waited on, i.e., seen this thread, and so haven't yet entered |
| it into our list of threads.) |
| */ |
| tinfo = find_thread_info (tsp->tts_lwpid); |
| if (tinfo != NULL) |
| { |
| copy_ttstate_t (&tinfo->last_stop_state, tsp); |
| tinfo->have_state = 1; |
| } |
| |
| return ttw_status; |
| } /* call_ttrace_wait */ |
| |
| #if defined(CHILD_REPORTED_EXEC_EVENTS_PER_EXEC_CALL) |
| int |
| child_reported_exec_events_per_exec_call (void) |
| { |
| return 1; /* ttrace reports the event once per call. */ |
| } |
| #endif |
| |
| |
| |
| /* Our implementation of hardware watchpoints involves making memory |
| pages write-protected. We must remember a page's original permissions, |
| and we must also know when it is appropriate to restore a page's |
| permissions to its original state. |
| |
| We use a "dictionary" of hardware-watched pages to do this. Each |
| hardware-watched page is recorded in the dictionary. Each page's |
| dictionary entry contains the original permissions and a reference |
| count. Pages are hashed into the dictionary by their start address. |
| |
| When hardware watchpoint is set on page X for the first time, page X |
| is added to the dictionary with a reference count of 1. If other |
| hardware watchpoints are subsequently set on page X, its reference |
| count is incremented. When hardware watchpoints are removed from |
| page X, its reference count is decremented. If a page's reference |
| count drops to 0, it's permissions are restored and the page's entry |
| is thrown out of the dictionary. |
| */ |
| typedef struct memory_page |
| { |
| CORE_ADDR page_start; |
| int reference_count; |
| int original_permissions; |
| struct memory_page *next; |
| struct memory_page *previous; |
| } |
| memory_page_t; |
| |
| #define MEMORY_PAGE_DICTIONARY_BUCKET_COUNT 128 |
| |
| static struct |
| { |
| LONGEST page_count; |
| int page_size; |
| int page_protections_allowed; |
| /* These are just the heads of chains of actual page descriptors. */ |
| memory_page_t buckets[MEMORY_PAGE_DICTIONARY_BUCKET_COUNT]; |
| } |
| memory_page_dictionary; |
| |
| |
| static void |
| require_memory_page_dictionary (void) |
| { |
| int i; |
| |
| /* Is the memory page dictionary ready for use? If so, we're done. */ |
| if (memory_page_dictionary.page_count >= (LONGEST) 0) |
| return; |
| |
| /* Else, initialize it. */ |
| memory_page_dictionary.page_count = (LONGEST) 0; |
| |
| for (i = 0; i < MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; i++) |
| { |
| memory_page_dictionary.buckets[i].page_start = (CORE_ADDR) 0; |
| memory_page_dictionary.buckets[i].reference_count = 0; |
| memory_page_dictionary.buckets[i].next = NULL; |
| memory_page_dictionary.buckets[i].previous = NULL; |
| } |
| } |
| |
| |
| static void |
| retire_memory_page_dictionary (void) |
| { |
| memory_page_dictionary.page_count = (LONGEST) - 1; |
| } |
| |
| |
| /* Write-protect the memory page that starts at this address. |
| |
| Returns the original permissions of the page. |
| */ |
| static int |
| write_protect_page (int pid, CORE_ADDR page_start) |
| { |
| int tt_status; |
| int original_permissions; |
| int new_permissions; |
| |
| tt_status = call_ttrace (TT_PROC_GET_MPROTECT, |
| pid, |
| (TTRACE_ARG_TYPE) page_start, |
| TT_NIL, |
| (TTRACE_ARG_TYPE) & original_permissions); |
| if (errno || (tt_status < 0)) |
| { |
| return 0; /* What else can we do? */ |
| } |
| |
| /* We'll also write-protect the page now, if that's allowed. */ |
| if (memory_page_dictionary.page_protections_allowed) |
| { |
| new_permissions = original_permissions & ~PROT_WRITE; |
| tt_status = call_ttrace (TT_PROC_SET_MPROTECT, |
| pid, |
| (TTRACE_ARG_TYPE) page_start, |
| (TTRACE_ARG_TYPE) memory_page_dictionary.page_size, |
| (TTRACE_ARG_TYPE) new_permissions); |
| if (errno || (tt_status < 0)) |
| { |
| return 0; /* What else can we do? */ |
| } |
| } |
| |
| return original_permissions; |
| } |
| |
| |
| /* Unwrite-protect the memory page that starts at this address, restoring |
| (what we must assume are) its original permissions. |
| */ |
| static void |
| unwrite_protect_page (int pid, CORE_ADDR page_start, int original_permissions) |
| { |
| int tt_status; |
| |
| tt_status = call_ttrace (TT_PROC_SET_MPROTECT, |
| pid, |
| (TTRACE_ARG_TYPE) page_start, |
| (TTRACE_ARG_TYPE) memory_page_dictionary.page_size, |
| (TTRACE_ARG_TYPE) original_permissions); |
| if (errno || (tt_status < 0)) |
| { |
| return; /* What else can we do? */ |
| } |
| } |
| |
| |
| /* Memory page-protections are used to implement "hardware" watchpoints |
| on HP-UX. |
| |
| For every memory page that is currently being watched (i.e., that |
| presently should be write-protected), write-protect it. |
| */ |
| void |
| hppa_enable_page_protection_events (int pid) |
| { |
| int bucket; |
| |
| memory_page_dictionary.page_protections_allowed = 1; |
| |
| for (bucket = 0; bucket < MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; bucket++) |
| { |
| memory_page_t *page; |
| |
| page = memory_page_dictionary.buckets[bucket].next; |
| while (page != NULL) |
| { |
| page->original_permissions = write_protect_page (pid, page->page_start); |
| page = page->next; |
| } |
| } |
| } |
| |
| |
| /* Memory page-protections are used to implement "hardware" watchpoints |
| on HP-UX. |
| |
| For every memory page that is currently being watched (i.e., that |
| presently is or should be write-protected), un-write-protect it. |
| */ |
| void |
| hppa_disable_page_protection_events (int pid) |
| { |
| int bucket; |
| |
| for (bucket = 0; bucket < MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; bucket++) |
| { |
| memory_page_t *page; |
| |
| page = memory_page_dictionary.buckets[bucket].next; |
| while (page != NULL) |
| { |
| unwrite_protect_page (pid, page->page_start, page->original_permissions); |
| page = page->next; |
| } |
| } |
| |
| memory_page_dictionary.page_protections_allowed = 0; |
| } |
| |
| /* Count the number of outstanding events. At this |
| * point, we have selected one thread and its event |
| * as the one to be "reported" upwards to core gdb. |
| * That thread is already marked as "handled". |
| * |
| * Note: we could just scan our own thread list. FIXME! |
| */ |
| static int |
| count_unhandled_events (int real_pid, lwpid_t real_tid) |
| { |
| ttstate_t tstate; |
| lwpid_t ttid; |
| int events_left; |
| |
| /* Ok, find out how many threads have real events to report. |
| */ |
| events_left = 0; |
| ttid = get_process_first_stopped_thread_id (real_pid, &tstate); |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| { |
| if (ttid == 0) |
| printf ("Process %d has no threads\n", real_pid); |
| else |
| printf ("Process %d has these threads:\n", real_pid); |
| } |
| #endif |
| |
| while (ttid > 0) |
| { |
| if (tstate.tts_event != TTEVT_NONE |
| && !was_handled (ttid)) |
| { |
| /* TTEVT_NONE implies we just stopped it ourselves |
| * because we're the stop-the-world guys, so it's |
| * not an event from our point of view. |
| * |
| * If "was_handled" is true, this is an event we |
| * already handled, so don't count it. |
| * |
| * Note that we don't count the thread with the |
| * currently-reported event, as it's already marked |
| * as handled. |
| */ |
| events_left++; |
| } |
| |
| #if defined( THREAD_DEBUG ) || defined( WAIT_BUFFER_DEBUG ) |
| if (debug_on) |
| { |
| if (ttid == real_tid) |
| printf ("*"); /* Thread we're reporting */ |
| else |
| printf (" "); |
| |
| if (tstate.tts_event != TTEVT_NONE) |
| printf ("+"); /* Thread with a real event */ |
| else |
| printf (" "); |
| |
| if (was_handled (ttid)) |
| printf ("h"); /* Thread has been handled */ |
| else |
| printf (" "); |
| |
| printf (" %d, with event %s", ttid, |
| get_printable_name_of_ttrace_event (tstate.tts_event)); |
| |
| if (tstate.tts_event == TTEVT_SIGNAL |
| && 5 == tstate.tts_u.tts_signal.tts_signo) |
| { |
| CORE_ADDR pc_val; |
| |
| pc_val = get_raw_pc (ttid); |
| |
| if (pc_val > 0) |
| printf (" breakpoint at 0x%x\n", pc_val); |
| else |
| printf (" bpt, can't fetch pc.\n"); |
| } |
| else |
| printf ("\n"); |
| } |
| #endif |
| |
| ttid = get_process_next_stopped_thread_id (real_pid, &tstate); |
| } |
| |
| #if defined( THREAD_DEBUG ) || defined( WAIT_BUFFER_DEBUG ) |
| if (debug_on) |
| if (events_left > 0) |
| printf ("There are thus %d pending events\n", events_left); |
| #endif |
| |
| return events_left; |
| } |
| |
| /* This function is provided as a sop to clients that are calling |
| * ptrace_wait to wait for a process to stop. (see the |
| * implementation of child_wait.) Return value is the pid for |
| * the event that ended the wait. |
| * |
| * Note: used by core gdb and so uses the pseudo-pid (really tid). |
| */ |
| int |
| ptrace_wait (ptid_t ptid, int *status) |
| { |
| ttstate_t tsp; |
| int ttwait_return; |
| int real_pid; |
| ttstate_t state; |
| lwpid_t real_tid; |
| int return_pid; |
| |
| /* The ptrace implementation of this also ignores pid. |
| */ |
| *status = 0; |
| |
| ttwait_return = call_ttrace_wait (0, TTRACE_WAITOK, &tsp, sizeof (tsp)); |
| if (ttwait_return < 0) |
| { |
| /* ??rehrauer: It appears that if our inferior exits and we |
| haven't asked for exit events, that we're not getting any |
| indication save a negative return from ttrace_wait and an |
| errno set to ESRCH? |
| */ |
| if (errno == ESRCH) |
| { |
| *status = 0; /* WIFEXITED */ |
| return PIDGET (inferior_ptid); |
| } |
| |
| warning ("Call of ttrace_wait returned with errno %d.", |
| errno); |
| *status = ttwait_return; |
| return PIDGET (inferior_ptid); |
| } |
| |
| real_pid = tsp.tts_pid; |
| real_tid = tsp.tts_lwpid; |
| |
| /* One complication is that the "tts_event" structure has |
| * a set of flags, and more than one can be set. So we |
| * either have to force an order (as we do here), or handle |
| * more than one flag at a time. |
| */ |
| if (tsp.tts_event & TTEVT_LWP_CREATE) |
| { |
| |
| /* Unlike what you might expect, this event is reported in |
| * the _creating_ thread, and the _created_ thread (whose tid |
| * we have) is still running. So we have to stop it. This |
| * has already been done in "call_ttrace_wait", but should we |
| * ever abandon the "stop-the-world" model, here's the command |
| * to use: |
| * |
| * call_ttrace( TT_LWP_STOP, real_tid, TT_NIL, TT_NIL, TT_NIL ); |
| * |
| * Note that this would depend on being called _after_ "add_tthread" |
| * below for the tid-to-pid translation to be done in "call_ttrace". |
| */ |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("New thread: pid %d, tid %d, creator tid %d\n", |
| real_pid, tsp.tts_u.tts_thread.tts_target_lwpid, |
| real_tid); |
| #endif |
| |
| /* Now we have to return the tid of the created thread, not |
| * the creating thread, or "wait_for_inferior" won't know we |
| * have a new "process" (thread). Plus we should record it |
| * right, too. |
| */ |
| real_tid = tsp.tts_u.tts_thread.tts_target_lwpid; |
| |
| add_tthread (real_pid, real_tid); |
| } |
| |
| else if ((tsp.tts_event & TTEVT_LWP_TERMINATE) |
| || (tsp.tts_event & TTEVT_LWP_EXIT)) |
| { |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Thread dies: %d\n", real_tid); |
| #endif |
| |
| del_tthread (real_tid); |
| } |
| |
| else if (tsp.tts_event & TTEVT_EXEC) |
| { |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Pid %d has zero'th thread %d; inferior pid is %d\n", |
| real_pid, real_tid, PIDGET (inferior_ptid)); |
| #endif |
| |
| add_tthread (real_pid, real_tid); |
| } |
| |
| #ifdef THREAD_DEBUG |
| else if (debug_on) |
| { |
| printf ("Process-level event %s, using tid %d\n", |
| get_printable_name_of_ttrace_event (tsp.tts_event), |
| real_tid); |
| |
| /* OK to do this, as "add_tthread" won't add |
| * duplicate entries. Also OK not to do it, |
| * as this event isn't one which can change the |
| * thread state. |
| */ |
| add_tthread (real_pid, real_tid); |
| } |
| #endif |
| |
| |
| /* How many events are left to report later? |
| * In a non-stop-the-world model, this isn't needed. |
| * |
| * Note that it's not always safe to query the thread state of a process, |
| * which is what count_unhandled_events does. (If unsafe, we're left with |
| * no other resort than to assume that no more events remain...) |
| */ |
| if (can_touch_threads_of_process (real_pid, tsp.tts_event)) |
| more_events_left = count_unhandled_events (real_pid, real_tid); |
| |
| else |
| { |
| if (more_events_left > 0) |
| warning ("Vfork or fork causing loss of %d buffered events.", |
| more_events_left); |
| |
| more_events_left = 0; |
| } |
| |
| /* Attempt to translate the ttrace_wait-returned status into the |
| ptrace equivalent. |
| |
| ??rehrauer: This is somewhat fragile. We really ought to rewrite |
| clients that expect to pick apart a ptrace wait status, to use |
| something a little more abstract. |
| */ |
| if ((tsp.tts_event & TTEVT_EXEC) |
| || (tsp.tts_event & TTEVT_FORK) |
| || (tsp.tts_event & TTEVT_VFORK)) |
| { |
| /* Forks come in pairs (parent and child), so core gdb |
| * will do two waits. Be ready to notice this. |
| */ |
| if (tsp.tts_event & TTEVT_FORK) |
| { |
| process_state = FORKING; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Process set to FORKING\n"); |
| #endif |
| } |
| else if (tsp.tts_event & TTEVT_VFORK) |
| { |
| process_state = VFORKING; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Process set to VFORKING\n"); |
| #endif |
| } |
| |
| /* Make an exec or fork look like a breakpoint. Definitely a hack, |
| but I don't think non HP-UX-specific clients really carefully |
| inspect the first events they get after inferior startup, so |
| it probably almost doesn't matter what we claim this is. |
| */ |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("..a process 'event'\n"); |
| #endif |
| |
| /* Also make fork and exec events look like bpts, so they can be caught. |
| */ |
| *status = 0177 | (_SIGTRAP << 8); |
| } |
| |
| /* Special-cases: We ask for syscall entry and exit events to implement |
| "fast" (aka "hardware") watchpoints. |
| |
| When we get a syscall entry, we want to disable page-protections, |
| and resume the inferior; this isn't an event we wish for |
| wait_for_inferior to see. Note that we must resume ONLY the |
| thread that reported the syscall entry; we don't want to allow |
| other threads to run with the page protections off, as they might |
| then be able to write to watch memory without it being caught. |
| |
| When we get a syscall exit, we want to reenable page-protections, |
| but we don't want to resume the inferior; this is an event we wish |
| wait_for_inferior to see. Make it look like the signal we normally |
| get for a single-step completion. This should cause wait_for_inferior |
| to evaluate whether any watchpoint triggered. |
| |
| Or rather, that's what we'd LIKE to do for syscall exit; we can't, |
| due to some HP-UX "features". Some syscalls have problems with |
| write-protections on some pages, and some syscalls seem to have |
| pending writes to those pages at the time we're getting the return |
| event. So, we'll single-step the inferior to get out of the syscall, |
| and then reenable protections. |
| |
| Note that we're intentionally allowing the syscall exit case to |
| fall through into the succeeding cases, as sometimes we single- |
| step out of one syscall only to immediately enter another... |
| */ |
| else if ((tsp.tts_event & TTEVT_SYSCALL_ENTRY) |
| || (tsp.tts_event & TTEVT_SYSCALL_RETURN)) |
| { |
| /* Make a syscall event look like a breakpoint. Same comments |
| as for exec & fork events. |
| */ |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("..a syscall 'event'\n"); |
| #endif |
| |
| /* Also make syscall events look like bpts, so they can be caught. |
| */ |
| *status = 0177 | (_SIGTRAP << 8); |
| } |
| |
| else if ((tsp.tts_event & TTEVT_LWP_CREATE) |
| || (tsp.tts_event & TTEVT_LWP_TERMINATE) |
| || (tsp.tts_event & TTEVT_LWP_EXIT)) |
| { |
| /* Make a thread event look like a breakpoint. Same comments |
| * as for exec & fork events. |
| */ |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("..a thread 'event'\n"); |
| #endif |
| |
| /* Also make thread events look like bpts, so they can be caught. |
| */ |
| *status = 0177 | (_SIGTRAP << 8); |
| } |
| |
| else if ((tsp.tts_event & TTEVT_EXIT)) |
| { /* WIFEXITED */ |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("..an exit\n"); |
| #endif |
| |
| /* Prevent rest of gdb from thinking this is |
| * a new thread if for some reason it's never |
| * seen the main thread before. |
| */ |
| inferior_ptid = pid_to_ptid (map_to_gdb_tid (real_tid)); /* HACK, FIX */ |
| |
| *status = 0 | (tsp.tts_u.tts_exit.tts_exitcode); |
| } |
| |
| else if (tsp.tts_event & TTEVT_SIGNAL) |
| { /* WIFSTOPPED */ |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("..a signal, %d\n", tsp.tts_u.tts_signal.tts_signo); |
| #endif |
| |
| *status = 0177 | (tsp.tts_u.tts_signal.tts_signo << 8); |
| } |
| |
| else |
| { /* !WIFSTOPPED */ |
| |
| /* This means the process or thread terminated. But we should've |
| caught an explicit exit/termination above. So warn (this is |
| really an internal error) and claim the process or thread |
| terminated with a SIGTRAP. |
| */ |
| |
| warning ("process_wait: unknown process state"); |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Process-level event %s, using tid %d\n", |
| get_printable_name_of_ttrace_event (tsp.tts_event), |
| real_tid); |
| #endif |
| |
| *status = _SIGTRAP; |
| } |
| |
| target_post_wait (pid_to_ptid (tsp.tts_pid), *status); |
| |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Done waiting, pid is %d, tid %d\n", real_pid, real_tid); |
| #endif |
| |
| /* All code external to this module uses the tid, but calls |
| * it "pid". There's some tweaking so that the outside sees |
| * the first thread as having the same number as the starting |
| * pid. |
| */ |
| return_pid = map_to_gdb_tid (real_tid); |
| |
| /* Remember this for later use in "hppa_prepare_to_proceed". |
| */ |
| old_gdb_pid = PIDGET (inferior_ptid); |
| reported_pid = return_pid; |
| reported_bpt = ((tsp.tts_event & TTEVT_SIGNAL) && (5 == tsp.tts_u.tts_signal.tts_signo)); |
| |
| if (real_tid == 0 || return_pid == 0) |
| { |
| warning ("Internal error: process-wait failed."); |
| } |
| |
| return return_pid; |
| } |
| |
| |
| /* This function causes the caller's process to be traced by its |
| parent. This is intended to be called after GDB forks itself, |
| and before the child execs the target. Despite the name, it |
| is called by the child. |
| |
| Note that HP-UX ttrace is rather funky in how this is done. |
| If the parent wants to get the initial exec event of a child, |
| it must set the ttrace event mask of the child to include execs. |
| (The child cannot do this itself.) This must be done after the |
| child is forked, but before it execs. |
| |
| To coordinate the parent and child, we implement a semaphore using |
| pipes. After SETTRC'ing itself, the child tells the parent that |
| it is now traceable by the parent, and waits for the parent's |
| acknowledgement. The parent can then set the child's event mask, |
| and notify the child that it can now exec. |
| |
| (The acknowledgement by parent happens as a result of a call to |
| child_acknowledge_created_inferior.) |
| */ |
| int |
| parent_attach_all (void) |
| { |
| int tt_status; |
| |
| /* We need a memory home for a constant, to pass it to ttrace. |
| The value of the constant is arbitrary, so long as both |
| parent and child use the same value. Might as well use the |
| "magic" constant provided by ttrace... |
| */ |
| uint64_t tc_magic_child = TT_VERSION; |
| uint64_t tc_magic_parent = 0; |
| |
| tt_status = call_real_ttrace ( |
| TT_PROC_SETTRC, |
| (int) TT_NIL, |
| (lwpid_t) TT_NIL, |
| TT_NIL, |
| (TTRACE_ARG_TYPE) TT_VERSION, |
| TT_NIL); |
| |
| if (tt_status < 0) |
| return tt_status; |
| |
| /* Notify the parent that we're potentially ready to exec(). */ |
| write (startup_semaphore.child_channel[SEM_TALK], |
| &tc_magic_child, |
| sizeof (tc_magic_child)); |
| |
| /* Wait for acknowledgement from the parent. */ |
| read (startup_semaphore.parent_channel[SEM_LISTEN], |
| &tc_magic_parent, |
| sizeof (tc_magic_parent)); |
| |
| if (tc_magic_child != tc_magic_parent) |
| warning ("mismatched semaphore magic"); |
| |
| /* Discard our copy of the semaphore. */ |
| (void) close (startup_semaphore.parent_channel[SEM_LISTEN]); |
| (void) close (startup_semaphore.parent_channel[SEM_TALK]); |
| (void) close (startup_semaphore.child_channel[SEM_LISTEN]); |
| (void) close (startup_semaphore.child_channel[SEM_TALK]); |
| |
| return tt_status; |
| } |
| |
| /* Despite being file-local, this routine is dealing with |
| * actual process IDs, not thread ids. That's because it's |
| * called before the first "wait" call, and there's no map |
| * yet from tids to pids. |
| * |
| * When it is called, a forked child is running, but waiting on |
| * the semaphore. If you stop the child and re-start it, |
| * things get confused, so don't do that! An attached child is |
| * stopped. |
| * |
| * Since this is called after either attach or run, we |
| * have to be the common part of both. |
| */ |
| static void |
| require_notification_of_events (int real_pid) |
| { |
| int tt_status; |
| ttevent_t notifiable_events; |
| |
| lwpid_t tid; |
| ttstate_t thread_state; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Require notif, pid is %d\n", real_pid); |
| #endif |
| |
| /* Temporary HACK: tell inftarg.c/child_wait to not |
| * loop until pids are the same. |
| */ |
| not_same_real_pid = 0; |
| |
| sigemptyset (¬ifiable_events.tte_signals); |
| notifiable_events.tte_opts = TTEO_NONE; |
| |
| /* This ensures that forked children inherit their parent's |
| * event mask, which we're setting here. |
| * |
| * NOTE: if you debug gdb with itself, then the ultimate |
| * debuggee gets flags set by the outermost gdb, as |
| * a child of a child will still inherit. |
| */ |
| notifiable_events.tte_opts |= TTEO_PROC_INHERIT; |
| |
| notifiable_events.tte_events = TTEVT_DEFAULT; |
| notifiable_events.tte_events |= TTEVT_SIGNAL; |
| notifiable_events.tte_events |= TTEVT_EXEC; |
| notifiable_events.tte_events |= TTEVT_EXIT; |
| notifiable_events.tte_events |= TTEVT_FORK; |
| notifiable_events.tte_events |= TTEVT_VFORK; |
| notifiable_events.tte_events |= TTEVT_LWP_CREATE; |
| notifiable_events.tte_events |= TTEVT_LWP_EXIT; |
| notifiable_events.tte_events |= TTEVT_LWP_TERMINATE; |
| |
| tt_status = call_real_ttrace ( |
| TT_PROC_SET_EVENT_MASK, |
| real_pid, |
| (lwpid_t) TT_NIL, |
| (TTRACE_ARG_TYPE) & notifiable_events, |
| (TTRACE_ARG_TYPE) sizeof (notifiable_events), |
| TT_NIL); |
| } |
| |
| static void |
| require_notification_of_exec_events (int real_pid) |
| { |
| int tt_status; |
| ttevent_t notifiable_events; |
| |
| lwpid_t tid; |
| ttstate_t thread_state; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Require notif, pid is %d\n", real_pid); |
| #endif |
| |
| /* Temporary HACK: tell inftarg.c/child_wait to not |
| * loop until pids are the same. |
| */ |
| not_same_real_pid = 0; |
| |
| sigemptyset (¬ifiable_events.tte_signals); |
| notifiable_events.tte_opts = TTEO_NOSTRCCHLD; |
| |
| /* This ensures that forked children don't inherit their parent's |
| * event mask, which we're setting here. |
| */ |
| notifiable_events.tte_opts &= ~TTEO_PROC_INHERIT; |
| |
| notifiable_events.tte_events = TTEVT_DEFAULT; |
| notifiable_events.tte_events |= TTEVT_EXEC; |
| notifiable_events.tte_events |= TTEVT_EXIT; |
| |
| tt_status = call_real_ttrace ( |
| TT_PROC_SET_EVENT_MASK, |
| real_pid, |
| (lwpid_t) TT_NIL, |
| (TTRACE_ARG_TYPE) & notifiable_events, |
| (TTRACE_ARG_TYPE) sizeof (notifiable_events), |
| TT_NIL); |
| } |
| |
| |
| /* This function is called by the parent process, with pid being the |
| * ID of the child process, after the debugger has forked. |
| */ |
| void |
| child_acknowledge_created_inferior (int pid) |
| { |
| /* We need a memory home for a constant, to pass it to ttrace. |
| The value of the constant is arbitrary, so long as both |
| parent and child use the same value. Might as well use the |
| "magic" constant provided by ttrace... |
| */ |
| uint64_t tc_magic_parent = TT_VERSION; |
| uint64_t tc_magic_child = 0; |
| |
| /* Wait for the child to tell us that it has forked. */ |
| read (startup_semaphore.child_channel[SEM_LISTEN], |
| &tc_magic_child, |
| sizeof (tc_magic_child)); |
| |
| /* Clear thread info now. We'd like to do this in |
| * "require...", but that messes up attach. |
| */ |
| clear_thread_info (); |
| |
| /* Tell the "rest of gdb" that the initial thread exists. |
| * This isn't really a hack. Other thread-based versions |
| * of gdb (e.g. gnu-nat.c) seem to do the same thing. |
| * |
| * Q: Why don't we also add this thread to the local |
| * list via "add_tthread"? |
| * |
| * A: Because we don't know the tid, and can't stop the |
| * the process safely to ask what it is. Anyway, we'll |
| * add it when it gets the EXEC event. |
| */ |
| add_thread (pid_to_ptid (pid)); /* in thread.c */ |
| |
| /* We can now set the child's ttrace event mask. |
| */ |
| require_notification_of_exec_events (pid); |
| |
| /* Tell ourselves that the process is running. |
| */ |
| process_state = RUNNING; |
| |
| /* Notify the child that it can exec. */ |
| write (startup_semaphore.parent_channel[SEM_TALK], |
| &tc_magic_parent, |
| sizeof (tc_magic_parent)); |
| |
| /* Discard our copy of the semaphore. */ |
| (void) close (startup_semaphore.parent_channel[SEM_LISTEN]); |
| (void) close (startup_semaphore.parent_channel[SEM_TALK]); |
| (void) close (startup_semaphore.child_channel[SEM_LISTEN]); |
| (void) close (startup_semaphore.child_channel[SEM_TALK]); |
| } |
| |
| |
| /* |
| * arrange for notification of all events by |
| * calling require_notification_of_events. |
| */ |
| void |
| child_post_startup_inferior (ptid_t ptid) |
| { |
| require_notification_of_events (PIDGET (ptid)); |
| } |
| |
| /* From here on, we should expect tids rather than pids. |
| */ |
| static void |
| hppa_enable_catch_fork (int tid) |
| { |
| int tt_status; |
| ttevent_t ttrace_events; |
| |
| /* Get the set of events that are currently enabled. |
| */ |
| tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| /* Add forks to that set. */ |
| ttrace_events.tte_events |= TTEVT_FORK; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("enable fork, tid is %d\n", tid); |
| #endif |
| |
| tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| } |
| |
| |
| static void |
| hppa_disable_catch_fork (int tid) |
| { |
| int tt_status; |
| ttevent_t ttrace_events; |
| |
| /* Get the set of events that are currently enabled. |
| */ |
| tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| /* Remove forks from that set. */ |
| ttrace_events.tte_events &= ~TTEVT_FORK; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("disable fork, tid is %d\n", tid); |
| #endif |
| |
| tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| } |
| |
| |
| #if defined(CHILD_INSERT_FORK_CATCHPOINT) |
| int |
| child_insert_fork_catchpoint (int tid) |
| { |
| /* Enable reporting of fork events from the kernel. */ |
| /* ??rehrauer: For the moment, we're always enabling these events, |
| and just ignoring them if there's no catchpoint to catch them. |
| */ |
| return 0; |
| } |
| #endif |
| |
| |
| #if defined(CHILD_REMOVE_FORK_CATCHPOINT) |
| int |
| child_remove_fork_catchpoint (int tid) |
| { |
| /* Disable reporting of fork events from the kernel. */ |
| /* ??rehrauer: For the moment, we're always enabling these events, |
| and just ignoring them if there's no catchpoint to catch them. |
| */ |
| return 0; |
| } |
| #endif |
| |
| |
| static void |
| hppa_enable_catch_vfork (int tid) |
| { |
| int tt_status; |
| ttevent_t ttrace_events; |
| |
| /* Get the set of events that are currently enabled. |
| */ |
| tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| /* Add vforks to that set. */ |
| ttrace_events.tte_events |= TTEVT_VFORK; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("enable vfork, tid is %d\n", tid); |
| #endif |
| |
| tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| } |
| |
| |
| static void |
| hppa_disable_catch_vfork (int tid) |
| { |
| int tt_status; |
| ttevent_t ttrace_events; |
| |
| /* Get the set of events that are currently enabled. */ |
| tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| /* Remove vforks from that set. */ |
| ttrace_events.tte_events &= ~TTEVT_VFORK; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("disable vfork, tid is %d\n", tid); |
| #endif |
| tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| } |
| |
| |
| #if defined(CHILD_INSERT_VFORK_CATCHPOINT) |
| int |
| child_insert_vfork_catchpoint (int tid) |
| { |
| /* Enable reporting of vfork events from the kernel. */ |
| /* ??rehrauer: For the moment, we're always enabling these events, |
| and just ignoring them if there's no catchpoint to catch them. |
| */ |
| return 0; |
| } |
| #endif |
| |
| |
| #if defined(CHILD_REMOVE_VFORK_CATCHPOINT) |
| int |
| child_remove_vfork_catchpoint (int tid) |
| { |
| /* Disable reporting of vfork events from the kernel. */ |
| /* ??rehrauer: For the moment, we're always enabling these events, |
| and just ignoring them if there's no catchpoint to catch them. |
| */ |
| return 0; |
| } |
| #endif |
| |
| /* Q: Do we need to map the returned process ID to a thread ID? |
| |
| * A: I don't think so--here we want a _real_ pid. Any later |
| * operations will call "require_notification_of_events" and |
| * start the mapping. |
| */ |
| int |
| hpux_has_forked (int tid, int *childpid) |
| { |
| int tt_status; |
| ttstate_t ttrace_state; |
| thread_info *tinfo; |
| |
| /* Do we have cached thread state that we can consult? If so, use it. */ |
| tinfo = find_thread_info (map_from_gdb_tid (tid)); |
| if (tinfo != NULL) |
| { |
| copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); |
| } |
| |
| /* Nope, must read the thread's current state */ |
| else |
| { |
| tt_status = call_ttrace (TT_LWP_GET_STATE, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_state, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_state), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| if (tt_status < 0) |
| return 0; |
| } |
| |
| if (ttrace_state.tts_event & TTEVT_FORK) |
| { |
| *childpid = ttrace_state.tts_u.tts_fork.tts_fpid; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* See hpux_has_forked for pid discussion. |
| */ |
| int |
| hpux_has_vforked (int tid, int *childpid) |
| { |
| int tt_status; |
| ttstate_t ttrace_state; |
| thread_info *tinfo; |
| |
| /* Do we have cached thread state that we can consult? If so, use it. */ |
| tinfo = find_thread_info (map_from_gdb_tid (tid)); |
| if (tinfo != NULL) |
| copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); |
| |
| /* Nope, must read the thread's current state */ |
| else |
| { |
| tt_status = call_ttrace (TT_LWP_GET_STATE, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_state, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_state), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| if (tt_status < 0) |
| return 0; |
| } |
| |
| if (ttrace_state.tts_event & TTEVT_VFORK) |
| { |
| *childpid = ttrace_state.tts_u.tts_fork.tts_fpid; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| #if defined(CHILD_INSERT_EXEC_CATCHPOINT) |
| int |
| child_insert_exec_catchpoint (int tid) |
| { |
| /* Enable reporting of exec events from the kernel. */ |
| /* ??rehrauer: For the moment, we're always enabling these events, |
| and just ignoring them if there's no catchpoint to catch them. |
| */ |
| return 0; |
| } |
| #endif |
| |
| |
| #if defined(CHILD_REMOVE_EXEC_CATCHPOINT) |
| int |
| child_remove_exec_catchpoint (int tid) |
| { |
| /* Disable reporting of execevents from the kernel. */ |
| /* ??rehrauer: For the moment, we're always enabling these events, |
| and just ignoring them if there's no catchpoint to catch them. |
| */ |
| return 0; |
| } |
| #endif |
| |
| |
| int |
| hpux_has_execd (int tid, char **execd_pathname) |
| { |
| int tt_status; |
| ttstate_t ttrace_state; |
| thread_info *tinfo; |
| |
| /* Do we have cached thread state that we can consult? If so, use it. */ |
| tinfo = find_thread_info (map_from_gdb_tid (tid)); |
| if (tinfo != NULL) |
| copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); |
| |
| /* Nope, must read the thread's current state */ |
| else |
| { |
| tt_status = call_ttrace (TT_LWP_GET_STATE, |
| tid, |
| (TTRACE_ARG_TYPE) & ttrace_state, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_state), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| if (tt_status < 0) |
| return 0; |
| } |
| |
| if (ttrace_state.tts_event & TTEVT_EXEC) |
| { |
| /* See child_pid_to_exec_file in this file: this is a macro. |
| */ |
| char *exec_file = target_pid_to_exec_file (tid); |
| |
| *execd_pathname = savestring (exec_file, strlen (exec_file)); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| int |
| hpux_has_syscall_event (int pid, enum target_waitkind *kind, int *syscall_id) |
| { |
| int tt_status; |
| ttstate_t ttrace_state; |
| thread_info *tinfo; |
| |
| /* Do we have cached thread state that we can consult? If so, use it. */ |
| tinfo = find_thread_info (map_from_gdb_tid (pid)); |
| if (tinfo != NULL) |
| copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); |
| |
| /* Nope, must read the thread's current state */ |
| else |
| { |
| tt_status = call_ttrace (TT_LWP_GET_STATE, |
| pid, |
| (TTRACE_ARG_TYPE) & ttrace_state, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_state), |
| TT_NIL); |
| |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| if (tt_status < 0) |
| return 0; |
| } |
| |
| *kind = TARGET_WAITKIND_SPURIOUS; /* Until proven otherwise... */ |
| *syscall_id = -1; |
| |
| if (ttrace_state.tts_event & TTEVT_SYSCALL_ENTRY) |
| *kind = TARGET_WAITKIND_SYSCALL_ENTRY; |
| else if (ttrace_state.tts_event & TTEVT_SYSCALL_RETURN) |
| *kind = TARGET_WAITKIND_SYSCALL_RETURN; |
| else |
| return 0; |
| |
| *syscall_id = ttrace_state.tts_scno; |
| return 1; |
| } |
| |
| |
| |
| #if defined(CHILD_THREAD_ALIVE) |
| |
| /* Check to see if the given thread is alive. |
| |
| * We'll trust the thread list, as the more correct |
| * approach of stopping the process and spinning down |
| * the OS's thread list is _very_ expensive. |
| * |
| * May need a FIXME for that reason. |
| */ |
| int |
| child_thread_alive (ptid_t ptid) |
| { |
| lwpid_t gdb_tid = PIDGET (ptid); |
| lwpid_t tid; |
| |
| /* This spins down the lists twice. |
| * Possible peformance improvement here! |
| */ |
| tid = map_from_gdb_tid (gdb_tid); |
| return !is_terminated (tid); |
| } |
| |
| #endif |
| |
| |
| |
| /* This function attempts to read the specified number of bytes from the |
| save_state_t that is our view into the hardware registers, starting at |
| ss_offset, and ending at ss_offset + sizeof_buf - 1 |
| |
| If this function succeeds, it deposits the fetched bytes into buf, |
| and returns 0. |
| |
| If it fails, it returns a negative result. The contents of buf are |
| undefined it this function fails. |
| */ |
| int |
| read_from_register_save_state (int tid, TTRACE_ARG_TYPE ss_offset, char *buf, |
| int sizeof_buf) |
| { |
| int tt_status; |
| register_value_t register_value = 0; |
| |
| tt_status = call_ttrace (TT_LWP_RUREGS, |
| tid, |
| ss_offset, |
| (TTRACE_ARG_TYPE) sizeof_buf, |
| (TTRACE_ARG_TYPE) buf); |
| |
| if (tt_status == 1) |
| /* Map ttrace's version of success to our version. |
| * Sometime ttrace returns 0, but that's ok here. |
| */ |
| return 0; |
| |
| return tt_status; |
| } |
| |
| |
| /* This function attempts to write the specified number of bytes to the |
| save_state_t that is our view into the hardware registers, starting at |
| ss_offset, and ending at ss_offset + sizeof_buf - 1 |
| |
| If this function succeeds, it deposits the bytes in buf, and returns 0. |
| |
| If it fails, it returns a negative result. The contents of the save_state_t |
| are undefined it this function fails. |
| */ |
| int |
| write_to_register_save_state (int tid, TTRACE_ARG_TYPE ss_offset, char *buf, |
| int sizeof_buf) |
| { |
| int tt_status; |
| register_value_t register_value = 0; |
| |
| tt_status = call_ttrace (TT_LWP_WUREGS, |
| tid, |
| ss_offset, |
| (TTRACE_ARG_TYPE) sizeof_buf, |
| (TTRACE_ARG_TYPE) buf); |
| return tt_status; |
| } |
| |
| |
| /* This function is a sop to the largeish number of direct calls |
| to call_ptrace that exist in other files. Rather than create |
| functions whose name abstracts away from ptrace, and change all |
| the present callers of call_ptrace, we'll do the expedient (and |
| perhaps only practical) thing. |
| |
| Note HP-UX explicitly disallows a mix of ptrace & ttrace on a traced |
| process. Thus, we must translate all ptrace requests into their |
| process-specific, ttrace equivalents. |
| */ |
| int |
| call_ptrace (int pt_request, int gdb_tid, PTRACE_ARG3_TYPE addr, int data) |
| { |
| ttreq_t tt_request; |
| TTRACE_ARG_TYPE tt_addr = (TTRACE_ARG_TYPE) addr; |
| TTRACE_ARG_TYPE tt_data = (TTRACE_ARG_TYPE) data; |
| TTRACE_ARG_TYPE tt_addr2 = TT_NIL; |
| int tt_status; |
| register_value_t register_value; |
| int read_buf; |
| |
| /* Perform the necessary argument translation. Note that some |
| cases are funky enough in the ttrace realm that we handle them |
| very specially. |
| */ |
| switch (pt_request) |
| { |
| /* The following cases cannot conveniently be handled conveniently |
| by merely adjusting the ptrace arguments and feeding into the |
| generic call to ttrace at the bottom of this function. |
| |
| Note that because all branches of this switch end in "return", |
| there's no need for any "break" statements. |
| */ |
| case PT_SETTRC: |
| return parent_attach_all (); |
| |
| case PT_RUREGS: |
| tt_status = read_from_register_save_state (gdb_tid, |
| tt_addr, |
| ®ister_value, |
| sizeof (register_value)); |
| if (tt_status < 0) |
| return tt_status; |
| return register_value; |
| |
| case PT_WUREGS: |
| register_value = (int) tt_data; |
| tt_status = write_to_register_save_state (gdb_tid, |
| tt_addr, |
| ®ister_value, |
| sizeof (register_value)); |
| return tt_status; |
| break; |
| |
| case PT_READ_I: |
| tt_status = call_ttrace (TT_PROC_RDTEXT, /* Implicit 4-byte xfer becomes block-xfer. */ |
| gdb_tid, |
| tt_addr, |
| (TTRACE_ARG_TYPE) 4, |
| (TTRACE_ARG_TYPE) & read_buf); |
| if (tt_status < 0) |
| return tt_status; |
| return read_buf; |
| |
| case PT_READ_D: |
| tt_status = call_ttrace (TT_PROC_RDDATA, /* Implicit 4-byte xfer becomes block-xfer. */ |
| gdb_tid, |
| tt_addr, |
| (TTRACE_ARG_TYPE) 4, |
| (TTRACE_ARG_TYPE) & read_buf); |
| if (tt_status < 0) |
| return tt_status; |
| return read_buf; |
| |
| case PT_ATTACH: |
| tt_status = call_real_ttrace (TT_PROC_ATTACH, |
| map_from_gdb_tid (gdb_tid), |
| (lwpid_t) TT_NIL, |
| tt_addr, |
| (TTRACE_ARG_TYPE) TT_VERSION, |
| tt_addr2); |
| if (tt_status < 0) |
| return tt_status; |
| return tt_status; |
| |
| /* The following cases are handled by merely adjusting the ptrace |
| arguments and feeding into the generic call to ttrace. |
| */ |
| case PT_DETACH: |
| tt_request = TT_PROC_DETACH; |
| break; |
| |
| case PT_WRITE_I: |
| tt_request = TT_PROC_WRTEXT; /* Translates 4-byte xfer to block-xfer. */ |
| tt_data = 4; /* This many bytes. */ |
| tt_addr2 = (TTRACE_ARG_TYPE) & data; /* Address of xfer source. */ |
| break; |
| |
| case PT_WRITE_D: |
| tt_request = TT_PROC_WRDATA; /* Translates 4-byte xfer to block-xfer. */ |
| tt_data = 4; /* This many bytes. */ |
| tt_addr2 = (TTRACE_ARG_TYPE) & data; /* Address of xfer source. */ |
| break; |
| |
| case PT_RDTEXT: |
| tt_request = TT_PROC_RDTEXT; |
| break; |
| |
| case PT_RDDATA: |
| tt_request = TT_PROC_RDDATA; |
| break; |
| |
| case PT_WRTEXT: |
| tt_request = TT_PROC_WRTEXT; |
| break; |
| |
| case PT_WRDATA: |
| tt_request = TT_PROC_WRDATA; |
| break; |
| |
| case PT_CONTINUE: |
| tt_request = TT_PROC_CONTINUE; |
| break; |
| |
| case PT_STEP: |
| tt_request = TT_LWP_SINGLE; /* Should not be making this request? */ |
| break; |
| |
| case PT_KILL: |
| tt_request = TT_PROC_EXIT; |
| break; |
| |
| case PT_GET_PROCESS_PATHNAME: |
| tt_request = TT_PROC_GET_PATHNAME; |
| break; |
| |
| default: |
| tt_request = pt_request; /* Let ttrace be the one to complain. */ |
| break; |
| } |
| |
| return call_ttrace (tt_request, |
| gdb_tid, |
| tt_addr, |
| tt_data, |
| tt_addr2); |
| } |
| |
| /* Kill that pesky process! |
| */ |
| void |
| kill_inferior (void) |
| { |
| int tid; |
| int wait_status; |
| thread_info *t; |
| thread_info **paranoia; |
| int para_count, i; |
| |
| if (PIDGET (inferior_ptid) == 0) |
| return; |
| |
| /* Walk the list of "threads", some of which are "pseudo threads", |
| aka "processes". For each that is NOT inferior_ptid, stop it, |
| and detach it. |
| |
| You see, we may not have just a single process to kill. If we're |
| restarting or quitting or detaching just after the inferior has |
| forked, then we've actually two processes to clean up. |
| |
| But we can't just call target_mourn_inferior() for each, since that |
| zaps the target vector. |
| */ |
| |
| paranoia = (thread_info **) xmalloc (thread_head.count * |
| sizeof (thread_info *)); |
| para_count = 0; |
| |
| t = thread_head.head; |
| while (t) |
| { |
| |
| paranoia[para_count] = t; |
| for (i = 0; i < para_count; i++) |
| { |
| if (t->next == paranoia[i]) |
| { |
| warning ("Bad data in gdb's thread data; repairing."); |
| t->next = 0; |
| } |
| } |
| para_count++; |
| |
| if (t->am_pseudo && (t->pid != PIDGET (inferior_ptid))) |
| { |
| call_ttrace (TT_PROC_EXIT, |
| t->pid, |
| TT_NIL, |
| TT_NIL, |
| TT_NIL); |
| } |
| t = t->next; |
| } |
| |
| xfree (paranoia); |
| |
| call_ttrace (TT_PROC_EXIT, |
| PIDGET (inferior_ptid), |
| TT_NIL, |
| TT_NIL, |
| TT_NIL); |
| target_mourn_inferior (); |
| clear_thread_info (); |
| } |
| |
| |
| #ifndef CHILD_RESUME |
| |
| /* Sanity check a thread about to be continued. |
| */ |
| static void |
| thread_dropping_event_check (thread_info *p) |
| { |
| if (!p->handled) |
| { |
| /* |
| * This seems to happen when we "next" over a |
| * "fork()" while following the parent. If it's |
| * the FORK event, that's ok. If it's a SIGNAL |
| * in the unfollowed child, that's ok to--but |
| * how can we know that's what's going on? |
| * |
| * FIXME! |
| */ |
| if (p->have_state) |
| { |
| if (p->last_stop_state.tts_event == TTEVT_FORK) |
| { |
| /* Ok */ |
| ; |
| } |
| else if (p->last_stop_state.tts_event == TTEVT_SIGNAL) |
| { |
| /* Ok, close eyes and let it happen. |
| */ |
| ; |
| } |
| else |
| { |
| /* This shouldn't happen--we're dropping a |
| * real event. |
| */ |
| warning ("About to continue process %d, thread %d with unhandled event %s.", |
| p->pid, p->tid, |
| get_printable_name_of_ttrace_event ( |
| p->last_stop_state.tts_event)); |
| |
| #ifdef PARANOIA |
| if (debug_on) |
| print_tthread (p); |
| #endif |
| } |
| } |
| else |
| { |
| /* No saved state, have to assume it failed. |
| */ |
| warning ("About to continue process %d, thread %d with unhandled event.", |
| p->pid, p->tid); |
| #ifdef PARANOIA |
| if (debug_on) |
| print_tthread (p); |
| #endif |
| } |
| } |
| |
| } /* thread_dropping_event_check */ |
| |
| /* Use a loop over the threads to continue all the threads but |
| * the one specified, which is to be stepped. |
| */ |
| static void |
| threads_continue_all_but_one (lwpid_t gdb_tid, int signal) |
| { |
| thread_info *p; |
| int thread_signal; |
| lwpid_t real_tid; |
| lwpid_t scan_tid; |
| ttstate_t state; |
| int real_pid; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Using loop over threads to step/resume with signals\n"); |
| #endif |
| |
| /* First update the thread list. |
| */ |
| set_all_unseen (); |
| real_tid = map_from_gdb_tid (gdb_tid); |
| real_pid = get_pid_for (real_tid); |
| |
| scan_tid = get_process_first_stopped_thread_id (real_pid, &state); |
| while (0 != scan_tid) |
| { |
| |
| #ifdef THREAD_DEBUG |
| /* FIX: later should check state is stopped; |
| * state.tts_flags & TTS_STATEMASK == TTS_WASSUSPENDED |
| */ |
| if (debug_on) |
| if ((state.tts_flags & TTS_STATEMASK) != TTS_WASSUSPENDED) |
| printf ("About to continue non-stopped thread %d\n", scan_tid); |
| #endif |
| |
| p = find_thread_info (scan_tid); |
| if (NULL == p) |
| { |
| add_tthread (real_pid, scan_tid); |
| p = find_thread_info (scan_tid); |
| |
| /* This is either a newly-created thread or the |
| * result of a fork; in either case there's no |
| * actual event to worry about. |
| */ |
| p->handled = 1; |
| |
| if (state.tts_event != TTEVT_NONE) |
| { |
| /* Oops, do need to worry! |
| */ |
| warning ("Unexpected thread with \"%s\" event.", |
| get_printable_name_of_ttrace_event (state.tts_event)); |
| } |
| } |
| else if (scan_tid != p->tid) |
| error ("Bad data in thread database."); |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| if (p->terminated) |
| printf ("Why are we continuing a dead thread?\n"); |
| #endif |
| |
| p->seen = 1; |
| |
| scan_tid = get_process_next_stopped_thread_id (real_pid, &state); |
| } |
| |
| /* Remove unseen threads. |
| */ |
| update_thread_list (); |
| |
| /* Now run down the thread list and continue or step. |
| */ |
| for (p = thread_head.head; p; p = p->next) |
| { |
| |
| /* Sanity check. |
| */ |
| thread_dropping_event_check (p); |
| |
| /* Pass the correct signals along. |
| */ |
| if (p->have_signal) |
| { |
| thread_signal = p->signal_value; |
| p->have_signal = 0; |
| } |
| else |
| thread_signal = 0; |
| |
| if (p->tid != real_tid) |
| { |
| /* |
| * Not the thread of interest, so continue it |
| * as the user expects. |
| */ |
| if (p->stepping_mode == DO_STEP) |
| { |
| /* Just step this thread. |
| */ |
| call_ttrace ( |
| TT_LWP_SINGLE, |
| p->tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (signal), |
| TT_NIL); |
| } |
| else |
| { |
| /* Regular continue (default case). |
| */ |
| call_ttrace ( |
| TT_LWP_CONTINUE, |
| p->tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (thread_signal), |
| TT_NIL); |
| } |
| } |
| else |
| { |
| /* Step the thread of interest. |
| */ |
| call_ttrace ( |
| TT_LWP_SINGLE, |
| real_tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (signal), |
| TT_NIL); |
| } |
| } /* Loop over threads */ |
| } /* End threads_continue_all_but_one */ |
| |
| /* Use a loop over the threads to continue all the threads. |
| * This is done when a signal must be sent to any of the threads. |
| */ |
| static void |
| threads_continue_all_with_signals (lwpid_t gdb_tid, int signal) |
| { |
| thread_info *p; |
| int thread_signal; |
| lwpid_t real_tid; |
| lwpid_t scan_tid; |
| ttstate_t state; |
| int real_pid; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Using loop over threads to resume with signals\n"); |
| #endif |
| |
| /* Scan and update thread list. |
| */ |
| set_all_unseen (); |
| real_tid = map_from_gdb_tid (gdb_tid); |
| real_pid = get_pid_for (real_tid); |
| |
| scan_tid = get_process_first_stopped_thread_id (real_pid, &state); |
| while (0 != scan_tid) |
| { |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| if ((state.tts_flags & TTS_STATEMASK) != TTS_WASSUSPENDED) |
| warning ("About to continue non-stopped thread %d\n", scan_tid); |
| #endif |
| |
| p = find_thread_info (scan_tid); |
| if (NULL == p) |
| { |
| add_tthread (real_pid, scan_tid); |
| p = find_thread_info (scan_tid); |
| |
| /* This is either a newly-created thread or the |
| * result of a fork; in either case there's no |
| * actual event to worry about. |
| */ |
| p->handled = 1; |
| |
| if (state.tts_event != TTEVT_NONE) |
| { |
| /* Oops, do need to worry! |
| */ |
| warning ("Unexpected thread with \"%s\" event.", |
| get_printable_name_of_ttrace_event (state.tts_event)); |
| } |
| } |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| if (p->terminated) |
| printf ("Why are we continuing a dead thread? (1)\n"); |
| #endif |
| |
| p->seen = 1; |
| |
| scan_tid = get_process_next_stopped_thread_id (real_pid, &state); |
| } |
| |
| /* Remove unseen threads from our list. |
| */ |
| update_thread_list (); |
| |
| /* Continue the threads. |
| */ |
| for (p = thread_head.head; p; p = p->next) |
| { |
| |
| /* Sanity check. |
| */ |
| thread_dropping_event_check (p); |
| |
| /* Pass the correct signals along. |
| */ |
| if (p->tid == real_tid) |
| { |
| thread_signal = signal; |
| p->have_signal = 0; |
| } |
| else if (p->have_signal) |
| { |
| thread_signal = p->signal_value; |
| p->have_signal = 0; |
| } |
| else |
| thread_signal = 0; |
| |
| if (p->stepping_mode == DO_STEP) |
| { |
| call_ttrace ( |
| TT_LWP_SINGLE, |
| p->tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (signal), |
| TT_NIL); |
| } |
| else |
| { |
| /* Continue this thread (default case). |
| */ |
| call_ttrace ( |
| TT_LWP_CONTINUE, |
| p->tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (thread_signal), |
| TT_NIL); |
| } |
| } |
| } /* End threads_continue_all_with_signals */ |
| |
| /* Step one thread only. |
| */ |
| static void |
| thread_fake_step (lwpid_t tid, enum target_signal signal) |
| { |
| thread_info *p; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| { |
| printf ("Doing a fake-step over a bpt, etc. for %d\n", tid); |
| |
| if (is_terminated (tid)) |
| printf ("Why are we continuing a dead thread? (4)\n"); |
| } |
| #endif |
| |
| if (doing_fake_step) |
| warning ("Step while step already in progress."); |
| |
| /* See if there's a saved signal value for this |
| * thread to be passed on, but no current signal. |
| */ |
| p = find_thread_info (tid); |
| if (p != NULL) |
| { |
| if (p->have_signal && signal == TARGET_SIGNAL_0) |
| { |
| /* Pass on a saved signal. |
| */ |
| signal = p->signal_value; |
| } |
| |
| p->have_signal = 0; |
| } |
| |
| if (!p->handled) |
| warning ("Internal error: continuing unhandled thread."); |
| |
| call_ttrace (TT_LWP_SINGLE, |
| tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (signal), |
| TT_NIL); |
| |
| /* Do bookkeeping so "call_ttrace_wait" knows it has to wait |
| * for this thread only, and clear any saved signal info. |
| */ |
| doing_fake_step = 1; |
| fake_step_tid = tid; |
| |
| } /* End thread_fake_step */ |
| |
| /* Continue one thread when a signal must be sent to it. |
| */ |
| static void |
| threads_continue_one_with_signal (lwpid_t gdb_tid, int signal) |
| { |
| thread_info *p; |
| lwpid_t real_tid; |
| int real_pid; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Continuing one thread with a signal\n"); |
| #endif |
| |
| real_tid = map_from_gdb_tid (gdb_tid); |
| real_pid = get_pid_for (real_tid); |
| |
| p = find_thread_info (real_tid); |
| if (NULL == p) |
| { |
| add_tthread (real_pid, real_tid); |
| } |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| if (p->terminated) |
| printf ("Why are we continuing a dead thread? (2)\n"); |
| #endif |
| |
| if (!p->handled) |
| warning ("Internal error: continuing unhandled thread."); |
| |
| p->have_signal = 0; |
| |
| call_ttrace (TT_LWP_CONTINUE, |
| gdb_tid, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (signal), |
| TT_NIL); |
| } |
| #endif |
| |
| #ifndef CHILD_RESUME |
| |
| /* Resume execution of the inferior process. |
| |
| * This routine is in charge of setting the "handled" bits. |
| * |
| * If STEP is zero, continue it. |
| * If STEP is nonzero, single-step it. |
| * |
| * If SIGNAL is nonzero, give it that signal. |
| * |
| * If TID is -1, apply to all threads. |
| * If TID is not -1, apply to specified thread. |
| * |
| * STEP |
| * \ !0 0 |
| * TID \________________________________________________ |
| * | |
| * -1 | Step current Continue all threads |
| * | thread and (but which gets any |
| * | continue others signal?--We look at |
| * | "inferior_ptid") |
| * | |
| * N | Step _this_ thread Continue _this_ thread |
| * | and leave others and leave others |
| * | stopped; internally stopped; used only for |
| * | used by gdb, never hardware watchpoints |
| * | a user command. and attach, never a |
| * | user command. |
| */ |
| void |
| child_resume (ptid_t ptid, int step, enum target_signal signal) |
| { |
| int resume_all_threads; |
| lwpid_t tid; |
| process_state_t new_process_state; |
| lwpid_t gdb_tid = PIDGET (ptid); |
| |
| resume_all_threads = |
| (gdb_tid == INFTTRACE_ALL_THREADS) || |
| (vfork_in_flight); |
| |
| if (resume_all_threads) |
| { |
| /* Resume all threads, but first pick a tid value |
| * so we can get the pid when in call_ttrace doing |
| * the map. |
| */ |
| if (vfork_in_flight) |
| tid = vforking_child_pid; |
| else |
| tid = map_from_gdb_tid (PIDGET (inferior_ptid)); |
| } |
| else |
| tid = map_from_gdb_tid (gdb_tid); |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| { |
| if (more_events_left) |
| printf ("More events; "); |
| |
| if (signal != 0) |
| printf ("Sending signal %d; ", signal); |
| |
| if (resume_all_threads) |
| { |
| if (step == 0) |
| printf ("Continue process %d\n", tid); |
| else |
| printf ("Step/continue thread %d\n", tid); |
| } |
| else |
| { |
| if (step == 0) |
| printf ("Continue thread %d\n", tid); |
| else |
| printf ("Step just thread %d\n", tid); |
| } |
| |
| if (vfork_in_flight) |
| printf ("Vfork in flight\n"); |
| } |
| #endif |
| |
| if (process_state == RUNNING) |
| warning ("Internal error in resume logic; doing resume or step anyway."); |
| |
| if (!step /* Asked to continue... */ |
| && resume_all_threads /* whole process.. */ |
| && signal != 0 /* with a signal... */ |
| && more_events_left > 0) |
| { /* but we can't yet--save it! */ |
| |
| /* Continue with signal means we have to set the pending |
| * signal value for this thread. |
| */ |
| thread_info *k; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Saving signal %d for thread %d\n", signal, tid); |
| #endif |
| |
| k = find_thread_info (tid); |
| if (k != NULL) |
| { |
| k->have_signal = 1; |
| k->signal_value = signal; |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| if (k->terminated) |
| printf ("Why are we continuing a dead thread? (3)\n"); |
| #endif |
| |
| } |
| |
| #ifdef THREAD_DEBUG |
| else if (debug_on) |
| { |
| printf ("No thread info for tid %d\n", tid); |
| } |
| #endif |
| } |
| |
| /* Are we faking this "continue" or "step"? |
| |
| * We used to do steps by continuing all the threads for |
| * which the events had been handled already. While |
| * conceptually nicer (hides it all in a lower level), this |
| * can lead to starvation and a hang (e.g. all but one thread |
| * are unhandled at a breakpoint just before a "join" operation, |
| * and one thread is in the join, and the user wants to step that |
| * thread). |
| */ |
| if (resume_all_threads /* Whole process, therefore user command */ |
| && more_events_left > 0) |
| { /* But we can't do this yet--fake it! */ |
| thread_info *p; |
| |
| if (!step) |
| { |
| /* No need to do any notes on a per-thread |
| * basis--we're done! |
| */ |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Faking a process resume.\n"); |
| #endif |
| |
| return; |
| } |
| else |
| { |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Faking a process step.\n"); |
| #endif |
| |
| } |
| |
| p = find_thread_info (tid); |
| if (p == NULL) |
| { |
| warning ("No thread information for tid %d, 'next' command ignored.\n", tid); |
| return; |
| } |
| else |
| { |
| |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| if (p->terminated) |
| printf ("Why are we continuing a dead thread? (3.5)\n"); |
| #endif |
| |
| if (p->stepping_mode != DO_DEFAULT) |
| { |
| warning ("Step or continue command applied to thread which is already stepping or continuing; command ignored."); |
| |
| return; |
| } |
| |
| if (step) |
| p->stepping_mode = DO_STEP; |
| else |
| p->stepping_mode = DO_CONTINUE; |
| |
| return; |
| } /* Have thread info */ |
| } /* Must fake step or go */ |
| |
| /* Execept for fake-steps, from here on we know we are |
| * going to wind up with a running process which will |
| * need a real wait. |
| */ |
| new_process_state = RUNNING; |
| |
| /* An address of TT_USE_CURRENT_PC tells ttrace to continue from where |
| * it was. (If GDB wanted it to start some other way, we have already |
| * written a new PC value to the child.) |
| * |
| * If this system does not support PT_STEP, a higher level function will |
| * have called single_step() to transmute the step request into a |
| * continue request (by setting breakpoints on all possible successor |
| * instructions), so we don't have to worry about that here. |
| */ |
| if (step) |
| { |
| if (resume_all_threads) |
| { |
| /* |
| * Regular user step: other threads get a "continue". |
| */ |
| threads_continue_all_but_one (tid, signal); |
| clear_all_handled (); |
| clear_all_stepping_mode (); |
| } |
| |
| else |
| { |
| /* "Fake step": gdb is stepping one thread over a |
| * breakpoint, watchpoint, or out of a library load |
| * event, etc. The rest just stay where they are. |
| * |
| * Also used when there are pending events: we really |
| * step the current thread, but leave the rest stopped. |
| * Users can't request this, but "wait_for_inferior" |
| * does--a lot! |
| */ |
| thread_fake_step (tid, signal); |
| |
| /* Clear the "handled" state of this thread, because |
| * we'll soon get a new event for it. Other events |
| * stay as they were. |
| */ |
| clear_handled (tid); |
| clear_stepping_mode (tid); |
| new_process_state = FAKE_STEPPING; |
| } |
| } |
| |
| else |
| { |
| /* TT_LWP_CONTINUE can pass signals to threads, TT_PROC_CONTINUE can't. |
| Therefore, we really can't use TT_PROC_CONTINUE here. |
| |
| Consider a process which stopped due to signal which gdb decides |
| to handle and not pass on to the inferior. In that case we must |
| clear the pending signal by restarting the inferior using |
| TT_LWP_CONTINUE and pass zero as the signal number. Else the |
| pending signal will be passed to the inferior. interrupt.exp |
| in the testsuite does this precise thing and fails due to the |
| unwanted signal delivery to the inferior. */ |
| /* drow/2002-12-05: However, note that we must use TT_PROC_CONTINUE |
| if we are tracing a vfork. */ |
| if (vfork_in_flight) |
| { |
| call_ttrace (TT_PROC_CONTINUE, tid, TT_NIL, TT_NIL, TT_NIL); |
| clear_all_handled (); |
| clear_all_stepping_mode (); |
| } |
| else if (resume_all_threads) |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Doing a continue by loop of all threads\n"); |
| #endif |
| |
| threads_continue_all_with_signals (tid, signal); |
| |
| clear_all_handled (); |
| clear_all_stepping_mode (); |
| } |
| else |
| { |
| #ifdef THREAD_DEBUG |
| printf ("Doing a continue w/signal of just thread %d\n", tid); |
| #endif |
| |
| threads_continue_one_with_signal (tid, signal); |
| |
| /* Clear the "handled" state of this thread, because we |
| will soon get a new event for it. Other events can |
| stay as they were. */ |
| clear_handled (tid); |
| clear_stepping_mode (tid); |
| } |
| } |
| |
| process_state = new_process_state; |
| |
| #ifdef WAIT_BUFFER_DEBUG |
| if (debug_on) |
| printf ("Process set to %s\n", |
| get_printable_name_of_process_state (process_state)); |
| #endif |
| |
| } |
| #endif /* CHILD_RESUME */ |
| |
| |
| #ifdef ATTACH_DETACH |
| /* |
| * Like it says. |
| * |
| * One worry is that we may not be attaching to "inferior_ptid" |
| * and thus may not want to clear out our data. FIXME? |
| * |
| */ |
| static void |
| update_thread_state_after_attach (int pid, attach_continue_t kind_of_go) |
| { |
| int tt_status; |
| ttstate_t thread_state; |
| lwpid_t a_thread; |
| lwpid_t tid; |
| |
| /* The process better be stopped. |
| */ |
| if (process_state != STOPPED |
| && process_state != VFORKING) |
| warning ("Internal error attaching."); |
| |
| /* Clear out old tthread info and start over. This has the |
| * side effect of ensuring that the TRAP is reported as being |
| * in the right thread (re-mapped from tid to pid). |
| * |
| * It's because we need to add the tthread _now_ that we |
| * need to call "clear_thread_info" _now_, and that's why |
| * "require_notification_of_events" doesn't clear the thread |
| * info (it's called later than this routine). |
| */ |
| clear_thread_info (); |
| a_thread = 0; |
| |
| for (tid = get_process_first_stopped_thread_id (pid, &thread_state); |
| tid != 0; |
| tid = get_process_next_stopped_thread_id (pid, &thread_state)) |
| { |
| thread_info *p; |
| |
| if (a_thread == 0) |
| { |
| a_thread = tid; |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("Attaching to process %d, thread %d\n", |
| pid, a_thread); |
| #endif |
| } |
| |
| /* Tell ourselves and the "rest of gdb" that this thread |
| * exists. |
| * |
| * This isn't really a hack. Other thread-based versions |
| * of gdb (e.g. gnu-nat.c) seem to do the same thing. |
| * |
| * We don't need to do mapping here, as we know this |
| * is the first thread and thus gets the real pid |
| * (and is "inferior_ptid"). |
| * |
| * NOTE: it probably isn't the originating thread, |
| * but that doesn't matter (we hope!). |
| */ |
| add_tthread (pid, tid); |
| p = find_thread_info (tid); |
| if (NULL == p) /* ?We just added it! */ |
| error ("Internal error adding a thread on attach."); |
| |
| copy_ttstate_t (&p->last_stop_state, &thread_state); |
| p->have_state = 1; |
| |
| if (DO_ATTACH_CONTINUE == kind_of_go) |
| { |
| /* |
| * If we are going to CONTINUE afterwards, |
| * raising a SIGTRAP, don't bother trying to |
| * handle this event. But check first! |
| */ |
| switch (p->last_stop_state.tts_event) |
| { |
| |
| case TTEVT_NONE: |
| /* Ok to set this handled. |
| */ |
| break; |
| |
| default: |
| warning ("Internal error; skipping event %s on process %d, thread %d.", |
| get_printable_name_of_ttrace_event ( |
| p->last_stop_state.tts_event), |
| p->pid, p->tid); |
| } |
| |
| set_handled (pid, tid); |
| |
| } |
| else |
| { |
| /* There will be no "continue" opertion, so the |
| * process remains stopped. Don't set any events |
| * handled except the "gimmies". |
| */ |
| switch (p->last_stop_state.tts_event) |
| { |
| |
| case TTEVT_NONE: |
| /* Ok to ignore this. |
| */ |
| set_handled (pid, tid); |
| break; |
| |
| case TTEVT_EXEC: |
| case TTEVT_FORK: |
| /* Expected "other" FORK or EXEC event from a |
| * fork or vfork. |
| */ |
| break; |
| |
| default: |
| printf ("Internal error: failed to handle event %s on process %d, thread %d.", |
| get_printable_name_of_ttrace_event ( |
| p->last_stop_state.tts_event), |
| p->pid, p->tid); |
| } |
| } |
| |
| add_thread (pid_to_ptid (pid)); /* in thread.c */ |
| } |
| |
| #ifdef PARANOIA |
| if (debug_on) |
| print_tthreads (); |
| #endif |
| |
| /* One mustn't call ttrace_wait() after attaching via ttrace, |
| 'cause the process is stopped already. |
| |
| However, the upper layers of gdb's execution control will |
| want to wait after attaching (but not after forks, in |
| which case they will be doing a "target_resume", anticipating |
| a later TTEVT_EXEC or TTEVT_FORK event). |
| |
| To make this attach() implementation more compatible with |
| others, we'll make the attached-to process raise a SIGTRAP. |
| |
| Issue: this continues only one thread. That could be |
| dangerous if the thread is blocked--the process won't run |
| and no trap will be raised. FIX! (check state.tts_flags? |
| need one that's either TTS_WASRUNNING--but we've stopped |
| it and made it TTS_WASSUSPENDED. Hum...FIXME!) |
| */ |
| if (DO_ATTACH_CONTINUE == kind_of_go) |
| { |
| tt_status = call_real_ttrace ( |
| TT_LWP_CONTINUE, |
| pid, |
| a_thread, |
| TT_USE_CURRENT_PC, |
| (TTRACE_ARG_TYPE) target_signal_to_host (TARGET_SIGNAL_TRAP), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| clear_handled (a_thread); /* So TRAP will be reported. */ |
| |
| /* Now running. |
| */ |
| process_state = RUNNING; |
| } |
| |
| attach_flag = 1; |
| } |
| #endif /* ATTACH_DETACH */ |
| |
| |
| #ifdef ATTACH_DETACH |
| /* Start debugging the process whose number is PID. |
| * (A _real_ pid). |
| */ |
| int |
| attach (int pid) |
| { |
| int tt_status; |
| |
| tt_status = call_real_ttrace ( |
| TT_PROC_ATTACH, |
| pid, |
| (lwpid_t) TT_NIL, |
| TT_NIL, |
| (TTRACE_ARG_TYPE) TT_VERSION, |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace attach"); |
| |
| /* If successful, the process is now stopped. |
| */ |
| process_state = STOPPED; |
| |
| /* Our caller ("attach_command" in "infcmd.c") |
| * expects to do a "wait_for_inferior" after |
| * the attach, so make sure the inferior is |
| * running when we're done. |
| */ |
| update_thread_state_after_attach (pid, DO_ATTACH_CONTINUE); |
| |
| return pid; |
| } |
| |
| |
| #if defined(CHILD_POST_ATTACH) |
| void |
| child_post_attach (int pid) |
| { |
| #ifdef THREAD_DEBUG |
| if (debug_on) |
| printf ("child-post-attach call\n"); |
| #endif |
| |
| require_notification_of_events (pid); |
| } |
| #endif |
| |
| |
| /* Stop debugging the process whose number is PID |
| and continue it with signal number SIGNAL. |
| SIGNAL = 0 means just continue it. |
| */ |
| void |
| detach (int signal) |
| { |
| errno = 0; |
| call_ttrace (TT_PROC_DETACH, |
| PIDGET (inferior_ptid), |
| TT_NIL, |
| (TTRACE_ARG_TYPE) signal, |
| TT_NIL); |
| attach_flag = 0; |
| |
| clear_thread_info (); |
| |
| /* Process-state? */ |
| } |
| #endif /* ATTACH_DETACH */ |
| |
| |
| /* Default the type of the ttrace transfer to int. */ |
| #ifndef TTRACE_XFER_TYPE |
| #define TTRACE_XFER_TYPE int |
| #endif |
| |
| void |
| _initialize_kernel_u_addr (void) |
| { |
| } |
| |
| #if !defined (CHILD_XFER_MEMORY) |
| /* NOTE! I tried using TTRACE_READDATA, etc., to read and write memory |
| in the NEW_SUN_TTRACE case. |
| It ought to be straightforward. But it appears that writing did |
| not write the data that I specified. I cannot understand where |
| it got the data that it actually did write. */ |
| |
| /* Copy LEN bytes to or from inferior's memory starting at MEMADDR |
| to debugger memory starting at MYADDR. Copy to inferior if |
| WRITE is nonzero. TARGET is ignored. |
| |
| Returns the length copied, which is either the LEN argument or zero. |
| This xfer function does not do partial moves, since child_ops |
| doesn't allow memory operations to cross below us in the target stack |
| anyway. */ |
| |
| int |
| child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, |
| struct mem_attrib *attrib, |
| struct target_ops *target) |
| { |
| register int i; |
| /* Round starting address down to longword boundary. */ |
| register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (TTRACE_XFER_TYPE); |
| /* Round ending address up; get number of longwords that makes. */ |
| register int count |
| = (((memaddr + len) - addr) + sizeof (TTRACE_XFER_TYPE) - 1) |
| / sizeof (TTRACE_XFER_TYPE); |
| /* Allocate buffer of that many longwords. */ |
| /* FIXME (alloca): This code, cloned from infptrace.c, is unsafe |
| because it uses alloca to allocate a buffer of arbitrary size. |
| For very large xfers, this could crash GDB's stack. */ |
| register TTRACE_XFER_TYPE *buffer |
| = (TTRACE_XFER_TYPE *) alloca (count * sizeof (TTRACE_XFER_TYPE)); |
| |
| if (write) |
| { |
| /* Fill start and end extra bytes of buffer with existing memory data. */ |
| |
| if (addr != memaddr || len < (int) sizeof (TTRACE_XFER_TYPE)) |
| { |
| /* Need part of initial word -- fetch it. */ |
| buffer[0] = call_ttrace (TT_LWP_RDTEXT, |
| PIDGET (inferior_ptid), |
| (TTRACE_ARG_TYPE) addr, |
| TT_NIL, |
| TT_NIL); |
| } |
| |
| if (count > 1) /* FIXME, avoid if even boundary */ |
| { |
| buffer[count - 1] = call_ttrace (TT_LWP_RDTEXT, |
| PIDGET (inferior_ptid), |
| ((TTRACE_ARG_TYPE) |
| (addr + (count - 1) * sizeof (TTRACE_XFER_TYPE))), |
| TT_NIL, |
| TT_NIL); |
| } |
| |
| /* Copy data to be written over corresponding part of buffer */ |
| |
| memcpy ((char *) buffer + (memaddr & (sizeof (TTRACE_XFER_TYPE) - 1)), |
| myaddr, |
| len); |
| |
| /* Write the entire buffer. */ |
| |
| for (i = 0; i < count; i++, addr += sizeof (TTRACE_XFER_TYPE)) |
| { |
| errno = 0; |
| call_ttrace (TT_LWP_WRDATA, |
| PIDGET (inferior_ptid), |
| (TTRACE_ARG_TYPE) addr, |
| (TTRACE_ARG_TYPE) buffer[i], |
| TT_NIL); |
| if (errno) |
| { |
| /* Using the appropriate one (I or D) is necessary for |
| Gould NP1, at least. */ |
| errno = 0; |
| call_ttrace (TT_LWP_WRTEXT, |
| PIDGET (inferior_ptid), |
| (TTRACE_ARG_TYPE) addr, |
| (TTRACE_ARG_TYPE) buffer[i], |
| TT_NIL); |
| } |
| if (errno) |
| return 0; |
| } |
| } |
| else |
| { |
| /* Read all the longwords */ |
| for (i = 0; i < count; i++, addr += sizeof (TTRACE_XFER_TYPE)) |
| { |
| errno = 0; |
| buffer[i] = call_ttrace (TT_LWP_RDTEXT, |
| PIDGET (inferior_ptid), |
| (TTRACE_ARG_TYPE) addr, |
| TT_NIL, |
| TT_NIL); |
| if (errno) |
| return 0; |
| QUIT; |
| } |
| |
| /* Copy appropriate bytes out of the buffer. */ |
| memcpy (myaddr, |
| (char *) buffer + (memaddr & (sizeof (TTRACE_XFER_TYPE) - 1)), |
| len); |
| } |
| return len; |
| } |
| |
| |
| static void |
| udot_info (void) |
| { |
| int udot_off; /* Offset into user struct */ |
| int udot_val; /* Value from user struct at udot_off */ |
| char mess[128]; /* For messages */ |
| |
| if (!target_has_execution) |
| { |
| error ("The program is not being run."); |
| } |
| |
| #if !defined (KERNEL_U_SIZE) |
| |
| /* Adding support for this command is easy. Typically you just add a |
| routine, called "kernel_u_size" that returns the size of the user |
| struct, to the appropriate *-nat.c file and then add to the native |
| config file "#define KERNEL_U_SIZE kernel_u_size()" */ |
| error ("Don't know how large ``struct user'' is in this version of gdb."); |
| |
| #else |
| |
| for (udot_off = 0; udot_off < KERNEL_U_SIZE; udot_off += sizeof (udot_val)) |
| { |
| if ((udot_off % 24) == 0) |
| { |
| if (udot_off > 0) |
| { |
| printf_filtered ("\n"); |
| } |
| printf_filtered ("%04x:", udot_off); |
| } |
| udot_val = call_ttrace (TT_LWP_RUREGS, |
| PIDGET (inferior_ptid), |
| (TTRACE_ARG_TYPE) udot_off, |
| TT_NIL, |
| TT_NIL); |
| if (errno != 0) |
| { |
| sprintf (mess, "\nreading user struct at offset 0x%x", udot_off); |
| perror_with_name (mess); |
| } |
| /* Avoid using nonportable (?) "*" in print specs */ |
| printf_filtered (sizeof (int) == 4 ? " 0x%08x" : " 0x%16x", udot_val); |
| } |
| printf_filtered ("\n"); |
| |
| #endif |
| } |
| #endif /* !defined (CHILD_XFER_MEMORY). */ |
| |
| |
| /* TTrace version of "target_pid_to_exec_file" |
| */ |
| char * |
| child_pid_to_exec_file (int tid) |
| { |
| int tt_status; |
| static char exec_file_buffer[1024]; |
| pid_t pid; |
| static struct pst_status buf; |
| |
| /* On various versions of hpux11, this may fail due to a supposed |
| kernel bug. We have alternate methods to get this information |
| (ie pstat). */ |
| tt_status = call_ttrace (TT_PROC_GET_PATHNAME, |
| tid, |
| (uint64_t) exec_file_buffer, |
| sizeof (exec_file_buffer) - 1, |
| 0); |
| if (tt_status >= 0) |
| return exec_file_buffer; |
| |
| /* Try to get process information via pstat and extract the filename |
| from the pst_cmd field within the pst_status structure. */ |
| if (pstat_getproc (&buf, sizeof (struct pst_status), 0, tid) != -1) |
| { |
| char *p = buf.pst_cmd; |
| |
| while (*p && *p != ' ') |
| p++; |
| *p = 0; |
| |
| return (buf.pst_cmd); |
| } |
| |
| return (NULL); |
| } |
| |
| void |
| pre_fork_inferior (void) |
| { |
| int status; |
| |
| status = pipe (startup_semaphore.parent_channel); |
| if (status < 0) |
| { |
| warning ("error getting parent pipe for startup semaphore"); |
| return; |
| } |
| |
| status = pipe (startup_semaphore.child_channel); |
| if (status < 0) |
| { |
| warning ("error getting child pipe for startup semaphore"); |
| return; |
| } |
| } |
| |
| /* Called from child_follow_fork in hppah-nat.c. |
| * |
| * This seems to be intended to attach after a fork or |
| * vfork, while "attach" is used to attach to a pid |
| * given by the user. The check for an existing attach |
| * seems odd--it always fails in our test system. |
| */ |
| int |
| hppa_require_attach (int pid) |
| { |
| int tt_status; |
| CORE_ADDR pc; |
| CORE_ADDR pc_addr; |
| unsigned int regs_offset; |
| process_state_t old_process_state = process_state; |
| |
| /* Are we already attached? There appears to be no explicit |
| * way to answer this via ttrace, so we try something which |
| * should be innocuous if we are attached. If that fails, |
| * then we assume we're not attached, and so attempt to make |
| * it so. |
| */ |
| errno = 0; |
| tt_status = call_real_ttrace (TT_PROC_STOP, |
| pid, |
| (lwpid_t) TT_NIL, |
| (TTRACE_ARG_TYPE) TT_NIL, |
| (TTRACE_ARG_TYPE) TT_NIL, |
| TT_NIL); |
| |
| if (errno) |
| { |
| /* No change to process-state! |
| */ |
| errno = 0; |
| pid = attach (pid); |
| } |
| else |
| { |
| /* If successful, the process is now stopped. But if |
| * we're VFORKING, the parent is still running, so don't |
| * change the process state. |
| */ |
| if (process_state != VFORKING) |
| process_state = STOPPED; |
| |
| /* If we were already attached, you'd think that we |
| * would need to start going again--but you'd be wrong, |
| * as the fork-following code is actually in the middle |
| * of the "resume" routine in in "infrun.c" and so |
| * will (almost) immediately do a resume. |
| * |
| * On the other hand, if we are VFORKING, which means |
| * that the child and the parent share a process for a |
| * while, we know that "resume" won't be resuming |
| * until the child EXEC event is seen. But we still |
| * don't want to continue, as the event is already |
| * there waiting. |
| */ |
| update_thread_state_after_attach (pid, DONT_ATTACH_CONTINUE); |
| } /* STOP succeeded */ |
| |
| return pid; |
| } |
| |
| int |
| hppa_require_detach (int pid, int signal) |
| { |
| int tt_status; |
| |
| /* If signal is non-zero, we must pass the signal on to the active |
| thread prior to detaching. We do this by continuing the threads |
| with the signal. |
| */ |
| if (signal != 0) |
| { |
| errno = 0; |
| threads_continue_all_with_signals (pid, signal); |
| } |
| |
| errno = 0; |
| tt_status = call_ttrace (TT_PROC_DETACH, |
| pid, |
| TT_NIL, |
| TT_NIL, |
| TT_NIL); |
| |
| errno = 0; /* Ignore any errors. */ |
| |
| /* process_state? */ |
| |
| return pid; |
| } |
| |
| /* Given the starting address of a memory page, hash it to a bucket in |
| the memory page dictionary. |
| */ |
| static int |
| get_dictionary_bucket_of_page (CORE_ADDR page_start) |
| { |
| int hash; |
| |
| hash = (page_start / memory_page_dictionary.page_size); |
| hash = hash % MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; |
| |
| return hash; |
| } |
| |
| |
| /* Given a memory page's starting address, get (i.e., find an existing |
| or create a new) dictionary entry for the page. The page will be |
| write-protected when this function returns, but may have a reference |
| count of 0 (if the page was newly-added to the dictionary). |
| */ |
| static memory_page_t * |
| get_dictionary_entry_of_page (int pid, CORE_ADDR page_start) |
| { |
| int bucket; |
| memory_page_t *page = NULL; |
| memory_page_t *previous_page = NULL; |
| |
| /* We're going to be using the dictionary now, than-kew. */ |
| require_memory_page_dictionary (); |
| |
| /* Try to find an existing dictionary entry for this page. Hash |
| on the page's starting address. |
| */ |
| bucket = get_dictionary_bucket_of_page (page_start); |
| page = &memory_page_dictionary.buckets[bucket]; |
| while (page != NULL) |
| { |
| if (page->page_start == page_start) |
| break; |
| previous_page = page; |
| page = page->next; |
| } |
| |
| /* Did we find a dictionary entry for this page? If not, then |
| add it to the dictionary now. |
| */ |
| if (page == NULL) |
| { |
| /* Create a new entry. */ |
| page = (memory_page_t *) xmalloc (sizeof (memory_page_t)); |
| page->page_start = page_start; |
| page->reference_count = 0; |
| page->next = NULL; |
| page->previous = NULL; |
| |
| /* We'll write-protect the page now, if that's allowed. */ |
| page->original_permissions = write_protect_page (pid, page_start); |
| |
| /* Add the new entry to the dictionary. */ |
| page->previous = previous_page; |
| previous_page->next = page; |
| |
| memory_page_dictionary.page_count++; |
| } |
| |
| return page; |
| } |
| |
| |
| static void |
| remove_dictionary_entry_of_page (int pid, memory_page_t *page) |
| { |
| /* Restore the page's original permissions. */ |
| unwrite_protect_page (pid, page->page_start, page->original_permissions); |
| |
| /* Kick the page out of the dictionary. */ |
| if (page->previous != NULL) |
| page->previous->next = page->next; |
| if (page->next != NULL) |
| page->next->previous = page->previous; |
| |
| /* Just in case someone retains a handle to this after it's freed. */ |
| page->page_start = (CORE_ADDR) 0; |
| |
| memory_page_dictionary.page_count--; |
| |
| xfree (page); |
| } |
| |
| |
| static void |
| hppa_enable_syscall_events (int pid) |
| { |
| int tt_status; |
| ttevent_t ttrace_events; |
| |
| /* Get the set of events that are currently enabled. */ |
| tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, |
| pid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| /* Add syscall events to that set. */ |
| ttrace_events.tte_events |= TTEVT_SYSCALL_ENTRY; |
| ttrace_events.tte_events |= TTEVT_SYSCALL_RETURN; |
| |
| tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, |
| pid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| } |
| |
| |
| static void |
| hppa_disable_syscall_events (int pid) |
| { |
| int tt_status; |
| ttevent_t ttrace_events; |
| |
| /* Get the set of events that are currently enabled. */ |
| tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, |
| pid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| |
| /* Remove syscall events from that set. */ |
| ttrace_events.tte_events &= ~TTEVT_SYSCALL_ENTRY; |
| ttrace_events.tte_events &= ~TTEVT_SYSCALL_RETURN; |
| |
| tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, |
| pid, |
| (TTRACE_ARG_TYPE) & ttrace_events, |
| (TTRACE_ARG_TYPE) sizeof (ttrace_events), |
| TT_NIL); |
| if (errno) |
| perror_with_name ("ttrace"); |
| } |
| |
| |
| /* The address range beginning with START and ending with START+LEN-1 |
| (inclusive) is to be watched via page-protection by a new watchpoint. |
| Set protection for all pages that overlap that range. |
| |
| Note that our caller sets TYPE to: |
| 0 for a bp_hardware_watchpoint, |
| 1 for a bp_read_watchpoint, |
| 2 for a bp_access_watchpoint |
| |
| (Yes, this is intentionally (though lord only knows why) different |
| from the TYPE that is passed to hppa_remove_hw_watchpoint.) |
| */ |
| int |
| hppa_insert_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len, int type) |
| { |
| CORE_ADDR page_start; |
| int dictionary_was_empty; |
| int page_size; |
| int page_id; |
| LONGEST range_size_in_pages; |
| |
| if (type != 0) |
| error ("read or access hardware watchpoints not supported on HP-UX"); |
| |
| /* Examine all pages in the address range. */ |
| require_memory_page_dictionary (); |
| |
| dictionary_was_empty = (memory_page_dictionary.page_count == (LONGEST) 0); |
| |
| page_size = memory_page_dictionary.page_size; |
| page_start = (start / page_size) * page_size; |
| range_size_in_pages = ((LONGEST) len + (LONGEST) page_size - 1) / (LONGEST) page_size; |
| |
| for (page_id = 0; page_id < range_size_in_pages; page_id++, page_start += page_size) |
| { |
| memory_page_t *page; |
| |
| /* This gets the page entered into the dictionary if it was |
| not already entered. |
| */ |
| page = get_dictionary_entry_of_page (pid, page_start); |
| page->reference_count++; |
| } |
| |
| /* Our implementation depends on seeing calls to kernel code, for the |
| following reason. Here we ask to be notified of syscalls. |
| |
| When a protected page is accessed by user code, HP-UX raises a SIGBUS. |
| Fine. |
| |
| But when kernel code accesses the page, it doesn't give a SIGBUS. |
| Rather, the system call that touched the page fails, with errno=EFAULT. |
| Not good for us. |
| |
| We could accomodate this "feature" by asking to be notified of syscall |
| entries & exits; upon getting an entry event, disabling page-protections; |
| upon getting an exit event, reenabling page-protections and then checking |
| if any watchpoints triggered. |
| |
| However, this turns out to be a real performance loser. syscalls are |
| usually a frequent occurrence. Having to unprotect-reprotect all watched |
| pages, and also to then read all watched memory locations and compare for |
| triggers, can be quite expensive. |
| |
| Instead, we'll only ask to be notified of syscall exits. When we get |
| one, we'll check whether errno is set. If not, or if it's not EFAULT, |
| we can just continue the inferior. |
| |
| If errno is set upon syscall exit to EFAULT, we must perform some fairly |
| hackish stuff to determine whether the failure really was due to a |
| page-protect trap on a watched location. |
| */ |
| if (dictionary_was_empty) |
| hppa_enable_syscall_events (pid); |
| |
| return 1; |
| } |
| |
| |
| /* The address range beginning with START and ending with START+LEN-1 |
| (inclusive) was being watched via page-protection by a watchpoint |
| which has been removed. Remove protection for all pages that |
| overlap that range, which are not also being watched by other |
| watchpoints. |
| */ |
| int |
| hppa_remove_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len, |
| enum bptype type) |
| { |
| CORE_ADDR page_start; |
| int dictionary_is_empty; |
| int page_size; |
| int page_id; |
| LONGEST range_size_in_pages; |
| |
| if (type != 0) |
| error ("read or access hardware watchpoints not supported on HP-UX"); |
| |
| /* Examine all pages in the address range. */ |
| require_memory_page_dictionary (); |
| |
| page_size = memory_page_dictionary.page_size; |
| page_start = (start / page_size) * page_size; |
| range_size_in_pages = ((LONGEST) len + (LONGEST) page_size - 1) / (LONGEST) page_size; |
| |
| for (page_id = 0; page_id < range_size_in_pages; page_id++, page_start += page_size) |
| { |
| memory_page_t *page; |
| |
| page = get_dictionary_entry_of_page (pid, page_start); |
| page->reference_count--; |
| |
| /* Was this the last reference of this page? If so, then we |
| must scrub the entry from the dictionary, and also restore |
| the page's original permissions. |
| */ |
| if (page->reference_count == 0) |
| remove_dictionary_entry_of_page (pid, page); |
| } |
| |
| dictionary_is_empty = (memory_page_dictionary.page_count == (LONGEST) 0); |
| |
| /* If write protections are currently disallowed, then that implies that |
| wait_for_inferior believes that the inferior is within a system call. |
| Since we want to see both syscall entry and return, it's clearly not |
| good to disable syscall events in this state! |
| |
| ??rehrauer: Yeah, it'd be better if we had a specific flag that said, |
| "inferior is between syscall events now". Oh well. |
| */ |
| if (dictionary_is_empty && memory_page_dictionary.page_protections_allowed) |
| hppa_disable_syscall_events (pid); |
| |
| return 1; |
| } |
| |
| |
| /* Could we implement a watchpoint of this type via our available |
| hardware support? |
| |
| This query does not consider whether a particular address range |
| could be so watched, but just whether support is generally available |
| for such things. See hppa_range_profitable_for_hw_watchpoint for a |
| query that answers whether a particular range should be watched via |
| hardware support. |
| */ |
| int |
| hppa_can_use_hw_watchpoint (enum bptype type, int cnt, enum bptype ot) |
| { |
| return (type == bp_hardware_watchpoint); |
| } |
| |
| |
| /* Assuming we could set a hardware watchpoint on this address, do |
| we think it would be profitable ("a good idea") to do so? If not, |
| we can always set a regular (aka single-step & test) watchpoint |
| on the address... |
| */ |
| int |
| hppa_range_profitable_for_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len) |
| { |
| int range_is_stack_based; |
| int range_is_accessible; |
| CORE_ADDR page_start; |
| int page_size; |
| int page; |
| LONGEST range_size_in_pages; |
| |
| /* ??rehrauer: For now, say that all addresses are potentially |
| profitable. Possibly later we'll want to test the address |
| for "stackness"? |
| */ |
| range_is_stack_based = 0; |
| |
| /* If any page in the range is inaccessible, then we cannot |
| really use hardware watchpointing, even though our client |
| thinks we can. In that case, it's actually an error to |
| attempt to use hw watchpoints, so we'll tell our client |
| that the range is "unprofitable", and hope that they listen... |
| */ |
| range_is_accessible = 1; /* Until proven otherwise. */ |
| |
| /* Examine all pages in the address range. */ |
| errno = 0; |
| page_size = sysconf (_SC_PAGE_SIZE); |
| |
| /* If we can't determine page size, we're hosed. Tell our |
| client it's unprofitable to use hw watchpoints for this |
| range. |
| */ |
| if (errno || (page_size <= 0)) |
| { |
| errno = 0; |
| return 0; |
| } |
| |
| page_start = (start / page_size) * page_size; |
| range_size_in_pages = len / (LONGEST) page_size; |
| |
| for (page = 0; page < range_size_in_pages; page++, page_start += page_size) |
| { |
| int tt_status; |
| int page_permissions; |
| |
| /* Is this page accessible? */ |
| errno = 0; |
| tt_status = call_ttrace (TT_PROC_GET_MPROTECT, |
| pid, |
| (TTRACE_ARG_TYPE) page_start, |
| TT_NIL, |
| (TTRACE_ARG_TYPE) & page_permissions); |
| if (errno || (tt_status < 0)) |
| { |
| errno = 0; |
| range_is_accessible = 0; |
| break; |
| } |
| |
| /* Yes, go for another... */ |
| } |
| |
| return (!range_is_stack_based && range_is_accessible); |
| } |
| |
| |
| char * |
| hppa_pid_or_tid_to_str (ptid_t ptid) |
| { |
| static char buf[100]; /* Static because address returned. */ |
| pid_t id = PIDGET (ptid); |
| |
| /* Does this appear to be a process? If so, print it that way. */ |
| if (is_process_id (id)) |
| return child_pid_to_str (ptid); |
| |
| /* Else, print both the GDB thread number and the system thread id. */ |
| sprintf (buf, "thread %d (", pid_to_thread_id (ptid)); |
| strcat (buf, hppa_tid_to_str (ptid)); |
| strcat (buf, ")\0"); |
| |
| return buf; |
| } |
| |
| |
| /* If the current pid is not the pid this module reported |
| * from "ptrace_wait" with the most recent event, then the |
| * user has switched threads. |
| * |
| * If the last reported event was a breakpoint, then return |
| * the old thread id, else return 0. |
| */ |
| pid_t |
| hppa_switched_threads (pid_t gdb_pid) |
| { |
| if (gdb_pid == old_gdb_pid) |
| { |
| /* |
| * Core gdb is working with the same pid that it |
| * was before we reported the last event. This |
| * is ok: e.g. we reported hitting a thread-specific |
| * breakpoint, but we were reporting the wrong |
| * thread, so the core just ignored the event. |
| * |
| * No thread switch has happened. |
| */ |
| return (pid_t) 0; |
| } |
| else if (gdb_pid == reported_pid) |
| { |
| /* |
| * Core gdb is working with the pid we reported, so |
| * any continue or step will be able to figure out |
| * that it needs to step over any hit breakpoints |
| * without our (i.e. PREPARE_TO_PROCEED's) help. |
| */ |
| return (pid_t) 0; |
| } |
| else if (!reported_bpt) |
| { |
| /* |
| * The core switched, but we didn't just report a |
| * breakpoint, so there's no just-hit breakpoint |
| * instruction at "reported_pid"'s PC, and thus there |
| * is no need to step over it. |
| */ |
| return (pid_t) 0; |
| } |
| else |
| { |
| /* There's been a real switch, and we reported |
| * a hit breakpoint. Let "hppa_prepare_to_proceed" |
| * know, so it can see whether the breakpoint is |
| * still active. |
| */ |
| return reported_pid; |
| } |
| |
| /* Keep compiler happy with an obvious return at the end. |
| */ |
| return (pid_t) 0; |
| } |
| |
| void |
| hppa_ensure_vforking_parent_remains_stopped (int pid) |
| { |
| /* Nothing to do when using ttrace. Only the ptrace-based implementation |
| must do real work. |
| */ |
| } |
| |
| |
| int |
| hppa_resume_execd_vforking_child_to_get_parent_vfork (void) |
| { |
| return 0; /* No, the parent vfork is available now. */ |
| } |
| |
| |
| /* Write a register as a 64bit value. This may be necessary if the |
| native OS is too braindamaged to allow some (or all) registers to |
| be written in 32bit hunks such as hpux11 and the PC queue registers. |
| |
| This is horribly gross and disgusting. */ |
| |
| int |
| ttrace_write_reg_64 (int gdb_tid, CORE_ADDR dest_addr, CORE_ADDR src_addr) |
| { |
| pid_t pid; |
| lwpid_t tid; |
| int tt_status; |
| |
| tid = map_from_gdb_tid (gdb_tid); |
| pid = get_pid_for (tid); |
| |
| errno = 0; |
| tt_status = ttrace (TT_LWP_WUREGS, |
| pid, |
| tid, |
| (TTRACE_ARG_TYPE) dest_addr, |
| 8, |
| (TTRACE_ARG_TYPE) src_addr ); |
| |
| #ifdef THREAD_DEBUG |
| if (errno) |
| { |
| /* Don't bother for a known benign error: if you ask for the |
| first thread state, but there is only one thread and it's |
| not stopped, ttrace complains. |
| |
| We have this inside the #ifdef because our caller will do |
| this check for real. */ |
| if( request != TT_PROC_GET_FIRST_LWP_STATE |
| || errno != EPROTO ) |
| { |
| if( debug_on ) |
| printf( "TT fail for %s, with pid %d, tid %d, status %d \n", |
| get_printable_name_of_ttrace_request (TT_LWP_WUREGS), |
| pid, tid, tt_status ); |
| } |
| } |
| #endif |
| |
| return tt_status; |
| } |
| |
| void |
| _initialize_infttrace (void) |
| { |
| /* Initialize the ttrace-based hardware watchpoint implementation. */ |
| memory_page_dictionary.page_count = (LONGEST) - 1; |
| memory_page_dictionary.page_protections_allowed = 1; |
| |
| errno = 0; |
| memory_page_dictionary.page_size = sysconf (_SC_PAGE_SIZE); |
| |
| /* We do a lot of casts from pointers to TTRACE_ARG_TYPE; make sure |
| this is okay. */ |
| if (sizeof (TTRACE_ARG_TYPE) < sizeof (void *)) |
| internal_error (__FILE__, __LINE__, "failed internal consistency check"); |
| |
| if (errno || (memory_page_dictionary.page_size <= 0)) |
| perror_with_name ("sysconf"); |
| } |