diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/config/i386/linux.mh ../working/insight/gdb/config/i386/linux.mh --- ../originals/insight-6.0/gdb/config/i386/linux.mh 2003-06-15 21:56:47.000000000 +0100 +++ ../working/insight/gdb/config/i386/linux.mh 2004-01-30 11:02:26.000000000 +0000 @@ -6,7 +6,7 @@ NAT_FILE= nm-linux.h NATDEPFILES= infptrace.o inftarg.o fork-child.o corelow.o linux-proc.o \ core-aout.o i386-nat.o i386-linux-nat.o \ proc-service.o thread-db.o lin-lwp.o linux-proc.o gcore.o \ - linux-nat.o + linux-nat.o valgrind.o # The dynamically loaded libthread_db needs access to symbols in the # gdb executable. diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/doc/gdb.texinfo ../working/insight/gdb/doc/gdb.texinfo --- ../originals/insight-6.0/gdb/doc/gdb.texinfo 2003-08-09 16:10:35.000000000 +0100 +++ ../working/insight/gdb/doc/gdb.texinfo 2004-02-12 18:28:07.000000000 +0000 @@ -1628,6 +1628,7 @@ kill a child process. * Starting:: Starting your program * Arguments:: Your program's arguments * Environment:: Your program's environment +* Shell:: The start-with-shell flag * Working Directory:: Your program's working directory * Input/Output:: Your program's input and output @@ -1736,6 +1737,10 @@ use the @value{GDBN} commands @code{set environment} to change parts of the environment that affect your program. @xref{Environment, ,Your program's environment}. +@item The @emph{@code{start-with-shell} flag.} +You can control whether your program is invoked directly or +indirectly using a shell with the @code{start-with-shell} flag. + @item The @emph{working directory.} Your program inherits its working directory from @value{GDBN}. You can set the @value{GDBN} working directory with the @code{cd} command in @value{GDBN}. @@ -1874,16 +1879,33 @@ program. This is different from @samp{s rather than assigning it an empty value. @end table -@emph{Warning:} On Unix systems, @value{GDBN} runs your program using +@node Shell +@section The start-with-shell flag +@cindex start-with-shell flag + +The @code{start-with-shell} flag controls how your program is invoked. + +@table @code +@kindex set start-with-shell +@item set start-with-shell +By default, on Unix systems @value{GDBN} runs your program using the shell indicated by your @code{SHELL} environment variable if it exists (or -@code{/bin/sh} if not). If your @code{SHELL} variable names a shell +@code{/bin/sh} if not). +@emph{Warning:} If your @code{SHELL} variable names a shell that runs an initialization file---such as @file{.cshrc} for C-shell, or @file{.bashrc} for BASH---any variables you set in that file affect your program. You may wish to move setting of environment variables to files that are only run when you sign on, such as @file{.login} or @file{.profile}. +If this option is unset, @value{GDBN} will invoke your program directly. + +@kindex show start-with-shell +@item show start-with-shell +Show if @value{GDBN} should use shell to invoke inferior. +@end table + @node Working Directory @section Your program's working directory @@ -11173,6 +11195,7 @@ configurations. * SVR4 Process Information:: SVR4 process information * DJGPP Native:: Features specific to the DJGPP port * Cygwin Native:: Features specific to the Cygwin port +* Valgrind:: Using @value{GDBN} with Valgrind @end menu @node HP-UX @@ -11584,6 +11607,25 @@ The author of these extensions is not en break point within a shared DLL like @file{kernel32.dll} is completely safe. +@node Valgrind +@subsection Using @value{GDBN} with Valgrind +@cindex debugging with Valgrind + +@value{GDBN} can be used in conjunction with the Valgrind CPU simulator for +enhanced debugging. The command @code{target valgrind} turns on +Valgrind support. Any program that is subsequently run using the @code{run} +command will be launched using Valgrind. You must have the @code{valgrind} +command in your path for this to work. + +Programs run under Valgrind can be debugged in the normal way. The first +thread (thread 0) is special and corresponds to the actual state of the +process. The other threads correspond to the threads as emulated +by Valgrind. + +As Valgrind doesn't support external debuggers 'out of the box' you will +need the appropriate patch to Valgrind to get the Valgrind target +to work. Only Valgrind 2.0.0 is currently supported. + @node Embedded OS @section Embedded Operating Systems diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/fork-child.c ../working/insight/gdb/fork-child.c --- ../originals/insight-6.0/gdb/fork-child.c 2003-05-08 19:08:57.000000000 +0100 +++ ../working/insight/gdb/fork-child.c 2004-02-11 18:36:34.000000000 +0000 @@ -30,6 +30,7 @@ #include "gdbcore.h" #include "terminal.h" #include "gdbthread.h" +#include "gdbcmd.h" #include "command.h" /* for dont_repeat () */ #include @@ -152,7 +153,7 @@ fork_inferior (char *exec_file_arg, char * bother figuring out what shell. */ shell_file = shell_file_arg; - if (STARTUP_WITH_SHELL) + if (start_with_shell_flag) { /* Figure out what shell to start up the user program under. */ if (shell_file == NULL) @@ -406,9 +407,9 @@ fork_inferior (char *exec_file_arg, char /* Accept NTRAPS traps from the inferior. */ void -startup_inferior (int ntraps) +startup_inferior () { - int pending_execs = ntraps; + int pending_execs; int terminal_initted; /* The process was started by the fork that created it, @@ -421,10 +422,11 @@ startup_inferior (int ntraps) terminal_initted = 0; - if (STARTUP_WITH_SHELL) - inferior_ignoring_startup_exec_events = ntraps; + if (start_with_shell_flag) + pending_execs = START_INFERIOR_TRAPS_EXPECTED; else - inferior_ignoring_startup_exec_events = 0; + pending_execs = START_INFERIOR_TRAPS_EXPECTED_NOSHELL; + inferior_ignoring_startup_exec_events = pending_execs; inferior_ignoring_leading_exec_events = target_reported_exec_events_per_exec_call () - 1; @@ -467,3 +469,16 @@ startup_inferior (int ntraps) } stop_soon = NO_STOP_QUIETLY; } + +void +_initialize_fork_child (void) +{ + struct cmd_list_element *cmd; + + cmd = add_set_cmd ("start-with-shell", class_obscure, var_boolean, + (char *) &start_with_shell_flag, + "Set if GDB should use shell to invoke inferior (performs argument expansion in shell).", + &setlist); + add_show_from_set (cmd, &showlist); +} + diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/infcmd.c ../working/insight/gdb/infcmd.c --- ../originals/insight-6.0/gdb/infcmd.c 2003-06-17 21:28:13.000000000 +0100 +++ ../working/insight/gdb/infcmd.c 2004-02-11 13:36:50.000000000 +0000 @@ -131,6 +131,13 @@ static char *inferior_args; static int inferior_argc; static char **inferior_argv; +/* Use this to tell whether we go to start the inferior using a shell or + * not. This has to be here, rather than in fork-child.c, because it is + * used in construct_inferior_arguments, which is also used here, but not + * everyone uses fork-child... */ + +int start_with_shell_flag = STARTUP_WITH_SHELL; + /* File name for default use for standard in/out in the inferior. */ char *inferior_io_terminal; @@ -267,7 +274,7 @@ construct_inferior_arguments (struct gdb { char *result; - if (STARTUP_WITH_SHELL) + if (start_with_shell_flag) { /* This holds all the characters considered special to the typical Unix shells. We include `^' because the SunOS diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/inferior.h ../working/insight/gdb/inferior.h --- ../originals/insight-6.0/gdb/inferior.h 2003-06-07 23:38:55.000000000 +0100 +++ ../working/insight/gdb/inferior.h 2004-02-11 14:13:45.000000000 +0000 @@ -261,7 +261,7 @@ extern void fork_inferior (char *, char void (*)(int), void (*)(void), char *); -extern void startup_inferior (int); +extern void startup_inferior (); extern char *construct_inferior_arguments (struct gdbarch *, int, char **); @@ -494,7 +494,7 @@ extern int deprecated_pc_in_call_dummy_a DEPRECATED_PC_IN_CALL_DUMMY((pc), (sp), (frame_address)) #endif -/* If STARTUP_WITH_SHELL is set, GDB's "run" +/* If start_with_shell_flag is set, GDB's "run" will attempts to start up the debugee under a shell. This is in order for argument-expansion to occur. E.g., (gdb) run * @@ -514,4 +515,15 @@ extern int deprecated_pc_in_call_dummy_a #if !defined(START_INFERIOR_TRAPS_EXPECTED) #define START_INFERIOR_TRAPS_EXPECTED 2 #endif +#if !defined(START_INFERIOR_TRAPS_EXPECTED_NOSHELL) +#define START_INFERIOR_TRAPS_EXPECTED_NOSHELL (START_INFERIOR_TRAPS_EXPECTED - 1) +#endif + +/* This variable - defined in infcmd.c - can be used to dynamically + * switch the start_with_shell feature. + * It is primed with STARTUP_WITH_SHELL */ + +extern int start_with_shell_flag; + #endif /* !defined (INFERIOR_H) */ + Files ../originals/insight-6.0/gdb/libgdb.a and ../working/insight/gdb/libgdb.a differ diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/Makefile.in ../working/insight/gdb/Makefile.in --- ../originals/insight-6.0/gdb/Makefile.in 2003-08-18 19:10:52.000000000 +0100 +++ ../working/insight/gdb/Makefile.in 2004-02-12 12:04:22.000000000 +0000 @@ -1295,6 +1296,7 @@ ALLDEPFILES = \ sparc64nbsd-nat.c sparcnbsd-nat.c sparcnbsd-tdep.c \ sparc-tdep.c sparcl-tdep.c sun3-nat.c \ symm-tdep.c symm-nat.c \ + valgrind.c \ vax-tdep.c \ vx-share/xdr_ld.c vx-share/xdr_ptrace.c vx-share/xdr_rdb.c \ win32-nat.c \ @@ -2363,6 +2368,9 @@ v850-tdep.o: v850-tdep.c $(defs_h) $(fra valarith.o: valarith.c $(defs_h) $(value_h) $(symtab_h) $(gdbtypes_h) \ $(expression_h) $(target_h) $(language_h) $(gdb_string_h) \ $(doublest_h) $(infcall_h) +valgrind.o: valgrind.c $(defs_h) $(frame_h) $(inferior_h) $(inferior_h) \ + $(target_h) $(gdbcore_h) $(command_h) $(gdb_stat_h) $(i386_tdep_h) \ + $(regcache_h) $(gdbcmd_h) $(gdbthread_h) valops.o: valops.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(value_h) $(frame_h) \ $(inferior_h) $(gdbcore_h) $(target_h) $(demangle_h) $(language_h) \ $(gdbcmd_h) $(regcache_h) $(cp_abi_h) $(block_h) $(infcall_h) \ diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/target.c ../working/insight/gdb/target.c --- ../originals/insight-6.0/gdb/target.c 2003-06-17 21:28:13.000000000 +0100 +++ ../working/insight/gdb/target.c 2004-02-06 11:12:37.000000000 +0000 @@ -1097,8 +1097,10 @@ target_info (char *args, int from_tty) { t = item->target_ops; +#if 0 if (!t->to_has_memory) continue; +#endif if ((int) (t->to_stratum) <= (int) dummy_stratum) continue; diff -I '$Id' -I '$Revision' -I '$Date' -X gdb-exclusions -X gdb-valgrind-exclusions -rpubBN ../originals/insight-6.0/gdb/valgrind.c ../working/insight/gdb/valgrind.c --- ../originals/insight-6.0/gdb/valgrind.c 1970-01-01 01:00:00.000000000 +0100 +++ ../working/insight/gdb/valgrind.c 2004-02-12 18:31:56.000000000 +0000 @@ -0,0 +1,819 @@ +/* Interface GDB to Valgrind simulated CPU. + Copyright 2003 + Christopher January + + This file is part of GDB. + + Written by Christopher January + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "command.h" +#include "gdb_stat.h" +#include "i386-tdep.h" +#include "regcache.h" +#include "gdbcmd.h" +#include "gdbthread.h" +#include +#include +#include +#include +#include +#include + +/* The Valgrind GDB target allows a user to debug a process running + * under the Valgrind CPU simulator. Valgrind doesn't currently + * support external debuggers 'out of the box', so you will need + * the appropriate patch to Valgrind to get this target to work. + * + * The Valgrind patch exposes part of the state of the Valgrind + * simulator, specifically the state of the emulated threads. The + * information is stored in a shared memory region which may be + * accessed by external debuggers such as GDB. */ + +static struct target_ops valgrind_ops; + +/* The default run target (if the Valgrind target was not on the stack). */ +static struct target_ops *default_run_target = NULL; + +/* Extra options to pass to Valgrind. */ +static char *valgrind_opts = NULL; + +/* Valgrind skin to use. + * Skin specific targets should specify a string using: + * + * extern char *valgrind_skin; + * valgrind_skin = "memcheck"; + */ +const char *valgrind_skin = "memcheck"; + +/* Valgrind definitions follow. */ +typedef unsigned char UChar; +typedef unsigned short UShort; +typedef unsigned int UInt; +typedef unsigned long long int ULong; + +typedef signed char Char; +typedef signed short Short; +typedef signed int Int; +typedef signed long long int Long; + +typedef unsigned int Addr; + +typedef unsigned char Bool; +#define False ((Bool)0) +#define True ((Bool)1) + +/* The following is copied from + /usr/src/linux-2.4.9-13/include/asm-i386/signal.h */ +#define VKI_KNSIG 64 /* true for linux 2.2.X and 2.4.X */ +#define VKI_KNSIG_BPW 32 /* since we're using UInts */ +#define VKI_KNSIG_WORDS (VKI_KNSIG / VKI_KNSIG_BPW) + +typedef struct +{ + UInt ws[VKI_KNSIG_WORDS]; +} +vki_ksigset_t; + +#define VG_N_THREADS 100 + +/* Number of entries in each thread's cleanup stack. */ +#define VG_N_CLEANUPSTACK 16 + +/* How big is the saved SSE/SSE2 state? Note that this subsumes the + FPU state. On machines without SSE, we just save/restore the FPU + state into the first part of this area. */ +/* A general comment about SSE save/restore: It appears that the 7th + word (which is the MXCSR) has to be &ed with 0x0000FFBF in order + that restoring from it later does not cause a GP fault (which is + delivered as a segfault). I guess this will have to be done + any time we do fxsave :-( 7th word means word offset 6 or byte + offset 24 from the start address of the save area. + */ +#define VG_SIZE_OF_SSESTATE 512 +/* ... and in words ... */ +#define VG_SIZE_OF_SSESTATE_W ((VG_SIZE_OF_SSESTATE+3)/4) + +/* Special magic value for an invalid ThreadId. It corresponds to + LinuxThreads using zero as the initial value for + pthread_mutex_t.__m_owner and pthread_cond_t.__c_waiting. */ +#define VG_INVALID_THREADID ((ThreadId)(0)) + +/* ThreadIds are simply indices into the VG_(threads)[] array. */ +typedef UInt ThreadId; + +typedef enum +{ + VgTs_Empty, /* this slot is not in use */ + VgTs_Runnable, /* waiting to be scheduled */ + VgTs_WaitJoiner, /* waiting for someone to do join on me */ + VgTs_WaitJoinee, /* waiting for the thread I did join on */ + VgTs_WaitFD, /* waiting for I/O completion on a fd */ + VgTs_WaitMX, /* waiting on a mutex */ + VgTs_WaitCV, /* waiting on a condition variable */ + VgTs_WaitSIG, /* waiting due to sigwait() */ + VgTs_Sleeping /* sleeping for a while */ +} +ThreadStatus; + +/* An entry in a threads's cleanup stack. */ +typedef struct +{ + void (*fn) (void *); + void *arg; +} +CleanupEntry; + +/* An entry in a thread's fork-handler stack. */ +typedef struct +{ + void (*prepare) (void); + void (*parent) (void); + void (*child) (void); +} +ForkHandlerEntry; + +typedef struct _DebugState +{ + ThreadId tid_currently_in_baseBlock; + ThreadId tid_last_in_baseBlock; + Bool need_to_invalidate_all_translations; +} +DebugState; + +typedef struct _ThreadState +{ + /* ThreadId == 0 (and hence vg_threads[0]) is NEVER USED. + The thread identity is simply the index in vg_threads[]. + ThreadId == 1 is the root thread and has the special property + that we don't try and allocate or deallocate its stack. For + convenience of generating error message, we also put the + ThreadId in this tid field, but be aware that it should + ALWAYS == the index in vg_threads[]. */ + ThreadId tid; + + /* Current scheduling status. + + Complications: whenever this is set to VgTs_WaitMX, you + should also set .m_edx to whatever the required return value + is for pthread_mutex_lock / pthread_cond_timedwait for when + the mutex finally gets unblocked. */ + ThreadStatus status; + + /* When .status == WaitMX, points to the mutex I am waiting for. + When .status == WaitCV, points to the mutex associated with + the condition variable indicated by the .associated_cv field. + In all other cases, should be NULL. */ + void * /*pthread_mutex_t* */ associated_mx; + + /* When .status == WaitCV, points to the condition variable I am + waiting for. In all other cases, should be NULL. */ + void * /*pthread_cond_t* */ associated_cv; + + /* If VgTs_Sleeping, this is when we should wake up, measured in + milliseconds as supplied by VG_(read_millisecond_counter). + + If VgTs_WaitCV, this indicates the time at which + pthread_cond_timedwait should wake up. If == 0xFFFFFFFF, + this means infinitely far in the future, viz, + pthread_cond_wait. */ + UInt awaken_at; + + /* If VgTs_WaitJoiner, return value, as generated by joinees. */ + void *joinee_retval; + + /* If VgTs_WaitJoinee, place to copy the return value to, and + the identity of the thread we're waiting for. */ + void **joiner_thread_return; + ThreadId joiner_jee_tid; + + /* Whether or not to single step. */ + Bool single_step; + + /* Whether or not the thread is stopped */ + Bool stopped; + + /* Whether or not detached. */ + Bool detached; + + /* Cancelability state and type. */ + Bool cancel_st; /* False==PTH_CANCEL_DISABLE; True==.._ENABLE */ + Bool cancel_ty; /* False==PTH_CANC_ASYNCH; True==..._DEFERRED */ + + /* Pointer to fn to call to do cancellation. Indicates whether + or not cancellation is pending. If NULL, not pending. Else + should be &thread_exit_wrapper(), indicating that + cancallation is pending. */ + void (*cancel_pend) (void *); + + /* The cleanup stack. */ + Int custack_used; + CleanupEntry custack[VG_N_CLEANUPSTACK]; + + /* A pointer to the thread's-specific-data. This is handled almost + entirely from vg_libpthread.c. We just provide hooks to get and + set this ptr. This is either NULL, indicating the thread has + read/written none of its specifics so far, OR points to a + void*[VG_N_THREAD_KEYS], allocated and deallocated in + vg_libpthread.c. */ + void **specifics_ptr; + + /* This thread's blocked-signals mask. Semantics is that for a + signal to be delivered to this thread, the signal must not be + blocked by either the process-wide signal mask nor by this + one. So, if this thread is prepared to handle any signal that + the process as a whole is prepared to handle, this mask should + be made empty -- and that it is its default, starting + state. */ + vki_ksigset_t sig_mask; + + /* When not VgTs_WaitSIG, has no meaning. When VgTs_WaitSIG, + is the set of signals for which we are sigwait()ing. */ + vki_ksigset_t sigs_waited_for; + + /* Counts the number of times a signal handler for this thread + has returned. This makes it easy to implement pause(), by + polling this value, of course interspersed with nanosleeps, + and waiting till it changes. */ + UInt n_signals_returned; + + /* Stacks. When a thread slot is freed, we don't deallocate its + stack; we just leave it lying around for the next use of the + slot. If the next use of the slot requires a larger stack, + only then is the old one deallocated and a new one + allocated. + + For the main thread (threadid == 0), this mechanism doesn't + apply. We don't know the size of the stack since we didn't + allocate it, and furthermore we never reallocate it. */ + + /* The allocated size of this thread's stack (permanently zero + if this is ThreadId == 0, since we didn't allocate its stack) */ + UInt stack_size; + + /* Address of the lowest word in this thread's stack. NULL means + not allocated yet. + */ + Addr stack_base; + + /* Address of the highest legitimate word in this stack. This is + used for error messages only -- not critical for execution + correctness. Is is set for all stacks, specifically including + ThreadId == 0 (the main thread). */ + Addr stack_highest_word; + + /* Pointer to this thread's Local (Segment) Descriptor Table. + Starts out as NULL, indicating there is no table, and we hope to + keep it that way. If the thread does __NR_modify_ldt to create + entries, we allocate a 8192-entry table at that point. This is + a straight copy of the Linux kernel's scheme. Don't forget to + deallocate this at thread exit. */ + /* VgLdtEntry* ldt; */ + void *ldt; + + /* Saved machine context. Note the FPU state, %EIP and segment + registers are not shadowed. + + Although the segment registers are 16 bits long, storage + management here, in VG_(baseBlock) and in VG_(m_state_static) is + simplified if we pretend they are 32 bits. */ + UInt m_cs; + UInt m_ss; + UInt m_ds; + UInt m_es; + UInt m_fs; + UInt m_gs; + + UInt m_eax; + UInt m_ebx; + UInt m_ecx; + UInt m_edx; + UInt m_esi; + UInt m_edi; + UInt m_ebp; + UInt m_esp; + UInt m_eflags; + UInt m_eip; + + /* The SSE/FPU state. This array does not (necessarily) have the + required 16-byte alignment required to get stuff in/out by + fxsave/fxrestore. So we have to do it "by hand". + */ + UInt m_sse[VG_SIZE_OF_SSESTATE_W]; + + UInt sh_eax; + UInt sh_ebx; + UInt sh_ecx; + UInt sh_edx; + UInt sh_esi; + UInt sh_edi; + UInt sh_ebp; + UInt sh_esp; + UInt sh_eflags; +} +ThreadState; + +#define VG_DEBUG_STATE_SHM_KEY 0x29BD0000 + +static ThreadState *threads = NULL; +static DebugState *debug_state = NULL; + +static int shmid = -1; + +/* Path to the Valgrind shared objects. */ +static char valgrind_path[PATH_MAX]; + +/* Search the current path for a file (in the manner of exec*p). */ +static char * +searchpath (const char *file) +{ + char *path, *p, *name; + size_t len; + size_t pathlen; + static char pathbuf[PATH_MAX]; + + path = getenv ("PATH"); + if (path == NULL) + { + /* There is no `PATH' in the environment. */ + path = "/bin:/usr/bin:"; + } + + len = strlen (file) + 1; + pathlen = strlen (path); + name = alloca (pathlen + len + 1); + /* Copy the file name at the top. */ + name = (char *) memcpy (name + pathlen + 1, file, len); + /* And add the slash. */ + *--name = '/'; + + p = path; + do + { + struct stat buf; + char *startp; + + path = p; + p = strchr (path, ':'); + + if (p == path) + /* Two adjacent colons, or a colon at the beginning or the end + of `PATH' means to search the current directory. */ + startp = name + 1; + else + startp = (char *) memcpy (name - (p - path), path, p - path); + + if (stat (startp, &buf) == 0) + { + strcpy (pathbuf, startp); + return pathbuf; + } + + switch (errno) + { + case EACCES: + case ENOENT: + case ESTALE: + case ENOTDIR: + /* Those errors indicate the file is missing or not executable + by us, in which case we want to just try the next path + directory. */ + break; + + default: + /* Some other error means we found an executable file, but + something went wrong executing it; return the error to our + caller. */ + return NULL; + } + } + while (*p++ != '\0'); + + /* Return the error from the last attempt (probably ENOENT). */ + return NULL; +} + +/* Parse the Valgrind command for the install prefix (in case the user + * copied the script elsewhere.) */ +static void +parse_valgrind_script (const char *filename) +{ + FILE *fh; + char line[PATH_MAX + 80]; + + valgrind_path[0] = 0; + + fh = fopen (filename, "rt"); + if (fh == NULL) + return; + + while (fgets (line, PATH_MAX + 80, fh)) + { + /* Look for a line of the form prefix="/some/path". */ + if (strncmp (line, "prefix=", 7) == 0) + { + if (line[7] == '"') + strncpy (valgrind_path, line + 8, strlen (line) - 10); + else + strcpy (valgrind_path, line + 7); + } + /* Check the Valgrind version. */ + if (strncmp (line, "version=", 8) == 0) + { + if (strncmp (line, "version=\"2.", 11) != 0) + error ("This target only works with Valgrind 2.\n"); + } + } + fclose (fh); + + /* The default prefix is /usr. */ + if (valgrind_path[0] == 0) + strcpy (valgrind_path, "/usr"); + + strcat (valgrind_path, "/lib/valgrind"); +} + +static void +valgrind_open (char *args, int from_tty) +{ + int ontop; + + /* Find out where the Valgrind shared objects are. */ + const char *exec_path = searchpath ("valgrind"); + if (exec_path == NULL) + error ("Valgrind is not in your path.\n"); + parse_valgrind_script (exec_path); + + target_preopen (from_tty); + + ontop = !push_target (&valgrind_ops); + if (args) + { + if (valgrind_opts) + xfree (valgrind_opts); + valgrind_opts = xstrdup (args); + } + + default_run_target = find_run_target (); + if (!default_run_target) + error ("No default run target.\n"); + + /* Valgrind doesn't work with tcsh so we can't use the + * STARTUP_WITH_SHELL feature of gdb. */ + start_with_shell_flag = 0; +} + +static void +valgrind_post_startup_inferior (ptid_t ptid) +{ + int key = PIDGET (inferior_ptid) + VG_DEBUG_STATE_SHM_KEY; + shmid = shmget (key, + sizeof (DebugState) + + VG_N_THREADS * sizeof (ThreadState), + IPC_CREAT | S_IRUSR | S_IWUSR); + if (shmid == -1) + error + ("Cannot create shared memory region (check your resource limits with ipcs.)\n"); + debug_state = shmat (shmid, NULL, 0); + assert (debug_state != (DebugState *) 0xffffffff); + threads = (ThreadState *) (debug_state + 1); +} + +static void +valgrind_close (int from_tty) +{ + if (debug_state) + { + shmdt (debug_state); + debug_state = NULL; + } + start_with_shell_flag = STARTUP_WITH_SHELL; +} + +static void +valgrind_mourn_inferior () +{ + /* Remove the shm id in case the child didn't do so. */ + struct shmid_ds buf; + if (shmid != -1) + { + shmctl (shmid, IPC_RMID, &buf); + shmid = -1; + } + default_run_target->to_mourn_inferior (); +} + +static void +valgrind_create_inferior (char *exec_file, char *allargs, char **env) +{ + char *vg_args; + int size; + char **cur, **cur2; + char **newenv; + char *ld_preload, *ld_library_path; + struct cleanup *old_chain; + + ld_preload = ld_library_path = ""; + + int toalloc = strlen (valgrind_path) + 32; + if (valgrind_opts) + toalloc += strlen (valgrind_opts); + vg_args = xmalloc (toalloc); + old_chain = make_cleanup (xfree, vg_args); + + /* Construct the arguments to pass to Valgrind. */ + sprintf (vg_args, "--suppressions=%s/default.supp", valgrind_path); + if (valgrind_opts) + sprintf (vg_args + strlen (vg_args), " %s", valgrind_opts); + + /* Find the values of LD_LIBRARY_PATH and LD_PRELOAD. */ + for (cur = env, size = 0; *cur; cur++, size++) + { + if (!strcmp (*cur, "LD_LIBRARY_PATH=")) + ld_library_path = strstr (*cur, "=") + 1; + if (!strcmp (*cur, "LD_PRELOAD=")) + ld_preload = strstr (*cur, "=") + 1; + } + + newenv = xmalloc (sizeof (char *) * (size + 4)); + make_cleanup (xfree, newenv); + + /* Remove LD_LIBRARY_PATH and LD_PRELOAD from the environment. */ + for (cur = env, cur2 = newenv; *cur; cur++) + { + if (strcmp (*cur, "LD_LIBRARY_PATH=") && strcmp (*cur, "LD_PRELOAD=")) + *cur2++ = *cur; + } + + *cur2 = xmalloc (strlen ("VG_ARGS=") + strlen (vg_args) + 32); + make_cleanup (xfree, *cur2); + sprintf (*cur2, "VG_ARGS=%s", vg_args); + + *++cur2 = + xmalloc (strlen ("LD_LIBRARY_PATH=") + strlen (valgrind_path) + + strlen (ld_library_path) + 32); + make_cleanup (xfree, *cur2); + sprintf (*cur2, "LD_LIBRARY_PATH=%s:%s", valgrind_path, ld_library_path); + + *++cur2 = + xmalloc (strlen ("LD_PRELOAD=") + strlen (valgrind_path) + + strlen ("valgrind.so") + strlen (valgrind_path) + + strlen (valgrind_skin) + strlen (ld_preload) + 32); + make_cleanup (xfree, *cur2); + sprintf (*cur2, "LD_PRELOAD=%s/vgskin_%s.so:%s/valgrind.so:%s", + valgrind_path, valgrind_skin, valgrind_path, ld_preload); + + *++cur2 = NULL; + + default_run_target->to_create_inferior (exec_file, allargs, newenv); + + do_cleanups (old_chain); +} + +static void +valgrind_files_info (struct target_ops *ops) +{ + /* The Valgrind target doesn't use any files. */ +} + +static void +valgrind_fetch_registers (int regno) +{ + int tid = TIDGET (inferior_ptid); + if (tid == 0) + { + /* Thread 0 is the real process. */ + default_run_target->to_fetch_registers (regno); + return; + } + if (regno < 0) + { + for (regno = 0; regno < NUM_REGS; regno++) + valgrind_fetch_registers (regno); + } + else if (regno < I386_NUM_GREGS) + { + void *addr; + ThreadState *thread = &threads[tid]; + + switch (regno) + { +#define GETREGADDR(regno,regname) \ +case regno: \ + addr = &(thread->m_ ## regname); \ + break; + GETREGADDR (0, eax); + GETREGADDR (1, ecx); + GETREGADDR (2, edx); + GETREGADDR (3, ebx); + GETREGADDR (4, esp); + GETREGADDR (5, ebp); + GETREGADDR (6, esi); + GETREGADDR (7, edi); + GETREGADDR (8, eip); + GETREGADDR (9, eflags); + GETREGADDR (10, cs); + GETREGADDR (11, ss); + GETREGADDR (12, ds); + GETREGADDR (13, es); + GETREGADDR (14, fs); + GETREGADDR (15, gs); + default: + printf_unfiltered ("unknown regno\n"); + return; + } +#undef GETREGADDR + + supply_register (regno, addr); + } +} + +static void +valgrind_store_registers (int regno) +{ + int tid = TIDGET (inferior_ptid); + if (tid == 0) + { + /* Thread 0 is the real process. */ + default_run_target->to_store_registers (regno); + return; + } + + if (regno < 0) + { + for (regno = 0; regno < NUM_REGS; regno++) + valgrind_store_registers (regno); + } + else if (regno < I386_NUM_GREGS) + { + void *addr; + ThreadState *thread = &threads[tid]; + switch (regno) + { +#define GETREGADDR(regno,regname) \ +case regno: \ + addr = &(thread->m_ ## regname); \ + break; + GETREGADDR (0, eax); + GETREGADDR (1, ecx); + GETREGADDR (2, edx); + GETREGADDR (3, ebx); + GETREGADDR (4, esp); + GETREGADDR (5, ebp); + GETREGADDR (6, esi); + GETREGADDR (7, edi); + GETREGADDR (8, eip); + GETREGADDR (9, eflags); + GETREGADDR (10, cs); + GETREGADDR (11, ss); + GETREGADDR (12, ds); + GETREGADDR (13, es); + GETREGADDR (14, fs); + GETREGADDR (15, gs); + default: + printf_unfiltered ("unknown regno\n"); + return; + } +#undef GETREGADDR + regcache_collect (regno, addr); + } +} + +static void +valgrind_resume (ptid_t ptid, int step, enum target_signal sig) +{ + int tid = TIDGET (ptid); + + /* If ptid == RESUME_ALL then clear the status of all the threads. */ + if (threads && PIDGET (ptid) == -1) + { + for (tid = 0; tid < VG_N_THREADS; tid++) + { + threads[tid].single_step = 0; + threads[tid].stopped = False; + } + } + + if (threads && debug_state && tid != 0) + { + threads[tid].stopped = False; + threads[tid].single_step = step; + step = 0; + /* Tell Valgrind to invalidate all preexisting translations so + * any new breakpoints are picked up. */ + debug_state->need_to_invalidate_all_translations = True; + ptid = ptid_build (PIDGET (ptid), 0, 0); + } + + default_run_target->to_resume (ptid, step, sig); +} + +static ptid_t +valgrind_wait (ptid_t pid, struct target_waitstatus *waitstatus) +{ + ptid_t wait_pid = default_run_target->to_wait (pid, waitstatus); + if (threads) + { + int tid; + for (tid = 0; tid < VG_N_THREADS; tid++) + threads[tid].stopped = True; + } + if (debug_state) + { + int tid = debug_state->tid_currently_in_baseBlock; + if (tid == -1) + tid = 0; + wait_pid = ptid_build (PIDGET (wait_pid), tid, 0); + /* This is the right place to do this, otherwise handle_inferior_event + * will break. */ + if (!in_thread_list (wait_pid)) + add_thread (wait_pid); + } + else + { + wait_pid = ptid_build (PIDGET (wait_pid), 0, 0); + } + return wait_pid; +} + +int +valgrind_thread_alive (ptid_t ptid) +{ + int tid = TIDGET (ptid); + + return threads[tid].status != VgTs_Empty; +} + +void +valgrind_find_new_threads (void) +{ + int tid; + for (tid = 0; tid < VG_N_THREADS; tid++) + { + if (threads[tid].status != VgTs_Empty) + { + ptid_t gdb_threadid = ptid_build (PIDGET (inferior_ptid), tid, 0); + + if (!in_thread_list (gdb_threadid)) + add_thread (gdb_threadid); + } + } +} + +char * +valgrind_extra_thread_info (struct thread_info *tinfo) +{ + return NULL; +} + +static void +init_valgrind_ops (void) +{ + valgrind_ops.to_shortname = "valgrind"; + valgrind_ops.to_longname = "Valgrind simulated CPU"; + valgrind_ops.to_doc = "Valgrind simulated CPU."; + valgrind_ops.to_open = valgrind_open; + valgrind_ops.to_close = valgrind_close; + valgrind_ops.to_fetch_registers = valgrind_fetch_registers; + valgrind_ops.to_store_registers = valgrind_store_registers; + valgrind_ops.to_resume = valgrind_resume; + valgrind_ops.to_wait = valgrind_wait; + valgrind_ops.to_thread_alive = valgrind_thread_alive; + valgrind_ops.to_files_info = valgrind_files_info; + valgrind_ops.to_create_inferior = valgrind_create_inferior; + valgrind_ops.to_post_startup_inferior = valgrind_post_startup_inferior; + valgrind_ops.to_mourn_inferior = valgrind_mourn_inferior; + valgrind_ops.to_stratum = thread_stratum; + valgrind_ops.to_has_registers = 1; + valgrind_ops.to_has_thread_control = 1; + valgrind_ops.to_find_new_threads = valgrind_find_new_threads; + valgrind_ops.to_magic = OPS_MAGIC; +} + +extern void +_initialize_valgrind (void) +{ + init_valgrind_ops (); + + add_target (&valgrind_ops); +}