Index: gdb/gdbserver/lynx-low.c |
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c |
index 2c7ab6e3dfa193fe433bf2eece609d3c419fcbd1..3c061b8f0779e3e4966d1c45f71ea3a8162c0799 100644 |
--- a/gdb/gdbserver/lynx-low.c |
+++ b/gdb/gdbserver/lynx-low.c |
@@ -1,4 +1,4 @@ |
-/* Copyright (C) 2009-2012 Free Software Foundation, Inc. |
+/* Copyright (C) 2009-2013 Free Software Foundation, Inc. |
This file is part of GDB. |
@@ -25,11 +25,23 @@ |
#include <unistd.h> |
#include <sys/ioctl.h> |
#include <sys/types.h> |
-#include <sys/wait.h> |
+#include "gdb_wait.h" |
#include <signal.h> |
+#include "filestuff.h" |
int using_threads = 1; |
+const struct target_desc *lynx_tdesc; |
+ |
+/* Per-process private data. */ |
+ |
+struct process_info_private |
+{ |
+ /* The PTID obtained from the last wait performed on this process. |
+ Initialized to null_ptid until the first wait is performed. */ |
+ ptid_t last_wait_event_ptid; |
+}; |
+ |
/* Print a debug trace on standard output if debug_threads is set. */ |
static void |
@@ -96,169 +108,79 @@ lynx_ptrace_pid_from_ptid (ptid_t ptid) |
static char * |
ptrace_request_to_str (int request) |
{ |
+#define CASE(X) case X: return #X |
switch (request) |
{ |
- case PTRACE_TRACEME: |
- return "PTRACE_TRACEME"; |
- break; |
- case PTRACE_PEEKTEXT: |
- return "PTRACE_PEEKTEXT"; |
- break; |
- case PTRACE_PEEKDATA: |
- return "PTRACE_PEEKDATA"; |
- break; |
- case PTRACE_PEEKUSER: |
- return "PTRACE_PEEKUSER"; |
- break; |
- case PTRACE_POKETEXT: |
- return "PTRACE_POKETEXT"; |
- break; |
- case PTRACE_POKEDATA: |
- return "PTRACE_POKEDATA"; |
- break; |
- case PTRACE_POKEUSER: |
- return "PTRACE_POKEUSER"; |
- break; |
- case PTRACE_CONT: |
- return "PTRACE_CONT"; |
- break; |
- case PTRACE_KILL: |
- return "PTRACE_KILL"; |
- break; |
- case PTRACE_SINGLESTEP: |
- return "PTRACE_SINGLESTEP"; |
- break; |
- case PTRACE_ATTACH: |
- return "PTRACE_ATTACH"; |
- break; |
- case PTRACE_DETACH: |
- return "PTRACE_DETACH"; |
- break; |
- case PTRACE_GETREGS: |
- return "PTRACE_GETREGS"; |
- break; |
- case PTRACE_SETREGS: |
- return "PTRACE_SETREGS"; |
- break; |
- case PTRACE_GETFPREGS: |
- return "PTRACE_GETFPREGS"; |
- break; |
- case PTRACE_SETFPREGS: |
- return "PTRACE_SETFPREGS"; |
- break; |
- case PTRACE_READDATA: |
- return "PTRACE_READDATA"; |
- break; |
- case PTRACE_WRITEDATA: |
- return "PTRACE_WRITEDATA"; |
- break; |
- case PTRACE_READTEXT: |
- return "PTRACE_READTEXT"; |
- break; |
- case PTRACE_WRITETEXT: |
- return "PTRACE_WRITETEXT"; |
- break; |
- case PTRACE_GETFPAREGS: |
- return "PTRACE_GETFPAREGS"; |
- break; |
- case PTRACE_SETFPAREGS: |
- return "PTRACE_SETFPAREGS"; |
- break; |
- case PTRACE_GETWINDOW: |
- return "PTRACE_GETWINDOW"; |
- break; |
- case PTRACE_SETWINDOW: |
- return "PTRACE_SETWINDOW"; |
- break; |
- case PTRACE_SYSCALL: |
- return "PTRACE_SYSCALL"; |
- break; |
- case PTRACE_DUMPCORE: |
- return "PTRACE_DUMPCORE"; |
- break; |
- case PTRACE_SETWRBKPT: |
- return "PTRACE_SETWRBKPT"; |
- break; |
- case PTRACE_SETACBKPT: |
- return "PTRACE_SETACBKPT"; |
- break; |
- case PTRACE_CLRBKPT: |
- return "PTRACE_CLRBKPT"; |
- break; |
- case PTRACE_GET_UCODE: |
- return "PTRACE_GET_UCODE"; |
- break; |
+ CASE(PTRACE_TRACEME); |
+ CASE(PTRACE_PEEKTEXT); |
+ CASE(PTRACE_PEEKDATA); |
+ CASE(PTRACE_PEEKUSER); |
+ CASE(PTRACE_POKETEXT); |
+ CASE(PTRACE_POKEDATA); |
+ CASE(PTRACE_POKEUSER); |
+ CASE(PTRACE_CONT); |
+ CASE(PTRACE_KILL); |
+ CASE(PTRACE_SINGLESTEP); |
+ CASE(PTRACE_ATTACH); |
+ CASE(PTRACE_DETACH); |
+ CASE(PTRACE_GETREGS); |
+ CASE(PTRACE_SETREGS); |
+ CASE(PTRACE_GETFPREGS); |
+ CASE(PTRACE_SETFPREGS); |
+ CASE(PTRACE_READDATA); |
+ CASE(PTRACE_WRITEDATA); |
+ CASE(PTRACE_READTEXT); |
+ CASE(PTRACE_WRITETEXT); |
+ CASE(PTRACE_GETFPAREGS); |
+ CASE(PTRACE_SETFPAREGS); |
+ CASE(PTRACE_GETWINDOW); |
+ CASE(PTRACE_SETWINDOW); |
+ CASE(PTRACE_SYSCALL); |
+ CASE(PTRACE_DUMPCORE); |
+ CASE(PTRACE_SETWRBKPT); |
+ CASE(PTRACE_SETACBKPT); |
+ CASE(PTRACE_CLRBKPT); |
+ CASE(PTRACE_GET_UCODE); |
#ifdef PT_READ_GPR |
- case PT_READ_GPR: |
- return "PT_READ_GPR"; |
- break; |
+ CASE(PT_READ_GPR); |
#endif |
#ifdef PT_WRITE_GPR |
- case PT_WRITE_GPR: |
- return "PT_WRITE_GPR"; |
- break; |
+ CASE(PT_WRITE_GPR); |
#endif |
#ifdef PT_READ_FPR |
- case PT_READ_FPR: |
- return "PT_READ_FPR"; |
- break; |
+ CASE(PT_READ_FPR); |
#endif |
#ifdef PT_WRITE_FPR |
- case PT_WRITE_FPR: |
- return "PT_WRITE_FPR"; |
- break; |
+ CASE(PT_WRITE_FPR); |
#endif |
#ifdef PT_READ_VPR |
- case PT_READ_VPR: |
- return "PT_READ_VPR"; |
- break; |
+ CASE(PT_READ_VPR); |
#endif |
#ifdef PT_WRITE_VPR |
- case PT_WRITE_VPR: |
- return "PT_WRITE_VPR"; |
- break; |
+ CASE(PT_WRITE_VPR); |
#endif |
#ifdef PTRACE_PEEKUSP |
- case PTRACE_PEEKUSP: |
- return "PTRACE_PEEKUSP"; |
- break; |
+ CASE(PTRACE_PEEKUSP); |
#endif |
#ifdef PTRACE_POKEUSP |
- case PTRACE_POKEUSP: |
- return "PTRACE_POKEUSP"; |
- break; |
+ CASE(PTRACE_POKEUSP); |
+#endif |
+ CASE(PTRACE_PEEKTHREAD); |
+ CASE(PTRACE_THREADUSER); |
+ CASE(PTRACE_FPREAD); |
+ CASE(PTRACE_FPWRITE); |
+ CASE(PTRACE_SETSIG); |
+ CASE(PTRACE_CONT_ONE); |
+ CASE(PTRACE_KILL_ONE); |
+ CASE(PTRACE_SINGLESTEP_ONE); |
+ CASE(PTRACE_GETLOADINFO); |
+ CASE(PTRACE_GETTRACESIG); |
+#ifdef PTRACE_GETTHREADLIST |
+ CASE(PTRACE_GETTHREADLIST); |
#endif |
- case PTRACE_PEEKTHREAD: |
- return "PTRACE_PEEKTHREAD"; |
- break; |
- case PTRACE_THREADUSER: |
- return "PTRACE_THREADUSER"; |
- break; |
- case PTRACE_FPREAD: |
- return "PTRACE_FPREAD"; |
- break; |
- case PTRACE_FPWRITE: |
- return "PTRACE_FPWRITE"; |
- break; |
- case PTRACE_SETSIG: |
- return "PTRACE_SETSIG"; |
- break; |
- case PTRACE_CONT_ONE: |
- return "PTRACE_CONT_ONE"; |
- break; |
- case PTRACE_KILL_ONE: |
- return "PTRACE_KILL_ONE"; |
- break; |
- case PTRACE_SINGLESTEP_ONE: |
- return "PTRACE_SINGLESTEP_ONE"; |
- break; |
- case PTRACE_GETLOADINFO: |
- return "PTRACE_GETLOADINFO"; |
- break; |
- case PTRACE_GETTHREADLIST: |
- return "PTRACE_GETTHREADLIST"; |
- break; |
} |
+#undef CASE |
+ |
return "<unknown-request>"; |
} |
@@ -286,12 +208,27 @@ lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2) |
return result; |
} |
+/* Call add_process with the given parameters, and initializes |
+ the process' private data. */ |
+ |
+static struct process_info * |
+lynx_add_process (int pid, int attached) |
+{ |
+ struct process_info *proc; |
+ |
+ proc = add_process (pid, attached); |
+ proc->tdesc = lynx_tdesc; |
+ proc->private = xcalloc (1, sizeof (*proc->private)); |
+ proc->private->last_wait_event_ptid = null_ptid; |
+ |
+ return proc; |
+} |
+ |
/* Implement the create_inferior method of the target_ops vector. */ |
static int |
lynx_create_inferior (char *program, char **allargs) |
{ |
- struct process_info *new_process; |
int pid; |
lynx_debug ("lynx_create_inferior ()"); |
@@ -304,6 +241,8 @@ lynx_create_inferior (char *program, char **allargs) |
{ |
int pgrp; |
+ close_most_fds (); |
+ |
/* Switch child to its own process group so that signals won't |
directly affect gdbserver. */ |
pgrp = getpid(); |
@@ -316,27 +255,62 @@ lynx_create_inferior (char *program, char **allargs) |
_exit (0177); |
} |
- new_process = add_process (pid, 0); |
+ lynx_add_process (pid, 0); |
/* Do not add the process thread just yet, as we do not know its tid. |
We will add it later, during the wait for the STOP event corresponding |
to the lynx_ptrace (PTRACE_TRACEME) call above. */ |
return pid; |
} |
+/* Assuming we've just attached to a running inferior whose pid is PID, |
+ add all threads running in that process. */ |
+ |
+static void |
+lynx_add_threads_after_attach (int pid) |
+{ |
+ /* Ugh! There appears to be no way to get the list of threads |
+ in the program we just attached to. So get the list by calling |
+ the "ps" command. This is only needed now, as we will then |
+ keep the thread list up to date thanks to thread creation and |
+ exit notifications. */ |
+ FILE *f; |
+ char buf[256]; |
+ int thread_pid, thread_tid; |
+ |
+ f = popen ("ps atx", "r"); |
+ if (f == NULL) |
+ perror_with_name ("Cannot get thread list"); |
+ |
+ while (fgets (buf, sizeof (buf), f) != NULL) |
+ if ((sscanf (buf, "%d %d", &thread_pid, &thread_tid) == 2 |
+ && thread_pid == pid)) |
+ { |
+ ptid_t thread_ptid = lynx_ptid_build (pid, thread_tid); |
+ |
+ if (!find_thread_ptid (thread_ptid)) |
+ { |
+ lynx_debug ("New thread: (pid = %d, tid = %d)", |
+ pid, thread_tid); |
+ add_thread (thread_ptid, NULL); |
+ } |
+ } |
+ |
+ pclose (f); |
+} |
+ |
/* Implement the attach target_ops method. */ |
static int |
lynx_attach (unsigned long pid) |
{ |
- struct process_info *new_process; |
ptid_t ptid = lynx_ptid_build (pid, 0); |
if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0) |
error ("Cannot attach to process %lu: %s (%d)\n", pid, |
strerror (errno), errno); |
- new_process = add_process (pid, 1); |
- add_thread (ptid, NULL); |
+ lynx_add_process (pid, 1); |
+ lynx_add_threads_after_attach (pid); |
return 0; |
} |
@@ -346,15 +320,34 @@ lynx_attach (unsigned long pid) |
static void |
lynx_resume (struct thread_resume *resume_info, size_t n) |
{ |
- ptid_t inferior_ptid = thread_to_gdb_id (current_inferior); |
/* FIXME: Assume for now that n == 1. */ |
+ ptid_t ptid = resume_info[0].thread; |
const int request = (resume_info[0].kind == resume_step |
? PTRACE_SINGLESTEP : PTRACE_CONT); |
const int signal = resume_info[0].sig; |
- int ret; |
+ |
+ /* If given a minus_one_ptid, then try using the current_process' |
+ private->last_wait_event_ptid. On most LynxOS versions, |
+ using any of the process' thread works well enough, but |
+ LynxOS 178 is a little more sensitive, and triggers some |
+ unexpected signals (Eg SIG61) when we resume the inferior |
+ using a different thread. */ |
+ if (ptid_equal (ptid, minus_one_ptid)) |
+ ptid = current_process()->private->last_wait_event_ptid; |
+ |
+ /* The ptid might still be minus_one_ptid; this can happen between |
+ the moment we create the inferior or attach to a process, and |
+ the moment we resume its execution for the first time. It is |
+ fine to use the current_inferior's ptid in those cases. */ |
+ if (ptid_equal (ptid, minus_one_ptid)) |
+ ptid = thread_to_gdb_id (current_inferior); |
regcache_invalidate (); |
- ret = lynx_ptrace (request, inferior_ptid, 1, signal, 0); |
+ |
+ errno = 0; |
+ lynx_ptrace (request, ptid, 1, signal, 0); |
+ if (errno) |
+ perror_with_name ("ptrace"); |
} |
/* Resume the execution of the given PTID. */ |
@@ -371,16 +364,6 @@ lynx_continue (ptid_t ptid) |
lynx_resume (&resume_info, 1); |
} |
-/* Remove all inferiors and associated threads. */ |
- |
-static void |
-lynx_clear_inferiors (void) |
-{ |
- /* We do not use private data, so nothing much to do except calling |
- clear_inferiors. */ |
- clear_inferiors (); |
-} |
- |
/* A wrapper around waitpid that handles the various idiosyncrasies |
of LynxOS' waitpid. */ |
@@ -438,6 +421,7 @@ retry: |
ret = lynx_waitpid (pid, &wstat); |
new_ptid = lynx_ptid_build (ret, ((union wait *) &wstat)->w_tid); |
+ find_process_pid (ret)->private->last_wait_event_ptid = new_ptid; |
/* If this is a new thread, then add it now. The reason why we do |
this here instead of when handling new-thread events is because |
@@ -445,7 +429,11 @@ retry: |
for non-threaded applications where the new-thread events are not |
generated. */ |
if (!find_thread_ptid (new_ptid)) |
- add_thread (new_ptid, NULL); |
+ { |
+ lynx_debug ("New thread: (pid = %d, tid = %d)", |
+ lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid)); |
+ add_thread (new_ptid, NULL); |
+ } |
if (WIFSTOPPED (wstat)) |
{ |
@@ -491,12 +479,12 @@ retry: |
case SIGNEWTHREAD: |
/* We just added the new thread above. No need to do anything |
further. Just resume the execution again. */ |
- lynx_continue (ptid); |
+ lynx_continue (new_ptid); |
goto retry; |
case SIGTHREADEXIT: |
remove_thread (find_thread_ptid (new_ptid)); |
- lynx_continue (ptid); |
+ lynx_continue (new_ptid); |
goto retry; |
} |
} |
@@ -562,7 +550,11 @@ lynx_detach (int pid) |
static void |
lynx_mourn (struct process_info *proc) |
{ |
- lynx_clear_inferiors (); |
+ /* Free our private data. */ |
+ free (proc->private); |
+ proc->private = NULL; |
+ |
+ clear_inferiors (); |
} |
/* Implement the join target_ops method. */ |