diff options
Diffstat (limited to 'gdb/inf-ttrace.c')
-rw-r--r-- | gdb/inf-ttrace.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/gdb/inf-ttrace.c b/gdb/inf-ttrace.c new file mode 100644 index 0000000..da15380 --- /dev/null +++ b/gdb/inf-ttrace.c @@ -0,0 +1,413 @@ +/* Low-level child interface to ttrace. + + Copyright 2004 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" + +/* The ttrace(2) system call didn't exist before HP-UX 10.30. Don't + try to compile this code unless we have it. */ +#ifdef HAVE_TTRACE + +#include "command.h" +#include "gdbcore.h" +#include "inferior.h" +#include "observer.h" +#include "target.h" + +#include "gdb_assert.h" +#include "gdb_string.h" +#include <sys/ttrace.h> + +#include "inf-child.h" +#include "inf-ttrace.h" + +/* HACK: Save the ttrace ops returned by inf_ttrace_target. */ +static struct target_ops *ttrace_ops_hack; + +/* File descriptors for pipes used as semaphores during initial + startup of an inferior. */ +static int inf_ttrace_pfd1[2]; +static int inf_ttrace_pfd2[2]; + +static void +do_cleanup_pfds (void *dummy) +{ + close (inf_ttrace_pfd1[0]); + close (inf_ttrace_pfd1[1]); + close (inf_ttrace_pfd2[0]); + close (inf_ttrace_pfd2[1]); +} + +static void +inf_ttrace_prepare (void) +{ + if (pipe (inf_ttrace_pfd1) == -1) + perror_with_name ("pipe"); + + if (pipe (inf_ttrace_pfd2) == -1) + { + close (inf_ttrace_pfd1[0]); + close (inf_ttrace_pfd2[0]); + perror_with_name ("pipe"); + } +} + +/* Prepare to be traced. */ + +static void +inf_ttrace_me (void) +{ + struct cleanup *old_chain = make_cleanup (do_cleanup_pfds, 0); + char c; + + /* "Trace me, Dr. Memory!" */ + if (ttrace (TT_PROC_SETTRC, 0, 0, 0, TT_VERSION, 0) == -1) + perror_with_name ("ttrace"); + + /* Tell our parent that we are ready to be traced. */ + if (write (inf_ttrace_pfd1[1], &c, sizeof c) != sizeof c) + perror_with_name ("write"); + + /* Wait until our parent has set the initial event mask. */ + if (read (inf_ttrace_pfd2[0], &c, sizeof c) != sizeof c) + perror_with_name ("read"); + + do_cleanups (old_chain); +} + +/* Start tracing PID. */ + +static void +inf_ttrace_him (int pid) +{ + struct cleanup *old_chain = make_cleanup (do_cleanup_pfds, 0); + ttevent_t tte; + ttstate_t tts; + char c; + + /* Wait until our child is ready to be traced. */ + if (read (inf_ttrace_pfd1[0], &c, sizeof c) != sizeof c) + perror_with_name ("read"); + + /* Set the initial event mask. */ + memset (&tte, 0, sizeof (tte)); + tte.tte_events = TTEVT_EXEC | TTEVT_EXIT; + tte.tte_opts = TTEO_NOSTRCCHLD; + if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0, + (uintptr_t)&tte, sizeof tte, 0) == -1) + perror_with_name ("ttrace"); + + /* Tell our child that we have set the initial event mask. */ + if (write (inf_ttrace_pfd2[1], &c, sizeof c) != sizeof c) + perror_with_name ("write"); + + do_cleanups (old_chain); + + push_target (ttrace_ops_hack); + + /* On some targets, there must be some explicit synchronization + between the parent and child processes after the debugger forks, + and before the child execs the debuggee program. This call + basically gives permission for the child to exec. */ + + target_acknowledge_created_inferior (pid); + + /* START_INFERIOR_TRAPS_EXPECTED is defined in inferior.h, and will + be 1 or 2 depending on whether we're starting without or with a + shell. */ + startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + + /* On some targets, there must be some explicit actions taken after + the inferior has been started up. */ + target_post_startup_inferior (pid_to_ptid (pid)); +} + +static void +inf_ttrace_create_inferior (char *exec_file, char *allargs, char **env, + int from_tty) +{ + fork_inferior (exec_file, allargs, env, inf_ttrace_me, inf_ttrace_him, + inf_ttrace_prepare, NULL); + + /* We are at the first instruction we care about. */ + observer_notify_inferior_created (¤t_target, from_tty); + + /* Pedal to the metal... */ + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); +} + +static void +inf_ttrace_kill_inferior (void) +{ + pid_t pid = ptid_get_pid (inferior_ptid); + + if (pid == 0) + return; + + if (ttrace (TT_PROC_EXIT, pid, 0, 0, 0, 0) == -1) + perror_with_name ("ttrace"); + /* ??? Is it necessary to call ttrace_wait() here? */ + target_mourn_inferior (); +} + +static void +inf_ttrace_mourn_inferior (void) +{ + unpush_target (ttrace_ops_hack); + generic_mourn_inferior (); +} + +static void +inf_ttrace_attach (char *args, int from_tty) +{ + char *exec_file; + pid_t pid; + char *dummy; + + if (!args) + error_no_arg ("process-id to attach"); + + dummy = args; + pid = strtol (args, &dummy, 0); + if (pid == 0 && args == dummy) + error ("Illegal process-id: %s\n", args); + + if (pid == getpid ()) /* Trying to masturbate? */ + error ("I refuse to debug myself!"); + + if (from_tty) + { + exec_file = (char *) get_exec_file (0); + + if (exec_file) + printf_unfiltered ("Attaching to program: %s, %s\n", exec_file, + target_pid_to_str (pid_to_ptid (pid))); + else + printf_unfiltered ("Attaching to %s\n", + target_pid_to_str (pid_to_ptid (pid))); + + gdb_flush (gdb_stdout); + } + + if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0) == -1) + perror_with_name ("ttrace"); + attach_flag = 1; + + inferior_ptid = pid_to_ptid (pid); + push_target (ttrace_ops_hack); + + /* Do this first, before anything has had a chance to query the + inferior's symbol table or similar. */ + observer_notify_inferior_created (¤t_target, from_tty); +} + +static void +inf_ttrace_detach (char *args, int from_tty) +{ + int sig = 0; + pid_t pid = ptid_get_pid (inferior_ptid); + + if (from_tty) + { + char *exec_file = get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf_unfiltered ("Detaching from program: %s, %s\n", exec_file, + target_pid_to_str (pid_to_ptid (pid))); + gdb_flush (gdb_stdout); + } + if (args) + sig = atoi (args); + + /* ??? The HP-UX 11.0 ttrace(2) manual page doesn't mention that we + can pass a signal number here. Does this really work? */ + if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1) + perror_with_name ("ttrace"); + + inferior_ptid = null_ptid; + unpush_target (ttrace_ops_hack); +} + +static void +inf_ttrace_resume (ptid_t ptid, int step, enum target_signal signal) +{ + pid_t pid = ptid_get_pid (ptid); + lwpid_t lwpid = ptid_get_lwp (ptid); + ttreq_t request = step ? TT_LWP_SINGLE : TT_LWP_CONTINUE; + int sig = target_signal_to_host (signal); + + if (pid == -1) + { + pid = ptid_get_pid (inferior_ptid); + lwpid = ptid_get_lwp (inferior_ptid); + } + + if (ttrace (request, pid, lwpid, TT_NOPC, sig, 0) == -1) + perror_with_name ("ttrace"); + + if (ptid_equal (ptid, minus_one_ptid)) + { + /* Let all the other threads run too. */ + if (ttrace (TT_PROC_CONTINUE, pid, 0, 0, 0, 0) == -1) + perror_with_name ("ttrace"); + } +} + +static ptid_t +inf_ttrace_wait (ptid_t ptid, struct target_waitstatus *ourstatus) +{ + pid_t pid = ptid_get_pid (ptid); + lwpid_t lwpid = ptid_get_lwp (ptid); + ttstate_t tts; + + ourstatus->kind = TARGET_WAITKIND_IGNORE; + + if (pid == -1) + pid = 0; + + gdb_assert (lwpid == 0 || pid != 0); + + do + { + set_sigint_trap (); + set_sigio_trap (); + + if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1) + perror_with_name ("ttrace_wait"); + + clear_sigio_trap (); + clear_sigint_trap (); + } + while (tts.tts_event == TTEVT_NONE); + + switch (tts.tts_event) + { + case TTEVT_EXEC: + /* Make it look like a breakpoint. */ + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + case TTEVT_EXIT: + store_waitstatus (ourstatus, tts.tts_u.tts_exit.tts_exitcode); + break; + case TTEVT_SIGNAL: + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = + target_signal_from_host (tts.tts_u.tts_signal.tts_signo); + break; + } + + /* Make sure all threads within the process are stopped. */ + if (ttrace (TT_PROC_STOP, tts.tts_pid, 0, 0, 0, 0) == -1) + perror_with_name ("ttrace"); + + /* HACK: Twiddle INFERIOR_PTID such that the initial thread of a + process isn't recognized as a new thread. */ + if (ptid_get_lwp (inferior_ptid) == 0) + inferior_ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, tts.tts_user_tid); + + return ptid_build (tts.tts_pid, tts.tts_lwpid, tts.tts_user_tid); +} + +/* Transfer LEN bytes from ADDR in the inferior's memory into READBUF, + and transfer LEN bytes from WRITEBUF into the inferior's memory at + ADDR. Either READBUF or WRITEBUF may be null, in which case the + corresponding transfer doesn't happen. Return the number of bytes + actually transferred (which may be zero if an error occurs). */ + +static LONGEST +inf_ttrace_xfer_memory (CORE_ADDR addr, ULONGEST len, + void *readbuf, const void *writebuf) +{ + pid_t pid = ptid_get_pid (inferior_ptid); + + /* HP-UX treats text space and data space differently. GDB however, + doesn't really know the difference. Therefore we try both. Try + text space before data space though because when we're writing + into text space the instruction cache might need to be flushed. */ + + if (readbuf + && ttrace (TT_PROC_RDTEXT, pid, 0, addr, len, (uintptr_t)readbuf) == -1 + && ttrace (TT_PROC_RDDATA, pid, 0, addr, len, (uintptr_t)readbuf) == -1) + return 0; + + if (writebuf + && ttrace (TT_PROC_WRTEXT, pid, 0, addr, len, (uintptr_t)writebuf) == -1 + && ttrace (TT_PROC_WRDATA, pid, 0, addr, len, (uintptr_t)writebuf) == -1) + return 0; + + return len; +} + +static LONGEST +inf_ttrace_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, void *readbuf, + const void *writebuf, ULONGEST offset, LONGEST len) +{ + switch (object) + { + case TARGET_OBJECT_MEMORY: + return inf_ttrace_xfer_memory (offset, len, readbuf, writebuf); + + case TARGET_OBJECT_UNWIND_TABLE: + return -1; + + case TARGET_OBJECT_AUXV: + return -1; + + case TARGET_OBJECT_WCOOKIE: + return -1; + + default: + return -1; + } +} + +/* Print status information about what we're accessing. */ + +static void +inf_ttrace_files_info (struct target_ops *ignore) +{ + printf_unfiltered ("\tUsing the running image of %s %s.\n", + attach_flag ? "attached" : "child", + target_pid_to_str (inferior_ptid)); +} + + +struct target_ops * +inf_ttrace_target (void) +{ + struct target_ops *t = inf_child_target (); + + t->to_create_inferior = inf_ttrace_create_inferior; + t->to_kill = inf_ttrace_kill_inferior; + t->to_mourn_inferior = inf_ttrace_mourn_inferior; + t->to_attach = inf_ttrace_attach; + t->to_detach = inf_ttrace_detach; + t->to_resume = inf_ttrace_resume; + t->to_wait = inf_ttrace_wait; + t->to_xfer_partial = inf_ttrace_xfer_partial; + t->to_files_info = inf_ttrace_files_info; + + ttrace_ops_hack = t; + return t; +} + +#endif |