diff options
-rw-r--r-- | gdb/ChangeLog | 18 | ||||
-rw-r--r-- | gdb/Makefile.in | 8 | ||||
-rw-r--r-- | gdb/config/pa/hpux.mh | 2 | ||||
-rw-r--r-- | gdb/hppa-hpux-nat.c | 103 | ||||
-rw-r--r-- | gdb/inf-ttrace.c | 413 | ||||
-rw-r--r-- | gdb/inf-ttrace.h | 30 |
6 files changed, 547 insertions, 27 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 83ecf1a..fcdbd92 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2004-11-23 Mark Kettenis <kettenis@gnu.org> + + * inf-ttrace.c: New file. + * inf-ttrace.h: New file. + * hppa-hpux-nat.c [HAVE_TTRACE]: Include <sys/ttrace>. + Include "inf-ttrace.h". + (ss_mpsfu_high): Define to ss_tlsp if necessary. + (hppa_hpux_fetch_register, hppa_hpux_store_register): Use + ptid_get_pid instead of PIDGET. Modify to handle both ttrace and + ptrace systems. + (_initialize_hppa_hpux_nat) [HAVE_TTRACE]: Call inf_ttrace_traget + instead of inf_ptrace_target. + * config/pa/hpux.mh (NATDEPFILES): Add inf-ttrace.o. + * Makefile.in (inf_ttrace_h): New variable. + (hppa-hpux-nat.o): Update dependency. + (inf-ttrace.o): New dependency. + (ALLDEPFILES): Add inf-ptrace.c and inf-ttrace.c. + 2004-11-23 Randolph Chung <tausq@debian.org> * arch-utils.c (generic_instruction_nullified): New. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 5173f06..ddac599 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -712,6 +712,7 @@ inf_loop_h = inf-loop.h inflow_h = inflow.h $(terminal_h) inf_ptrace_h = inf-ptrace.h infttrace_h = infttrace.h +inf_ttrace_h = inf-ttrace.h interps_h = interps.h jv_lang_h = jv-lang.h kod_h = kod.h @@ -1383,6 +1384,7 @@ ALLDEPFILES = \ i386-sol2-nat.c i386-sol2-tdep.c \ i386gnu-nat.c i386gnu-tdep.c \ ia64-linux-nat.c ia64-linux-tdep.c ia64-tdep.c \ + inf-ptrace.c inf-ttrace.c \ infptrace.c inftarg.c irix4-nat.c irix5-nat.c \ libunwind-frame.c \ lynx-nat.c m3-nat.c \ @@ -1970,7 +1972,8 @@ hppah-nat.o: hppah-nat.c $(defs_h) $(inferior_h) $(target_h) $(gdbcore_h) \ $(gdb_wait_h) $(regcache_h) $(gdb_string_h) $(infttrace_h) \ $(hppa_tdep_h) hppa-hpux-nat.o: hppa-hpux-nat.c $(defs_h) $(inferior_h) $(regcache_h) \ - $(target_h) $(gdb_assert_h) $(hppa_tdep_h) $(inf_ptrace_h) + $(target_h) $(gdb_assert_h) $(hppa_tdep_h) $(inf_ptrace_h) \ + $(inf_ttrace_h) hppa-hpux-tdep.o: hppa-hpux-tdep.c $(defs_h) $(arch_utils_h) $(gdbcore_h) \ $(osabi_h) $(gdb_string_h) $(frame_h) $(frame_unwind_h) \ $(trad_frame_h) $(symtab_h) $(objfiles_h) $(inferior_h) $(infcall_h) \ @@ -2085,6 +2088,9 @@ inflow.o: inflow.c $(defs_h) $(frame_h) $(inferior_h) $(command_h) \ inf-ptrace.o: inf-ptrace.c $(defs_h) $(command_h) $(inferior_h) $(inflow_h) \ $(gdbcore_h) $(observer_h) $(gdb_string_h) $(gdb_ptrace_h) \ $(gdb_wait_h) $(inf_child_h) +inf-ttrace.o: inf-ttrace.c $(defs_h) $(command_h) $(inferior_h) \ + $(observer_h) $(target_h) $(gdb_assert_h) $(gdb_string_h) \ + $(inf_child_h) $(inf_ttrace_h) infptrace.o: infptrace.c $(defs_h) $(command_h) $(frame_h) $(gdbcore_h) \ $(inferior_h) $(regcache_h) $(target_h) $(gdb_assert_h) \ $(gdb_wait_h) $(gdb_string_h) $(gdb_dirent_h) $(gdb_ptrace_h) diff --git a/gdb/config/pa/hpux.mh b/gdb/config/pa/hpux.mh index 0a0d2f8..30d349e 100644 --- a/gdb/config/pa/hpux.mh +++ b/gdb/config/pa/hpux.mh @@ -1,3 +1,3 @@ # Host: PA-RISC HP-UX -NATDEPFILES= fork-child.o inf-ptrace.o \ +NATDEPFILES= fork-child.o inf-ptrace.o inf-ttrace.o \ hppa-hpux-nat.o hpread.o somread.o somsolib.o diff --git a/gdb/hppa-hpux-nat.c b/gdb/hppa-hpux-nat.c index df925d0..11fc993 100644 --- a/gdb/hppa-hpux-nat.c +++ b/gdb/hppa-hpux-nat.c @@ -28,8 +28,20 @@ #include <sys/ptrace.h> #include <machine/save_state.h> +#ifdef HAVE_TTRACE +#include <sys/ttrace.h> +#endif + #include "hppa-tdep.h" #include "inf-ptrace.h" +#include "inf-ttrace.h" + +/* HP-UX 10.20 has a different name than HP-UX 11.00 and later. + Apparently, the intended usage changed. Unfortunately HP didn't + care about backwards compatibility. */ +#ifdef ss_tlsp +#define ss_mpsfu_high ss_tlsp +#endif int child_suppress_run = 0; /* Non-zero if we should pretend not to be a runnable target. */ @@ -179,6 +191,12 @@ hppa_hpux_cannot_store_register (int regnum) return hppa_hpux_cannot_fetch_register (regnum); } +/* Just in case a future version of PA-RISC HP-UX won't have ptrace(2) + at all. */ +#ifndef PTRACE_TYPE_RET +#define PTRACE_TYPE_RET void +#endif + static void hppa_hpux_fetch_register (int regnum) { @@ -194,26 +212,41 @@ hppa_hpux_fetch_register (int regnum) return; } - pid = PIDGET (inferior_ptid); + pid = ptid_get_pid (inferior_ptid); /* This isn't really an address. But ptrace thinks of it as one. */ addr = hppa_hpux_save_state_offset[regnum]; size = register_size (current_gdbarch, regnum); - gdb_assert ((size % sizeof (PTRACE_TYPE_RET)) == 0); + gdb_assert (size == 4 || size == 8); buf = alloca (size); - /* Read the register contents from the inferior a chuck at the time. */ - for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++) - { - errno = 0; - buf[i] = ptrace (PT_RUREGS, pid, (PTRACE_TYPE_ARG3) addr, 0, 0); - if (errno != 0) - error ("Couldn't read register %s (#%d): %s.", REGISTER_NAME (regnum), - regnum, safe_strerror (errno)); +#ifdef HAVE_TTRACE + { + lwpid_t lwp = ptid_get_lwp (inferior_ptid); + + if (ttrace (TT_LWP_RUREGS, pid, lwp, addr, size, (uintptr_t)buf) == -1) + error ("Couldn't read register %s (#%d): %s", + REGISTER_NAME (regnum), regnum, safe_strerror (errno)); + } +#else + { + int i; + + /* Read the register contents from the inferior a chuck at the time. */ + for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++) + { + errno = 0; + buf[i] = ptrace (PT_RUREGS, pid, (PTRACE_TYPE_ARG3) addr, 0, 0); + if (errno != 0) + error ("Couldn't read register %s (#%d): %s", + REGISTER_NAME (regnum), regnum, safe_strerror (errno)); + + addr += sizeof (PTRACE_TYPE_RET); + } + } +#endif - addr += sizeof (PTRACE_TYPE_RET); - } regcache_raw_supply (current_regcache, regnum, buf); } @@ -236,32 +269,46 @@ hppa_hpux_store_register (int regnum) size_t size; PTRACE_TYPE_RET *buf; pid_t pid; - int i; if (hppa_hpux_cannot_store_register (regnum)) return; - pid = PIDGET (inferior_ptid); + pid = ptid_get_pid (inferior_ptid); /* This isn't really an address. But ptrace thinks of it as one. */ addr = hppa_hpux_save_state_offset[regnum]; size = register_size (current_gdbarch, regnum); - gdb_assert ((size % sizeof (PTRACE_TYPE_RET)) == 0); + gdb_assert (size == 4 || size == 8); buf = alloca (size); - /* Write the register contents into the inferior a chunk at the time. */ regcache_raw_collect (current_regcache, regnum, buf); - for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++) - { - errno = 0; - ptrace (PT_WUREGS, pid, (PTRACE_TYPE_ARG3) addr, buf[i], 0); - if (errno != 0) - error ("Couldn't write register %s (#%d): %s.", REGISTER_NAME (regnum), - regnum, safe_strerror (errno)); - addr += sizeof (PTRACE_TYPE_RET); - } +#ifdef HAVE_TTRACE + { + lwpid_t lwp = ptid_get_lwp (inferior_ptid); + + if (ttrace (TT_LWP_WUREGS, pid, lwp, addr, size, (uintptr_t)buf) == -1) + error ("Couldn't write register %s (#%d): %s", + REGISTER_NAME (regnum), regnum, safe_strerror (errno)); + } +#else + { + int i; + + /* Write the register contents into the inferior a chunk at the time. */ + for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++) + { + errno = 0; + ptrace (PT_WUREGS, pid, (PTRACE_TYPE_ARG3) addr, buf[i], 0); + if (errno != 0) + error ("Couldn't write register %s (#%d): %s", + REGISTER_NAME (regnum), regnum, safe_strerror (errno)); + + addr += sizeof (PTRACE_TYPE_RET); + } + } +#endif } /* Store register REGNUM back into the inferior. If REGNUM is -1, do @@ -296,9 +343,15 @@ _initialize_hppa_hpux_nat (void) { struct target_ops *t; +#ifdef HAVE_TTRACE + t = inf_ttrace_target (); +#else t = inf_ptrace_target (); +#endif + t->to_fetch_registers = hppa_hpux_fetch_inferior_registers; t->to_store_registers = hppa_hpux_store_inferior_registers; t->to_can_run = hppa_hpux_child_can_run; + add_target (t); } 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 diff --git a/gdb/inf-ttrace.h b/gdb/inf-ttrace.h new file mode 100644 index 0000000..cc38cbf --- /dev/null +++ b/gdb/inf-ttrace.h @@ -0,0 +1,30 @@ +/* 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. */ + +#ifndef INF_TTRACE_H +#define INF_TTRACE_H + +/* Create a prototype ttrace target. The client can override it with + local methods. */ + +extern struct target_ops *inf_ttrace_target (void); + +#endif |