#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "target.h"
#include "gdb_string.h"
#include "gdb_wait.h"
#include "command.h"
#define _PSTAT64
#include <sys/pstat.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
#ifdef NO_FLAGS
#if (NO_FLAGS != INFTTRACE_TEMP_HACK )
#else
#endif
#else
#define NO_FLAGS INFTTRACE_TEMP_HACK
#endif
#if !defined (PT_SETTRC)
#define PT_SETTRC 0
#endif
#if !defined (PT_READ_I)
#define PT_READ_I 1
#endif
#if !defined (PT_READ_D)
#define PT_READ_D 2
#endif
#if !defined (PT_READ_U)
#define PT_READ_U 3
#endif
#if !defined (PT_WRITE_I)
#define PT_WRITE_I 4
#endif
#if !defined (PT_WRITE_D)
#define PT_WRITE_D 5
#endif
#if !defined (PT_WRITE_U)
#define PT_WRITE_U 6
#endif
#if !defined (PT_CONTINUE)
#define PT_CONTINUE 7
#endif
#if !defined (PT_STEP)
#define PT_STEP 9
#endif
#if !defined (PT_KILL)
#define PT_KILL 8
#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
typedef struct
{
int parent_channel[2];
int child_channel[2];
}
startup_semaphore_t;
#define SEM_TALK (1)
#define SEM_LISTEN (0)
static startup_semaphore_t startup_semaphore;
static int vforking_child_pid = 0;
static int vfork_in_flight = 0;
static pid_t old_gdb_pid = 0;
static pid_t reported_pid = 0;
static int reported_bpt = 0;
#define TT_OK( _status, _errno ) \
(((_status) == 1) && ((_errno) == 0))
#define TTRACE_ARG_TYPE uint64_t
#define TT_USE_CURRENT_PC ((TTRACE_ARG_TYPE) TT_NOPC)
#define TT_NIL ((TTRACE_ARG_TYPE) TT_NULLARG)
typedef int register_value_t;
#define INFTTRACE_ALL_THREADS (-1)
#define INFTTRACE_STEP (1)
#define INFTTRACE_CONTINUE (0)
extern int not_same_real_pid;
static unsigned int more_events_left = 0;
typedef enum process_state_enum
{
STOPPED,
FAKE_STEPPING,
FAKE_CONTINUE,
RUNNING,
FORKING,
VFORKING
}
process_state_t;
static process_state_t process_state = STOPPED;
typedef enum stepping_mode_enum
{
DO_DEFAULT,
DO_STEP,
DO_CONTINUE
}
stepping_mode_t;
typedef enum attach_continue_enum
{
DO_ATTACH_CONTINUE,
DONT_ATTACH_CONTINUE
}
attach_continue_t;
static int doing_fake_step = 0;
static lwpid_t fake_step_tid = 0;
typedef
struct thread_info_struct
{
int am_pseudo;
int pid;
lwpid_t tid;
int handled;
int seen;
int terminated;
int have_signal;
enum target_signal signal_value;
int have_start;
stepping_mode_t stepping_mode;
CORE_ADDR start;
int have_state;
ttstate_t last_stop_state;
struct thread_info_struct
*next;
struct thread_info_struct
*next_pseudo;
}
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;
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?";
}
}
char *
get_printable_name_of_ttrace_event (ttevents_t event)
{
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?";
}
}
char *
get_printable_name_of_ttrace_request (ttreq_t request)
{
if (!IS_TTRACE_REQ (request))
return "?bad req?";
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?";
}
}
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?";
}
}
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;
}
static void
copy_ttstate_t (ttstate_t *tts_to, ttstate_t *tts_from)
{
memcpy ((char *) tts_to, (char *) tts_from, sizeof (*tts_to));
}
static int
any_thread_records (void)
{
return (thread_head.count > 0);
}
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
}
thread_head.count++;
new_p->next = thread_head.head;
thread_head.head = new_p;
p = thread_head.head;
thread_count_of_pid = 0;
while (p)
{
if (p->pid == new_p->pid)
thread_count_of_pid++;
p = p->next;
}
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;
}
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;
more_events_left = 0;
}
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;
}
static lwpid_t
map_from_gdb_tid (lwpid_t gdb_tid)
{
thread_info *p;
for (p = thread_head.head; p; p = p->next)
{
if (p->tid == gdb_tid)
return gdb_tid;
}
for (p = thread_head.head_pseudo; p != NULL; p = p->next_pseudo)
{
if (p->pid == gdb_tid)
return p->tid;
}
for (p = deleted_threads.head; p; p = p->next)
{
if (p->tid == gdb_tid)
return gdb_tid;
}
for (p = deleted_threads.head_pseudo; p != NULL; p = p->next_pseudo)
{
if (p->pid == gdb_tid)
return p->tid;
}
return 0;
}
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;
else
return real_tid;
}
return 0;
}
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;
}
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;
}
static int
is_terminated (lwpid_t tid)
{
thread_info *p = find_thread_info (tid);
if (NULL != p)
return p->terminated;
return 0;
}
static int
is_process_id (int pid)
{
lwpid_t tid;
thread_info *tinfo;
pid_t this_pid;
int this_pid_count;
tid = map_from_gdb_tid (pid);
if (tid <= 0)
return 0;
tinfo = find_thread_info (tid);
if (!tinfo->am_pseudo)
return 0;
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);
}
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;
}
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)
{
thread_info *q;
thread_info *q_chase;
q_chase = NULL;
for (q = thread_head.head_pseudo; q; q = q->next)
{
if (q == p)
{
if (q_chase == NULL)
thread_head.head_pseudo = p->next_pseudo;
else
q_chase->next = p->next_pseudo;
}
else
q_chase = q;
}
}
thread_head.count--;
if (NULL == chase)
thread_head.head = p->next;
else
chase->next = p->next;
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;
}
}
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;
}
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;
}
static int
was_handled (lwpid_t tid)
{
thread_info *p;
p = find_thread_info (tid);
if (NULL != p)
return p->handled;
return 0;
}
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;
}
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;
}
}
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;
}
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;
}
}
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 ))
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
static void
update_thread_list (void)
{
thread_info *p;
thread_info *chase;
chase = NULL;
for (p = thread_head.head; p; p = p->next)
{
if ((!p->seen) && p->am_pseudo && vfork_in_flight
&& (p->pid != vforking_child_pid))
p->seen = 1;
if (!p->seen)
{
#ifdef THREAD_DEBUG
if (debug_on)
printf ("Delete unseen thread: %d \n", p->tid);
#endif
del_tthread (p->tid);
}
}
}
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)
{
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
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;
}
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;
}
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)
{
tt_status = 1;
errno = 0;
return 0;
}
else
perror_with_name ("ttrace");
}
if (tt_status < 0)
return 0;
return thread_state->tts_lwpid;
}
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)
return 0;
else if (tt_status == 0)
{
return 0;
}
return thread_state->tts_lwpid;
}
static lwpid_t
get_active_tid_of_pid (int pid)
{
ttstate_t thread_state;
return get_process_first_stopped_thread_id (pid, &thread_state);
}
int
is_process_ttrace_request (ttreq_t tt_request)
{
return IS_TTRACE_PROCREQ (tt_request);
}
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;
default:
return request;
}
}
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];
#ifdef THREAD_DEBUG
int is_interesting = 0;
if (TT_LWP_RUREGS == request)
{
is_interesting = 1;
}
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
#ifdef THREAD_DEBUG
if (request == TT_PROC_SETTRC && debug_on)
printf ("Unexpected call for TT_PROC_SETTRC\n");
#endif
if (gdb_tid == 0)
{
errno = ESRCH;
return -1;
}
if (!any_thread_records ())
{
#ifdef THREAD_DEBUG
if (debug_on)
warning ("No thread records for ttrace call");
#endif
errno = ESRCH;
return -1;
}
real_tid = map_from_gdb_tid (gdb_tid);
real_pid = get_pid_for (real_tid);
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))
{
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
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
{
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
}
}
else
{
error ("Thread request with no threads (%s)",
get_printable_name_of_ttrace_request (request));
}
}
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
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;
}
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");
}
#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;
}
static int
select_stopped_thread_of_process (int pid, ttstate_t *tsp)
{
lwpid_t candidate_tid, tid;
ttstate_t candidate_tstate, tstate;
if (!can_touch_threads_of_process (pid, tsp->tts_event))
return 1;
candidate_tid = 0;
for (tid = get_process_first_stopped_thread_id (pid, &tstate);
tid != 0;
tid = get_process_next_stopped_thread_id (pid, &tstate))
{
if (tstate.tts_event == TTEVT_NONE)
{
set_handled (pid, tstate.tts_lwpid);
}
else if (doing_fake_step && (tstate.tts_lwpid == fake_step_tid))
{
#ifdef WAIT_BUFFER_DEBUG
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
candidate_tid = tstate.tts_lwpid;
candidate_tstate = tstate;
}
#ifdef FORGET_DELETED_BPTS
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)))
{
#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 if (!was_handled (tstate.tts_lwpid) && candidate_tid == 0)
{
candidate_tid = tstate.tts_lwpid;
candidate_tstate = tstate;
}
}
if (doing_fake_step)
{
if (candidate_tid == fake_step_tid)
{
tstate = candidate_tstate;
}
else
{
warning ("Internal error: fake-step failed to complete.");
return 0;
}
}
else if (candidate_tid != 0)
{
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;
}
#ifdef PARANOIA
static void
check_thread_consistency (pid_t real_pid)
{
int tid;
ttstate_t tstate;
thread_info *p;
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
static int
call_ttrace_wait (int pid, ttwopt_t option, ttstate_t *tsp, size_t tsp_size)
{
static int real_pid;
int wait_pid = 0;
lwpid_t wait_tid = 0;
lwpid_t real_tid;
int ttw_status = 0;
thread_info *tinfo = NULL;
if (pid != 0)
{
#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");
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
}
if (more_events_left == 0)
{
if (process_state == RUNNING)
{
;
}
else if (process_state == FAKE_STEPPING)
{
if (!doing_fake_step)
{
warning ("Inconsistent thread state.");
}
}
else if ((process_state == FORKING)
|| (process_state == VFORKING))
{
;
}
else if (process_state == STOPPED)
{
warning ("Process not running at wait call.");
}
else
warning ("Inconsistent process state.");
}
else
{
if (process_state == STOPPED)
{
;
}
else if (process_state == RUNNING)
{
warning ("Trying to continue with buffered events:");
}
else if (process_state == FAKE_STEPPING)
{
if (!doing_fake_step)
{
warning ("Losing buffered thread events!\n");
}
}
else if ((process_state == FORKING)
|| (process_state == VFORKING))
{
;
}
else
warning ("Process in unknown state with buffered events.");
}
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
|| process_state != STOPPED)
{
#ifdef WAIT_BUFFER_DEBUG
if (debug_on)
printf ("TW: do it for real; pid %d, tid %d\n", wait_pid, wait_tid);
#endif
ttw_status = call_real_ttrace_wait (wait_pid, wait_tid, option, tsp, tsp_size);
real_pid = tsp->tts_pid;
if (can_touch_threads_of_process (real_pid, tsp->tts_event))
{
if (!doing_fake_step)
{
if (more_events_left > 0)
warning ("Internal error in stopping process");
stop_all_threads_of_process (real_pid);
}
}
#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
{
#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;
}
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.");
doing_fake_step = 0;
fake_step_tid = 0;
}
set_handled (real_pid, tsp->tts_lwpid);
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;
}
#if defined(CHILD_REPORTED_EXEC_EVENTS_PER_EXEC_CALL)
int
child_reported_exec_events_per_exec_call (void)
{
return 1;
}
#endif
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;
memory_page_t buckets[MEMORY_PAGE_DICTIONARY_BUCKET_COUNT];
}
memory_page_dictionary;
static void
require_memory_page_dictionary (void)
{
int i;
if (memory_page_dictionary.page_count >= (LONGEST) 0)
return;
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;
}
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;
}
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;
}
}
return 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;
}
}
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;
}
}
}
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;
}
static int
count_unhandled_events (int real_pid, lwpid_t real_tid)
{
ttstate_t tstate;
lwpid_t ttid;
int events_left;
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))
{
events_left++;
}
#if defined( THREAD_DEBUG ) || defined( WAIT_BUFFER_DEBUG )
if (debug_on)
{
if (ttid == real_tid)
printf ("*");
else
printf (" ");
if (tstate.tts_event != TTEVT_NONE)
printf ("+");
else
printf (" ");
if (was_handled (ttid))
printf ("h");
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;
}
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;
*status = 0;
ttwait_return = call_ttrace_wait (0, TTRACE_WAITOK, &tsp, sizeof (tsp));
if (ttwait_return < 0)
{
if (errno == ESRCH)
{
*status = 0;
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;
if (tsp.tts_event & TTEVT_LWP_CREATE)
{
#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
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);
add_tthread (real_pid, real_tid);
}
#endif
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;
}
if ((tsp.tts_event & TTEVT_EXEC)
|| (tsp.tts_event & TTEVT_FORK)
|| (tsp.tts_event & TTEVT_VFORK))
{
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
}
#ifdef THREAD_DEBUG
if (debug_on)
printf ("..a process 'event'\n");
#endif
*status = 0177 | (_SIGTRAP << 8);
}
else if ((tsp.tts_event & TTEVT_SYSCALL_ENTRY)
|| (tsp.tts_event & TTEVT_SYSCALL_RETURN))
{
#ifdef THREAD_DEBUG
if (debug_on)
printf ("..a syscall 'event'\n");
#endif
*status = 0177 | (_SIGTRAP << 8);
}
else if ((tsp.tts_event & TTEVT_LWP_CREATE)
|| (tsp.tts_event & TTEVT_LWP_TERMINATE)
|| (tsp.tts_event & TTEVT_LWP_EXIT))
{
#ifdef THREAD_DEBUG
if (debug_on)
printf ("..a thread 'event'\n");
#endif
*status = 0177 | (_SIGTRAP << 8);
}
else if ((tsp.tts_event & TTEVT_EXIT))
{
#ifdef THREAD_DEBUG
if (debug_on)
printf ("..an exit\n");
#endif
inferior_ptid = pid_to_ptid (map_to_gdb_tid (real_tid));
*status = 0 | (tsp.tts_u.tts_exit.tts_exitcode);
}
else if (tsp.tts_event & TTEVT_SIGNAL)
{
#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
{
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
return_pid = map_to_gdb_tid (real_tid);
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;
}
int
parent_attach_all (void)
{
int tt_status;
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;
write (startup_semaphore.child_channel[SEM_TALK],
&tc_magic_child,
sizeof (tc_magic_child));
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");
(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;
}
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
not_same_real_pid = 0;
sigemptyset (¬ifiable_events.tte_signals);
notifiable_events.tte_opts = TTEO_NONE;
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
not_same_real_pid = 0;
sigemptyset (¬ifiable_events.tte_signals);
notifiable_events.tte_opts = TTEO_NOSTRCCHLD;
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);
}
void
child_acknowledge_created_inferior (int pid)
{
uint64_t tc_magic_parent = TT_VERSION;
uint64_t tc_magic_child = 0;
read (startup_semaphore.child_channel[SEM_LISTEN],
&tc_magic_child,
sizeof (tc_magic_child));
clear_thread_info ();
add_thread (pid_to_ptid (pid));
require_notification_of_exec_events (pid);
process_state = RUNNING;
write (startup_semaphore.parent_channel[SEM_TALK],
&tc_magic_parent,
sizeof (tc_magic_parent));
(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]);
}
void
child_post_startup_inferior (ptid_t ptid)
{
require_notification_of_events (PIDGET (ptid));
}
static void
hppa_enable_catch_fork (int tid)
{
int tt_status;
ttevent_t ttrace_events;
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");
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;
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");
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)
{
return 0;
}
#endif
#if defined(CHILD_REMOVE_FORK_CATCHPOINT)
int
child_remove_fork_catchpoint (int tid)
{
return 0;
}
#endif
static void
hppa_enable_catch_vfork (int tid)
{
int tt_status;
ttevent_t ttrace_events;
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");
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;
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");
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)
{
return 0;
}
#endif
#if defined(CHILD_REMOVE_VFORK_CATCHPOINT)
int
child_remove_vfork_catchpoint (int tid)
{
return 0;
}
#endif
#if defined(CHILD_HAS_FORKED)
int
child_has_forked (int tid, int *childpid)
{
int tt_status;
ttstate_t ttrace_state;
thread_info *tinfo;
tinfo = find_thread_info (map_from_gdb_tid (tid));
if (tinfo != NULL)
{
copy_ttstate_t (&ttrace_state, &tinfo->last_stop_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;
}
#endif
#if defined(CHILD_HAS_VFORKED)
int
child_has_vforked (int tid, int *childpid)
{
int tt_status;
ttstate_t ttrace_state;
thread_info *tinfo;
tinfo = find_thread_info (map_from_gdb_tid (tid));
if (tinfo != NULL)
copy_ttstate_t (&ttrace_state, &tinfo->last_stop_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;
}
#endif
#if defined(CHILD_CAN_FOLLOW_VFORK_PRIOR_TO_EXEC)
int
child_can_follow_vfork_prior_to_exec (void)
{
return 0;
}
#endif
#if defined(CHILD_INSERT_EXEC_CATCHPOINT)
int
child_insert_exec_catchpoint (int tid)
{
return 0;
}
#endif
#if defined(CHILD_REMOVE_EXEC_CATCHPOINT)
int
child_remove_exec_catchpoint (int tid)
{
return 0;
}
#endif
#if defined(CHILD_HAS_EXECD)
int
child_has_execd (int tid, char **execd_pathname)
{
int tt_status;
ttstate_t ttrace_state;
thread_info *tinfo;
tinfo = find_thread_info (map_from_gdb_tid (tid));
if (tinfo != NULL)
copy_ttstate_t (&ttrace_state, &tinfo->last_stop_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)
{
char *exec_file = target_pid_to_exec_file (tid);
*execd_pathname = savestring (exec_file, strlen (exec_file));
return 1;
}
return 0;
}
#endif
#if defined(CHILD_HAS_SYSCALL_EVENT)
int
child_has_syscall_event (int pid, enum target_waitkind *kind, int *syscall_id)
{
int tt_status;
ttstate_t ttrace_state;
thread_info *tinfo;
tinfo = find_thread_info (map_from_gdb_tid (pid));
if (tinfo != NULL)
copy_ttstate_t (&ttrace_state, &tinfo->last_stop_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;
*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;
}
#endif
#if defined(CHILD_THREAD_ALIVE)
int
child_thread_alive (ptid_t ptid)
{
lwpid_t gdb_tid = PIDGET (ptid);
lwpid_t tid;
tid = map_from_gdb_tid (gdb_tid);
return !is_terminated (tid);
}
#endif
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)
return 0;
return tt_status;
}
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;
}
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;
switch (pt_request)
{
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,
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,
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;
case PT_DETACH:
tt_request = TT_PROC_DETACH;
break;
case PT_WRITE_I:
tt_request = TT_PROC_WRTEXT;
tt_data = 4;
tt_addr2 = (TTRACE_ARG_TYPE) & data;
break;
case PT_WRITE_D:
tt_request = TT_PROC_WRDATA;
tt_data = 4;
tt_addr2 = (TTRACE_ARG_TYPE) & data;
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;
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;
break;
}
return call_ttrace (tt_request,
gdb_tid,
tt_addr,
tt_data,
tt_addr2);
}
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;
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
static void
thread_dropping_event_check (thread_info *p)
{
if (!p->handled)
{
if (p->have_state)
{
if (p->last_stop_state.tts_event == TTEVT_FORK)
{
;
}
else if (p->last_stop_state.tts_event == TTEVT_SIGNAL)
{
;
}
else
{
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
{
warning ("About to continue process %d, thread %d with unhandled event.",
p->pid, p->tid);
#ifdef PARANOIA
if (debug_on)
print_tthread (p);
#endif
}
}
}
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
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)
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);
p->handled = 1;
if (state.tts_event != TTEVT_NONE)
{
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);
}
update_thread_list ();
for (p = thread_head.head; p; p = p->next)
{
thread_dropping_event_check (p);
if (p->have_signal)
{
thread_signal = p->signal_value;
p->have_signal = 0;
}
else
thread_signal = 0;
if (p->tid != real_tid)
{
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
{
call_ttrace (
TT_LWP_CONTINUE,
p->tid,
TT_USE_CURRENT_PC,
(TTRACE_ARG_TYPE) target_signal_to_host (thread_signal),
TT_NIL);
}
}
else
{
call_ttrace (
TT_LWP_SINGLE,
real_tid,
TT_USE_CURRENT_PC,
(TTRACE_ARG_TYPE) target_signal_to_host (signal),
TT_NIL);
}
}
}
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
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);
p->handled = 1;
if (state.tts_event != TTEVT_NONE)
{
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);
}
update_thread_list ();
for (p = thread_head.head; p; p = p->next)
{
thread_dropping_event_check (p);
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
{
call_ttrace (
TT_LWP_CONTINUE,
p->tid,
TT_USE_CURRENT_PC,
(TTRACE_ARG_TYPE) target_signal_to_host (thread_signal),
TT_NIL);
}
}
}
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.");
p = find_thread_info (tid);
if (p != NULL)
{
if (p->have_signal && signal == TARGET_SIGNAL_0)
{
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);
doing_fake_step = 1;
fake_step_tid = tid;
}
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
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)
{
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
&& resume_all_threads
&& signal != 0
&& more_events_left > 0)
{
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
}
if (resume_all_threads
&& more_events_left > 0)
{
thread_info *p;
if (!step)
{
#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;
}
}
new_process_state = RUNNING;
if (step)
{
if (resume_all_threads)
{
threads_continue_all_but_one (tid, signal);
clear_all_handled ();
clear_all_stepping_mode ();
}
else
{
thread_fake_step (tid, signal);
clear_handled (tid);
clear_stepping_mode (tid);
new_process_state = FAKE_STEPPING;
}
}
else
{
if ((signal != 0) || saved_signals_exist ())
{
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_handled (tid);
clear_stepping_mode (tid);
}
}
else
{
if (resume_all_threads)
{
#ifdef THREAD_DEBUG
if (debug_on)
printf ("Doing a continue by process of process %d\n", tid);
#endif
if (more_events_left > 0)
{
warning ("Losing buffered events on continue.");
more_events_left = 0;
}
call_ttrace (TT_PROC_CONTINUE,
tid,
TT_NIL,
TT_NIL,
TT_NIL);
clear_all_handled ();
clear_all_stepping_mode ();
}
else
{
#ifdef THREAD_DEBUG
if (debug_on)
{
printf ("Doing a continue of just thread %d\n", tid);
if (is_terminated (tid))
printf ("Why are we continuing a dead thread? (5)\n");
}
#endif
call_ttrace (TT_LWP_CONTINUE,
tid,
TT_NIL,
TT_NIL,
TT_NIL);
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
#ifdef ATTACH_DETACH
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;
if (process_state != STOPPED
&& process_state != VFORKING)
warning ("Internal error attaching.");
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
}
add_tthread (pid, tid);
p = find_thread_info (tid);
if (NULL == p)
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)
{
switch (p->last_stop_state.tts_event)
{
case TTEVT_NONE:
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
{
switch (p->last_stop_state.tts_event)
{
case TTEVT_NONE:
set_handled (pid, tid);
break;
case TTEVT_EXEC:
case TTEVT_FORK:
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));
}
#ifdef PARANOIA
if (debug_on)
print_tthreads ();
#endif
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);
process_state = RUNNING;
}
attach_flag = 1;
}
#endif
#ifdef ATTACH_DETACH
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");
process_state = STOPPED;
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
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 ();
}
#endif
#ifndef TTRACE_XFER_TYPE
#define TTRACE_XFER_TYPE int
#endif
void
_initialize_kernel_u_addr (void)
{
}
#if !defined (CHILD_XFER_MEMORY)
int
child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
struct mem_attrib *attrib,
struct target_ops *target)
{
register int i;
register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (TTRACE_XFER_TYPE);
register int count
= (((memaddr + len) - addr) + sizeof (TTRACE_XFER_TYPE) - 1)
/ sizeof (TTRACE_XFER_TYPE);
register TTRACE_XFER_TYPE *buffer
= (TTRACE_XFER_TYPE *) alloca (count * sizeof (TTRACE_XFER_TYPE));
if (write)
{
if (addr != memaddr || len < (int) sizeof (TTRACE_XFER_TYPE))
{
buffer[0] = call_ttrace (TT_LWP_RDTEXT,
PIDGET (inferior_ptid),
(TTRACE_ARG_TYPE) addr,
TT_NIL,
TT_NIL);
}
if (count > 1)
{
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);
}
memcpy ((char *) buffer + (memaddr & (sizeof (TTRACE_XFER_TYPE) - 1)),
myaddr,
len);
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)
{
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
{
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;
}
memcpy (myaddr,
(char *) buffer + (memaddr & (sizeof (TTRACE_XFER_TYPE) - 1)),
len);
}
return len;
}
static void
udot_info (void)
{
int udot_off;
int udot_val;
char mess[128];
if (!target_has_execution)
{
error ("The program is not being run.");
}
#if !defined (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);
}
printf_filtered (sizeof (int) == 4 ? " 0x%08x" : " 0x%16x", udot_val);
}
printf_filtered ("\n");
#endif
}
#endif
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;
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;
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;
}
}
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;
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)
{
errno = 0;
pid = attach (pid);
}
else
{
if (process_state != VFORKING)
process_state = STOPPED;
update_thread_state_after_attach (pid, DONT_ATTACH_CONTINUE);
}
return pid;
}
int
hppa_require_detach (int pid, int signal)
{
int tt_status;
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;
return pid;
}
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;
}
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;
require_memory_page_dictionary ();
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;
}
if (page == NULL)
{
page = (memory_page_t *) xmalloc (sizeof (memory_page_t));
page->page_start = page_start;
page->reference_count = 0;
page->next = NULL;
page->previous = NULL;
page->original_permissions = write_protect_page (pid, page_start);
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)
{
unwrite_protect_page (pid, page->page_start, page->original_permissions);
if (page->previous != NULL)
page->previous->next = page->next;
if (page->next != NULL)
page->next->previous = page->previous;
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;
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");
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;
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");
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");
}
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");
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;
page = get_dictionary_entry_of_page (pid, page_start);
page->reference_count++;
}
if (dictionary_was_empty)
hppa_enable_syscall_events (pid);
return 1;
}
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");
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--;
if (page->reference_count == 0)
remove_dictionary_entry_of_page (pid, page);
}
dictionary_is_empty = (memory_page_dictionary.page_count == (LONGEST) 0);
if (dictionary_is_empty && memory_page_dictionary.page_protections_allowed)
hppa_disable_syscall_events (pid);
return 1;
}
int
hppa_can_use_hw_watchpoint (enum bptype type, int cnt, enum bptype ot)
{
return (type == bp_hardware_watchpoint);
}
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;
range_is_stack_based = 0;
range_is_accessible = 1;
errno = 0;
page_size = sysconf (_SC_PAGE_SIZE);
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;
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;
}
}
return (!range_is_stack_based && range_is_accessible);
}
char *
hppa_pid_or_tid_to_str (ptid_t ptid)
{
static char buf[100];
pid_t id = PIDGET (ptid);
if (is_process_id (id))
return child_pid_to_str (ptid);
sprintf (buf, "thread %d (", pid_to_thread_id (ptid));
strcat (buf, hppa_tid_to_str (ptid));
strcat (buf, ")\0");
return buf;
}
pid_t
hppa_switched_threads (pid_t gdb_pid)
{
if (gdb_pid == old_gdb_pid)
{
return (pid_t) 0;
}
else if (gdb_pid == reported_pid)
{
return (pid_t) 0;
}
else if (!reported_bpt)
{
return (pid_t) 0;
}
else
{
return reported_pid;
}
return (pid_t) 0;
}
void
hppa_ensure_vforking_parent_remains_stopped (int pid)
{
}
int
hppa_resume_execd_vforking_child_to_get_parent_vfork (void)
{
return 0;
}
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)
{
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)
{
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);
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");
}