diff options
author | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-05-06 21:02:55 +0000 |
---|---|---|
committer | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-05-06 21:02:55 +0000 |
commit | 3b290486cd4cd601b20e04340e593c9ed9717e5f (patch) | |
tree | aa42d3aca15d82df7023cd2455bc2dbc8c34417e | |
parent | 9943003f546be63b208d02ab57fd31b1c00b8aba (diff) |
Implement a GDB server in Valgrind. See #214909.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11727 a5019735-40e9-0310-863c-91ae7b9d1cf9
193 files changed, 17528 insertions, 132 deletions
diff --git a/Makefile.am b/Makefile.am index 61fddf53..be6e8b76 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ else TEST_EXP_TOOLS = exp-bbv endif + # Put docs last because building the HTML is slow and we want to get # everything else working before we try it. SUBDIRS = \ @@ -36,6 +37,7 @@ SUBDIRS = \ $(EXP_TOOLS) \ tests \ perf \ + gdbserver_tests \ auxprogs \ mpi \ docs @@ -72,11 +74,15 @@ default.supp: $(DEFAULT_SUPP_FILES) ## Preprend @PERL@ because tests/vg_regtest isn't executable regtest: check - @PERL@ tests/vg_regtest $(TEST_TOOLS) $(TEST_EXP_TOOLS) + gdbserver_tests/make_local_links $(GDB) + @PERL@ tests/vg_regtest gdbserver_tests $(TEST_TOOLS) $(TEST_EXP_TOOLS) nonexp-regtest: check @PERL@ tests/vg_regtest $(TEST_TOOLS) exp-regtest: check - @PERL@ tests/vg_regtest $(TEST_EXP_TOOLS) + @PERL@ tests/vg_regtest gdbserver_tests $(TEST_EXP_TOOLS) +# Nb: gdbserver_tests are put in exp-regtest rather than nonexp-regtest +# because they are tested with various valgrind tools, so might be using +# an experimental tool. ## Preprend @PERL@ because tests/vg_perf isn't executable perf: check diff --git a/callgrind/docs/cl-manual.xml b/callgrind/docs/cl-manual.xml index 3f8330ea..cb79bdc6 100644 --- a/callgrind/docs/cl-manual.xml +++ b/callgrind/docs/cl-manual.xml @@ -1075,6 +1075,32 @@ Also see <xref linkend="cl-manual.cycles"/>.</para> </sect1> +<sect1 id="cl-manual.monitor-commands" xreflabel="Callgrind Monitor Commands"> +<title>Callgrind Monitor Commands</title> +<para>The Callgrind tool provides monitor commands handled by the Valgrind +gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>). +</para> + +<itemizedlist> + <listitem> + <para><varname>ct.dump [<dump_hint>]</varname> requests to dump the + profile data. </para> + </listitem> + + <listitem> + <para><varname>ct.zero</varname> requests to zero the profile data + counters. </para> + </listitem> + + <listitem> + <para>It would be nice to have some more callgrind monitor + commands such as e.g. toggle collect and start instrumentation. + </para> + </listitem> + +</itemizedlist> +</sect1> + <sect1 id="cl-manual.clientrequests" xreflabel="Client request reference"> <title>Callgrind specific client requests</title> diff --git a/callgrind/main.c b/callgrind/main.c index a07c453c..7de26557 100644 --- a/callgrind/main.c +++ b/callgrind/main.c @@ -36,6 +36,7 @@ #include "global.h" #include "pub_tool_threadstate.h" +#include "pub_tool_gdbserver.h" #include "cg_branchpred.c" @@ -1355,11 +1356,56 @@ void CLG_(set_instrument_state)(Char* reason, Bool state) reason, state ? "ON" : "OFF"); } +static void print_monitor_help ( void ) +{ + VG_(gdb_printf) ("\n"); + VG_(gdb_printf) ("callgrind monitor commands:\n"); + VG_(gdb_printf) (" ct.dump [<dump_hint>]\n"); + VG_(gdb_printf) (" dump counters\n"); + VG_(gdb_printf) (" ct.zero\n"); + VG_(gdb_printf) (" zero counters\n"); + VG_(gdb_printf) ("\n"); +} + +/* return True if request recognised, False otherwise */ +static Bool handle_gdb_monitor_command (ThreadId tid, Char *req) +{ + Char* wcmd; + Char s[VG_(strlen(req))]; /* copy for strtok_r */ + Char *ssaveptr; + + VG_(strcpy) (s, req); + + wcmd = VG_(strtok_r) (s, " ", &ssaveptr); + switch (VG_(keyword_id) ("help ct.dump ct.zero", + wcmd, kwd_report_duplicated_matches)) { + case -2: /* multiple matches */ + return True; + case -1: /* not found */ + return False; + case 0: /* help */ + print_monitor_help(); + return True; + case 1: { /* ct.dump */ + CLG_(dump_profile)(req, False); + return True; + } + case 2: { /* ct.zero */ + CLG_(zero_all_cost)(False); + return True; + } + + default: + tl_assert(0); + return False; + } +} static Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret) { - if (!VG_IS_TOOL_USERREQ('C','T',args[0])) + if (!VG_IS_TOOL_USERREQ('C','T',args[0]) + && VG_USERREQ__GDB_MONITOR_COMMAND != args[0]) return False; switch(args[0]) { @@ -1399,6 +1445,14 @@ Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret) *ret = 0; /* meaningless */ break; + case VG_USERREQ__GDB_MONITOR_COMMAND: { + Bool handled = handle_gdb_monitor_command (tid, (Char*)args[1]); + if (handled) + *ret = 1; + else + *ret = 0; + return handled; + } default: return False; } diff --git a/configure.in b/configure.in index 667064e1..a0df0633 100644 --- a/configure.in +++ b/configure.in @@ -1907,6 +1907,7 @@ AC_CONFIG_FILES([ tests/vg_regtest perf/Makefile perf/vg_perf + gdbserver_tests/Makefile include/Makefile auxprogs/Makefile mpi/Makefile diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 57fa5935..43d8683a 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -31,6 +31,7 @@ EXTRA_DIST = \ bin_PROGRAMS = \ valgrind \ + vgdb \ no_op_client_for_valgrind if VGCONF_OS_IS_LINUX @@ -58,6 +59,12 @@ if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN valgrind_LDFLAGS += -Wl,-read_only_relocs -Wl,suppress endif +vgdb_SOURCES = vgdb.c +vgdb_CPPFLAGS = $(AM_CPPFLAGS_PRI) +vgdb_CFLAGS = $(AM_CFLAGS_PRI) +vgdb_CCASFLAGS = $(AM_CCASFLAGS_PRI) +vgdb_LDFLAGS = $(AM_CFLAGS_PRI) -lpthread + no_op_client_for_valgrind_SOURCES = no_op_client_for_valgrind.c no_op_client_for_valgrind_CPPFLAGS = $(AM_CPPFLAGS_PRI) no_op_client_for_valgrind_CFLAGS = $(AM_CFLAGS_PRI) @@ -147,6 +154,7 @@ noinst_HEADERS = \ pub_core_dispatch_asm.h \ pub_core_errormgr.h \ pub_core_execontext.h \ + pub_core_gdbserver.h \ pub_core_hashtable.h \ pub_core_initimg.h \ pub_core_libcbase.h \ @@ -202,6 +210,12 @@ noinst_HEADERS = \ m_demangle/demangle.h \ m_demangle/safe-ctype.h \ m_demangle/vg_libciface.h \ + m_gdbserver/regcache.h \ + m_gdbserver/regdef.h \ + m_gdbserver/server.h \ + m_gdbserver/target.h \ + m_gdbserver/valgrind_low.h \ + m_gdbserver/gdb/signals.h \ m_initimg/priv_initimg_pathscan.h \ m_initimg/simple_huffman.c \ m_scheduler/priv_sema.h \ @@ -291,12 +305,29 @@ COREGRIND_SOURCES_COMMON = \ m_dispatch/dispatch-amd64-linux.S \ m_dispatch/dispatch-ppc32-linux.S \ m_dispatch/dispatch-ppc64-linux.S \ - m_dispatch/dispatch-arm-linux.S \ + m_dispatch/dispatch-arm-linux.S \ m_dispatch/dispatch-s390x-linux.S \ m_dispatch/dispatch-ppc32-aix5.S \ m_dispatch/dispatch-ppc64-aix5.S \ m_dispatch/dispatch-x86-darwin.S \ m_dispatch/dispatch-amd64-darwin.S \ + m_gdbserver/m_gdbserver.c \ + m_gdbserver/inferiors.c \ + m_gdbserver/m_gdbserver.c \ + m_gdbserver/regcache.c \ + m_gdbserver/remote-utils.c \ + m_gdbserver/server.c \ + m_gdbserver/signals.c \ + m_gdbserver/target.c \ + m_gdbserver/utils.c \ + m_gdbserver/valgrind-low.c \ + m_gdbserver/valgrind-low-x86.c \ + m_gdbserver/valgrind-low-amd64.c \ + m_gdbserver/valgrind-low-arm.c \ + m_gdbserver/valgrind-low-ppc32.c \ + m_gdbserver/valgrind-low-ppc64.c \ + m_gdbserver/valgrind-low-s390x.c \ + m_gdbserver/version.c \ m_initimg/initimg-linux.c \ m_initimg/initimg-aix5.c \ m_initimg/initimg-darwin.c \ @@ -312,7 +343,7 @@ COREGRIND_SOURCES_COMMON = \ m_sigframe/sigframe-amd64-linux.c \ m_sigframe/sigframe-ppc32-linux.c \ m_sigframe/sigframe-ppc64-linux.c \ - m_sigframe/sigframe-arm-linux.c \ + m_sigframe/sigframe-arm-linux.c \ m_sigframe/sigframe-s390x-linux.c \ m_sigframe/sigframe-ppc32-aix5.c \ m_sigframe/sigframe-ppc64-aix5.c \ @@ -322,7 +353,7 @@ COREGRIND_SOURCES_COMMON = \ m_syswrap/syscall-amd64-linux.S \ m_syswrap/syscall-ppc32-linux.S \ m_syswrap/syscall-ppc64-linux.S \ - m_syswrap/syscall-arm-linux.S \ + m_syswrap/syscall-arm-linux.S \ m_syswrap/syscall-s390x-linux.S \ m_syswrap/syscall-ppc32-aix5.S \ m_syswrap/syscall-ppc64-aix5.S \ @@ -338,7 +369,7 @@ COREGRIND_SOURCES_COMMON = \ m_syswrap/syswrap-amd64-linux.c \ m_syswrap/syswrap-ppc32-linux.c \ m_syswrap/syswrap-ppc64-linux.c \ - m_syswrap/syswrap-arm-linux.c \ + m_syswrap/syswrap-arm-linux.c \ m_syswrap/syswrap-s390x-linux.c \ m_syswrap/syswrap-ppc32-aix5.c \ m_syswrap/syswrap-ppc64-aix5.c \ @@ -434,10 +465,103 @@ vgpreload_core_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_LDFLAGS = \ endif #---------------------------------------------------------------------------- +# gdbserver xml target descriptions +#---------------------------------------------------------------------------- +pkglib_DATA = + +if VGCONF_ARCHS_INCLUDE_ARM +pkglib_DATA += m_gdbserver/arm-core-valgrind-s1.xml \ + m_gdbserver/arm-core-valgrind-s2.xml \ + m_gdbserver/arm-core.xml \ + m_gdbserver/arm-vfpv3-valgrind-s1.xml \ + m_gdbserver/arm-vfpv3-valgrind-s2.xml \ + m_gdbserver/arm-vfpv3.xml \ + m_gdbserver/arm-with-vfpv3-valgrind.xml \ + m_gdbserver/arm-with-vfpv3.xml +endif + +if VGCONF_ARCHS_INCLUDE_X86 +pkglib_DATA += m_gdbserver/32bit-core-valgrind-s1.xml \ + m_gdbserver/32bit-core-valgrind-s2.xml \ + m_gdbserver/32bit-core.xml \ + m_gdbserver/32bit-sse-valgrind-s1.xml \ + m_gdbserver/32bit-sse-valgrind-s2.xml \ + m_gdbserver/32bit-sse.xml +if VGCONF_OS_IS_LINUX +pkglib_DATA += m_gdbserver/32bit-linux-valgrind-s1.xml \ + m_gdbserver/32bit-linux-valgrind-s2.xml \ + m_gdbserver/32bit-linux.xml \ + m_gdbserver/i386-linux-valgrind.xml +endif +if VGCONF_OS_IS_DARWIN +pkglib_DATA += m_gdbserver/i386-coresse-valgrind.xml +endif +endif + +if VGCONF_ARCHS_INCLUDE_AMD64 +pkglib_DATA += m_gdbserver/64bit-core-valgrind-s1.xml \ + m_gdbserver/64bit-core-valgrind-s2.xml \ + m_gdbserver/64bit-core.xml \ + m_gdbserver/64bit-sse-valgrind-s1.xml \ + m_gdbserver/64bit-sse-valgrind-s2.xml \ + m_gdbserver/64bit-sse.xml + +if VGCONF_OS_IS_LINUX +pkglib_DATA += m_gdbserver/64bit-linux-valgrind-s1.xml \ + m_gdbserver/64bit-linux-valgrind-s2.xml \ + m_gdbserver/64bit-linux.xml \ + m_gdbserver/amd64-linux-valgrind.xml +endif +if VGCONF_OS_IS_DARWIN +pkglib_DATA += m_gdbserver/amd64-coresse-valgrind.xml +endif +endif + +if VGCONF_ARCHS_INCLUDE_PPC32 +pkglib_DATA += m_gdbserver/power-altivec-valgrind-s1.xml \ + m_gdbserver/power-altivec-valgrind-s2.xml \ + m_gdbserver/power-altivec.xml \ + m_gdbserver/power-core.xml \ + m_gdbserver/power-fpu-valgrind-s1.xml \ + m_gdbserver/power-fpu-valgrind-s2.xml \ + m_gdbserver/power-fpu.xml \ + m_gdbserver/power-linux-valgrind-s1.xml \ + m_gdbserver/power-linux-valgrind-s2.xml \ + m_gdbserver/power-linux.xml \ + m_gdbserver/powerpc-altivec32l-valgrind.xml \ + m_gdbserver/powerpc-altivec32l.xml +endif + +if VGCONF_ARCHS_INCLUDE_PPC64 +pkglib_DATA += m_gdbserver/power64-core-valgrind-s1.xml \ + m_gdbserver/power64-core-valgrind-s2.xml \ + m_gdbserver/power64-core.xml \ + m_gdbserver/power64-linux-valgrind-s1.xml \ + m_gdbserver/power64-linux-valgrind-s2.xml \ + m_gdbserver/power64-linux.xml \ + m_gdbserver/powerpc-altivec64l-valgrind.xml \ + m_gdbserver/powerpc-altivec64l.xml +if ! VGCONF_ARCHS_INCLUDE_PPC32 +pkglib_DATA += m_gdbserver/power-altivec-valgrind-s1.xml \ + m_gdbserver/power-altivec-valgrind-s2.xml \ + m_gdbserver/power-altivec.xml \ + m_gdbserver/power-fpu-valgrind-s1.xml \ + m_gdbserver/power-fpu-valgrind-s2.xml \ + m_gdbserver/power-fpu.xml +endif +endif + + +#---------------------------------------------------------------------------- # General stuff #---------------------------------------------------------------------------- all-local: inplace-noinst_PROGRAMS inplace-noinst_DSYMS + mkdir -p $(inplacedir); \ + for f in $(pkglib_DATA); do \ + rm -f $(inplacedir)/$$f; \ + ln -f -s ../$(subdir)/$$f $(inplacedir); \ + done clean-local: clean-noinst_DSYMS diff --git a/coregrind/m_aspacemgr/aspacemgr-linux.c b/coregrind/m_aspacemgr/aspacemgr-linux.c index cc092cec..9765ba8d 100644 --- a/coregrind/m_aspacemgr/aspacemgr-linux.c +++ b/coregrind/m_aspacemgr/aspacemgr-linux.c @@ -2488,11 +2488,11 @@ SysRes VG_(am_sbrk_anon_float_valgrind)( SizeT cszB ) /* Map a file at an unconstrained address for V, and update the - segment array accordingly. This is used by V for transiently - mapping in object files to read their debug info. */ + segment array accordingly. Use the provided flags */ -SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, - Int fd, Off64T offset ) +static SysRes VG_(am_mmap_file_float_valgrind_flags) ( SizeT length, UInt prot, + UInt flags, + Int fd, Off64T offset ) { SysRes sres; NSegment seg; @@ -2520,7 +2520,7 @@ SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, any resulting failure immediately. */ sres = VG_(am_do_mmap_NO_NOTIFY)( advised, length, prot, - VKI_MAP_FIXED|VKI_MAP_PRIVATE, + flags, fd, offset ); if (sr_isError(sres)) @@ -2556,7 +2556,25 @@ SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, AM_SANITY_CHECK; return sres; } +/* Map privately a file at an unconstrained address for V, and update the + segment array accordingly. This is used by V for transiently + mapping in object files to read their debug info. */ + +SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, + Int fd, Off64T offset ) +{ + return VG_(am_mmap_file_float_valgrind_flags) (length, prot, + VKI_MAP_FIXED|VKI_MAP_PRIVATE, + fd, offset ); +} +extern SysRes VG_(am_shared_mmap_file_float_valgrind) + ( SizeT length, UInt prot, Int fd, Off64T offset ) +{ + return VG_(am_mmap_file_float_valgrind_flags) (length, prot, + VKI_MAP_FIXED|VKI_MAP_SHARED, + fd, offset ); +} /* --- --- munmap helper --- --- */ diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c index bb3247a8..98e9d53a 100644 --- a/coregrind/m_errormgr.c +++ b/coregrind/m_errormgr.c @@ -36,6 +36,7 @@ #include "pub_core_debuginfo.h" #include "pub_core_errormgr.h" #include "pub_core_execontext.h" +#include "pub_core_gdbserver.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcfile.h" @@ -494,7 +495,18 @@ void do_actions_on_error(Error* err, Bool allow_db_attach) /* Should be assured by caller */ vg_assert( ! VG_(clo_xml) ); + /* if user wants to debug from a certain error nr, then wait for gdb/vgdb */ + if (VG_(clo_vgdb) != Vg_VgdbNo + && allow_db_attach + && VG_(dyn_vgdb_error) <= n_errs_found) { + VG_(umsg)("(action on error) vgdb me ... \n"); + VG_(gdbserver)( err->tid ); + VG_(umsg)("Continuing ...\n"); + } + /* Perhaps we want a debugger attach at this point? */ + /* GDBTD ??? maybe we should/could remove the below assuming the + gdbserver interface is better ??? */ if (allow_db_attach && VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) )) { @@ -540,13 +552,13 @@ void do_actions_on_error(Error* err, Bool allow_db_attach) attach (and detach), and optionally prints a suppression; both of these may require user input. */ -static void pp_Error ( Error* err, Bool allow_db_attach ) +static void pp_Error ( Error* err, Bool allow_db_attach, Bool xml ) { /* If this fails, you probably specified your tool's method dictionary incorrectly. */ vg_assert(VG_(needs).tool_errors); - if (VG_(clo_xml)) { + if (xml) { /* Note, allow_db_attach is ignored in here. */ @@ -718,7 +730,8 @@ void VG_(maybe_record_error) ( ThreadId tid, } /* Move p to the front of the list so that future searches - for it are faster. */ + for it are faster. It also allows to print the last + error (see VG_(show_last_error). */ if (p_prev != NULL) { vg_assert(p_prev->next == p); p_prev->next = p->next; @@ -780,7 +793,7 @@ void VG_(maybe_record_error) ( ThreadId tid, n_err_contexts++; n_errs_found++; /* Actually show the error; more complex than you might think. */ - pp_Error( p, /*allow_db_attach*/True ); + pp_Error( p, /*allow_db_attach*/True, VG_(clo_xml) ); /* update stats */ n_errs_shown++; } else { @@ -825,7 +838,7 @@ Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, Addr a, Char* s, if (print_error) { /* Actually show the error; more complex than you might think. */ - pp_Error(&err, allow_db_attach); + pp_Error(&err, allow_db_attach, VG_(clo_xml)); /* update stats */ n_errs_shown++; } @@ -881,20 +894,19 @@ static Bool show_used_suppressions ( void ) return any_supp; } - /* Show all the errors that occurred, and possibly also the suppressions used. */ -void VG_(show_all_errors) ( void ) +void VG_(show_all_errors) ( Int verbosity, Bool xml ) { Int i, n_min; Error *p, *p_min; Bool any_supp; - if (VG_(clo_verbosity) == 0) + if (verbosity == 0) return; /* If we're printing XML, just show the suppressions and stop. */ - if (VG_(clo_xml)) { + if (xml) { (void)show_used_suppressions(); return; } @@ -905,13 +917,15 @@ void VG_(show_all_errors) ( void ) n_errs_found, n_err_contexts, n_errs_suppressed, n_supp_contexts ); - if (VG_(clo_verbosity) <= 1) + if (verbosity <= 1) return; // We do the following only at -v or above, and only in non-XML // mode - /* Print the contexts in order of increasing error count. */ + /* Print the contexts in order of increasing error count. + Once an error is shown, we add a huge value to its count to filter it + out. After having shown all errors, we reset count to the original value. */ for (i = 0; i < n_err_contexts; i++) { n_min = (1 << 30) - 1; p_min = NULL; @@ -928,10 +942,10 @@ void VG_(show_all_errors) ( void ) VG_(umsg)("\n"); VG_(umsg)("%d errors in context %d of %d:\n", p_min->count, i+1, n_err_contexts); - pp_Error( p_min, False/*allow_db_attach*/ ); + pp_Error( p_min, False/*allow_db_attach*/, False /* xml */ ); // We're not printing XML -- we'd have exited above if so. - vg_assert(! VG_(clo_xml)); + vg_assert(! xml); if ((i+1 == VG_(clo_dump_error))) { StackTrace ips = VG_(get_ExeContext_StackTrace)(p_min->where); @@ -941,9 +955,16 @@ void VG_(show_all_errors) ( void ) /*allow redir?*/True); } - p_min->count = 1 << 30; + p_min->count = p_min->count + (1 << 30); } + /* reset the counts, otherwise a 2nd call does not show anything anymore */ + for (p = errors; p != NULL; p = p->next) { + if (p->count >= (1 << 30)) + p->count = p->count - (1 << 30); + } + + any_supp = show_used_suppressions(); if (any_supp) @@ -956,6 +977,16 @@ void VG_(show_all_errors) ( void ) n_supp_contexts ); } +void VG_(show_last_error) ( void ) +{ + if (n_err_contexts == 0) { + VG_(umsg)("No errors yet\n"); + return; + } + + pp_Error( errors, False/*allow_db_attach*/, False/*xml*/ ); +} + /* Show occurrence counts of all errors, in XML form. */ void VG_(show_error_counts_as_XML) ( void ) diff --git a/coregrind/m_gdbserver/32bit-core-valgrind-s1.xml b/coregrind/m_gdbserver/32bit-core-valgrind-s1.xml new file mode 100644 index 00000000..9a0582f5 --- /dev/null +++ b/coregrind/m_gdbserver/32bit-core-valgrind-s1.xml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.core.valgrind.s1"> + <flags id="i386_eflags" size="4"> + <field name="CF" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="PF" start="2" end="2"/> + <field name="AF" start="4" end="4"/> + <field name="ZF" start="6" end="6"/> + <field name="SF" start="7" end="7"/> + <field name="TF" start="8" end="8"/> + <field name="IF" start="9" end="9"/> + <field name="DF" start="10" end="10"/> + <field name="OF" start="11" end="11"/> + <field name="NT" start="14" end="14"/> + <field name="RF" start="16" end="16"/> + <field name="VM" start="17" end="17"/> + <field name="AC" start="18" end="18"/> + <field name="VIF" start="19" end="19"/> + <field name="VIP" start="20" end="20"/> + <field name="ID" start="21" end="21"/> + </flags> + + <reg name="eaxs1" bitsize="32" type="int32"/> + <reg name="ecxs1" bitsize="32" type="int32"/> + <reg name="edxs1" bitsize="32" type="int32"/> + <reg name="ebxs1" bitsize="32" type="int32"/> + <reg name="esps1" bitsize="32" type="data_ptr"/> + <reg name="ebps1" bitsize="32" type="data_ptr"/> + <reg name="esis1" bitsize="32" type="int32"/> + <reg name="edis1" bitsize="32" type="int32"/> + + <reg name="eips1" bitsize="32" type="code_ptr"/> + <reg name="eflagss1" bitsize="32" type="i386_eflags"/> + <reg name="css1" bitsize="32" type="int32"/> + <reg name="sss1" bitsize="32" type="int32"/> + <reg name="dss1" bitsize="32" type="int32"/> + <reg name="ess1" bitsize="32" type="int32"/> + <reg name="fss1" bitsize="32" type="int32"/> + <reg name="gss1" bitsize="32" type="int32"/> + + <reg name="st0s1" bitsize="80" type="i387_ext"/> + <reg name="st1s1" bitsize="80" type="i387_ext"/> + <reg name="st2s1" bitsize="80" type="i387_ext"/> + <reg name="st3s1" bitsize="80" type="i387_ext"/> + <reg name="st4s1" bitsize="80" type="i387_ext"/> + <reg name="st5s1" bitsize="80" type="i387_ext"/> + <reg name="st6s1" bitsize="80" type="i387_ext"/> + <reg name="st7s1" bitsize="80" type="i387_ext"/> + + <reg name="fctrls1" bitsize="32" type="int" group="float"/> + <reg name="fstats1" bitsize="32" type="int" group="float"/> + <reg name="ftags1" bitsize="32" type="int" group="float"/> + <reg name="fisegs1" bitsize="32" type="int" group="float"/> + <reg name="fioffs1" bitsize="32" type="int" group="float"/> + <reg name="fosegs1" bitsize="32" type="int" group="float"/> + <reg name="fooffs1" bitsize="32" type="int" group="float"/> + <reg name="fops1" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-core-valgrind-s2.xml b/coregrind/m_gdbserver/32bit-core-valgrind-s2.xml new file mode 100644 index 00000000..1b272c55 --- /dev/null +++ b/coregrind/m_gdbserver/32bit-core-valgrind-s2.xml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.core.valgrind.s2"> + <flags id="i386_eflags" size="4"> + <field name="CF" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="PF" start="2" end="2"/> + <field name="AF" start="4" end="4"/> + <field name="ZF" start="6" end="6"/> + <field name="SF" start="7" end="7"/> + <field name="TF" start="8" end="8"/> + <field name="IF" start="9" end="9"/> + <field name="DF" start="10" end="10"/> + <field name="OF" start="11" end="11"/> + <field name="NT" start="14" end="14"/> + <field name="RF" start="16" end="16"/> + <field name="VM" start="17" end="17"/> + <field name="AC" start="18" end="18"/> + <field name="VIF" start="19" end="19"/> + <field name="VIP" start="20" end="20"/> + <field name="ID" start="21" end="21"/> + </flags> + + <reg name="eaxs2" bitsize="32" type="int32"/> + <reg name="ecxs2" bitsize="32" type="int32"/> + <reg name="edxs2" bitsize="32" type="int32"/> + <reg name="ebxs2" bitsize="32" type="int32"/> + <reg name="esps2" bitsize="32" type="data_ptr"/> + <reg name="ebps2" bitsize="32" type="data_ptr"/> + <reg name="esis2" bitsize="32" type="int32"/> + <reg name="edis2" bitsize="32" type="int32"/> + + <reg name="eips2" bitsize="32" type="code_ptr"/> + <reg name="eflagss2" bitsize="32" type="i386_eflags"/> + <reg name="css2" bitsize="32" type="int32"/> + <reg name="sss2" bitsize="32" type="int32"/> + <reg name="dss2" bitsize="32" type="int32"/> + <reg name="ess2" bitsize="32" type="int32"/> + <reg name="fss2" bitsize="32" type="int32"/> + <reg name="gss2" bitsize="32" type="int32"/> + + <reg name="st0s2" bitsize="80" type="i387_ext"/> + <reg name="st1s2" bitsize="80" type="i387_ext"/> + <reg name="st2s2" bitsize="80" type="i387_ext"/> + <reg name="st3s2" bitsize="80" type="i387_ext"/> + <reg name="st4s2" bitsize="80" type="i387_ext"/> + <reg name="st5s2" bitsize="80" type="i387_ext"/> + <reg name="st6s2" bitsize="80" type="i387_ext"/> + <reg name="st7s2" bitsize="80" type="i387_ext"/> + + <reg name="fctrls2" bitsize="32" type="int" group="float"/> + <reg name="fstats2" bitsize="32" type="int" group="float"/> + <reg name="ftags2" bitsize="32" type="int" group="float"/> + <reg name="fisegs2" bitsize="32" type="int" group="float"/> + <reg name="fioffs2" bitsize="32" type="int" group="float"/> + <reg name="fosegs2" bitsize="32" type="int" group="float"/> + <reg name="fooffs2" bitsize="32" type="int" group="float"/> + <reg name="fops2" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-core.xml b/coregrind/m_gdbserver/32bit-core.xml new file mode 100644 index 00000000..4d0377eb --- /dev/null +++ b/coregrind/m_gdbserver/32bit-core.xml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.core"> + <flags id="i386_eflags" size="4"> + <field name="CF" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="PF" start="2" end="2"/> + <field name="AF" start="4" end="4"/> + <field name="ZF" start="6" end="6"/> + <field name="SF" start="7" end="7"/> + <field name="TF" start="8" end="8"/> + <field name="IF" start="9" end="9"/> + <field name="DF" start="10" end="10"/> + <field name="OF" start="11" end="11"/> + <field name="NT" start="14" end="14"/> + <field name="RF" start="16" end="16"/> + <field name="VM" start="17" end="17"/> + <field name="AC" start="18" end="18"/> + <field name="VIF" start="19" end="19"/> + <field name="VIP" start="20" end="20"/> + <field name="ID" start="21" end="21"/> + </flags> + + <reg name="eax" bitsize="32" type="int32"/> + <reg name="ecx" bitsize="32" type="int32"/> + <reg name="edx" bitsize="32" type="int32"/> + <reg name="ebx" bitsize="32" type="int32"/> + <reg name="esp" bitsize="32" type="data_ptr"/> + <reg name="ebp" bitsize="32" type="data_ptr"/> + <reg name="esi" bitsize="32" type="int32"/> + <reg name="edi" bitsize="32" type="int32"/> + + <reg name="eip" bitsize="32" type="code_ptr"/> + <reg name="eflags" bitsize="32" type="i386_eflags"/> + <reg name="cs" bitsize="32" type="int32"/> + <reg name="ss" bitsize="32" type="int32"/> + <reg name="ds" bitsize="32" type="int32"/> + <reg name="es" bitsize="32" type="int32"/> + <reg name="fs" bitsize="32" type="int32"/> + <reg name="gs" bitsize="32" type="int32"/> + + <reg name="st0" bitsize="80" type="i387_ext"/> + <reg name="st1" bitsize="80" type="i387_ext"/> + <reg name="st2" bitsize="80" type="i387_ext"/> + <reg name="st3" bitsize="80" type="i387_ext"/> + <reg name="st4" bitsize="80" type="i387_ext"/> + <reg name="st5" bitsize="80" type="i387_ext"/> + <reg name="st6" bitsize="80" type="i387_ext"/> + <reg name="st7" bitsize="80" type="i387_ext"/> + + <reg name="fctrl" bitsize="32" type="int" group="float"/> + <reg name="fstat" bitsize="32" type="int" group="float"/> + <reg name="ftag" bitsize="32" type="int" group="float"/> + <reg name="fiseg" bitsize="32" type="int" group="float"/> + <reg name="fioff" bitsize="32" type="int" group="float"/> + <reg name="foseg" bitsize="32" type="int" group="float"/> + <reg name="fooff" bitsize="32" type="int" group="float"/> + <reg name="fop" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-linux-valgrind-s1.xml b/coregrind/m_gdbserver/32bit-linux-valgrind-s1.xml new file mode 100644 index 00000000..fdf23f0c --- /dev/null +++ b/coregrind/m_gdbserver/32bit-linux-valgrind-s1.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.linux.valgrind.s1"> + <reg name="orig_eaxs1" bitsize="32" type="int" regnum="83"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-linux-valgrind-s2.xml b/coregrind/m_gdbserver/32bit-linux-valgrind-s2.xml new file mode 100644 index 00000000..137e3afb --- /dev/null +++ b/coregrind/m_gdbserver/32bit-linux-valgrind-s2.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.linux.valgrind.s2"> + <reg name="orig_eaxs2" bitsize="32" type="int" regnum="125"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-linux.xml b/coregrind/m_gdbserver/32bit-linux.xml new file mode 100644 index 00000000..975daf98 --- /dev/null +++ b/coregrind/m_gdbserver/32bit-linux.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.linux"> + <reg name="orig_eax" bitsize="32" type="int" regnum="41"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-sse-valgrind-s1.xml b/coregrind/m_gdbserver/32bit-sse-valgrind-s1.xml new file mode 100644 index 00000000..1a368c4e --- /dev/null +++ b/coregrind/m_gdbserver/32bit-sse-valgrind-s1.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.sse.valgrind.s1"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + <union id="vec128"> + <field name="v4_float" type="v4f"/> + <field name="v2_double" type="v2d"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + <flags id="i386_mxcsr" size="4"> + <field name="IE" start="0" end="0"/> + <field name="DE" start="1" end="1"/> + <field name="ZE" start="2" end="2"/> + <field name="OE" start="3" end="3"/> + <field name="UE" start="4" end="4"/> + <field name="PE" start="5" end="5"/> + <field name="DAZ" start="6" end="6"/> + <field name="IM" start="7" end="7"/> + <field name="DM" start="8" end="8"/> + <field name="ZM" start="9" end="9"/> + <field name="OM" start="10" end="10"/> + <field name="UM" start="11" end="11"/> + <field name="PM" start="12" end="12"/> + <field name="FZ" start="15" end="15"/> + </flags> + + <reg name="xmm0s1" bitsize="128" type="vec128"/> + <reg name="xmm1s1" bitsize="128" type="vec128"/> + <reg name="xmm2s1" bitsize="128" type="vec128"/> + <reg name="xmm3s1" bitsize="128" type="vec128"/> + <reg name="xmm4s1" bitsize="128" type="vec128"/> + <reg name="xmm5s1" bitsize="128" type="vec128"/> + <reg name="xmm6s1" bitsize="128" type="vec128"/> + <reg name="xmm7s1" bitsize="128" type="vec128"/> + + <reg name="mxcsrs1" bitsize="32" type="i386_mxcsr" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-sse-valgrind-s2.xml b/coregrind/m_gdbserver/32bit-sse-valgrind-s2.xml new file mode 100644 index 00000000..c69da70f --- /dev/null +++ b/coregrind/m_gdbserver/32bit-sse-valgrind-s2.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.sse.valgrind.s2"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + <union id="vec128"> + <field name="v4_float" type="v4f"/> + <field name="v2_double" type="v2d"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + <flags id="i386_mxcsr" size="4"> + <field name="IE" start="0" end="0"/> + <field name="DE" start="1" end="1"/> + <field name="ZE" start="2" end="2"/> + <field name="OE" start="3" end="3"/> + <field name="UE" start="4" end="4"/> + <field name="PE" start="5" end="5"/> + <field name="DAZ" start="6" end="6"/> + <field name="IM" start="7" end="7"/> + <field name="DM" start="8" end="8"/> + <field name="ZM" start="9" end="9"/> + <field name="OM" start="10" end="10"/> + <field name="UM" start="11" end="11"/> + <field name="PM" start="12" end="12"/> + <field name="FZ" start="15" end="15"/> + </flags> + + <reg name="xmm0s2" bitsize="128" type="vec128"/> + <reg name="xmm1s2" bitsize="128" type="vec128"/> + <reg name="xmm2s2" bitsize="128" type="vec128"/> + <reg name="xmm3s2" bitsize="128" type="vec128"/> + <reg name="xmm4s2" bitsize="128" type="vec128"/> + <reg name="xmm5s2" bitsize="128" type="vec128"/> + <reg name="xmm6s2" bitsize="128" type="vec128"/> + <reg name="xmm7s2" bitsize="128" type="vec128"/> + + <reg name="mxcsrs2" bitsize="32" type="i386_mxcsr" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/32bit-sse.xml b/coregrind/m_gdbserver/32bit-sse.xml new file mode 100644 index 00000000..cca94b37 --- /dev/null +++ b/coregrind/m_gdbserver/32bit-sse.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.sse"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + <union id="vec128"> + <field name="v4_float" type="v4f"/> + <field name="v2_double" type="v2d"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + <flags id="i386_mxcsr" size="4"> + <field name="IE" start="0" end="0"/> + <field name="DE" start="1" end="1"/> + <field name="ZE" start="2" end="2"/> + <field name="OE" start="3" end="3"/> + <field name="UE" start="4" end="4"/> + <field name="PE" start="5" end="5"/> + <field name="DAZ" start="6" end="6"/> + <field name="IM" start="7" end="7"/> + <field name="DM" start="8" end="8"/> + <field name="ZM" start="9" end="9"/> + <field name="OM" start="10" end="10"/> + <field name="UM" start="11" end="11"/> + <field name="PM" start="12" end="12"/> + <field name="FZ" start="15" end="15"/> + </flags> + + <reg name="xmm0" bitsize="128" type="vec128" regnum="32"/> + <reg name="xmm1" bitsize="128" type="vec128"/> + <reg name="xmm2" bitsize="128" type="vec128"/> + <reg name="xmm3" bitsize="128" type="vec128"/> + <reg name="xmm4" bitsize="128" type="vec128"/> + <reg name="xmm5" bitsize="128" type="vec128"/> + <reg name="xmm6" bitsize="128" type="vec128"/> + <reg name="xmm7" bitsize="128" type="vec128"/> + + <reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-core-valgrind-s1.xml b/coregrind/m_gdbserver/64bit-core-valgrind-s1.xml new file mode 100644 index 00000000..67b497f1 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-core-valgrind-s1.xml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.core.valgrind.s1"> + <flags id="i386_eflags" size="4"> + <field name="CF" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="PF" start="2" end="2"/> + <field name="AF" start="4" end="4"/> + <field name="ZF" start="6" end="6"/> + <field name="SF" start="7" end="7"/> + <field name="TF" start="8" end="8"/> + <field name="IF" start="9" end="9"/> + <field name="DF" start="10" end="10"/> + <field name="OF" start="11" end="11"/> + <field name="NT" start="14" end="14"/> + <field name="RF" start="16" end="16"/> + <field name="VM" start="17" end="17"/> + <field name="AC" start="18" end="18"/> + <field name="VIF" start="19" end="19"/> + <field name="VIP" start="20" end="20"/> + <field name="ID" start="21" end="21"/> + </flags> + + <reg name="raxs1" bitsize="64" type="int64"/> + <reg name="rbxs1" bitsize="64" type="int64"/> + <reg name="rcxs1" bitsize="64" type="int64"/> + <reg name="rdxs1" bitsize="64" type="int64"/> + <reg name="rsis1" bitsize="64" type="int64"/> + <reg name="rdis1" bitsize="64" type="int64"/> + <reg name="rbps1" bitsize="64" type="data_ptr"/> + <reg name="rsps1" bitsize="64" type="data_ptr"/> + <reg name="r8s1" bitsize="64" type="int64"/> + <reg name="r9s1" bitsize="64" type="int64"/> + <reg name="r10s1" bitsize="64" type="int64"/> + <reg name="r11s1" bitsize="64" type="int64"/> + <reg name="r12s1" bitsize="64" type="int64"/> + <reg name="r13s1" bitsize="64" type="int64"/> + <reg name="r14s1" bitsize="64" type="int64"/> + <reg name="r15s1" bitsize="64" type="int64"/> + + <reg name="rips1" bitsize="64" type="code_ptr"/> + <reg name="eflagss1" bitsize="32" type="i386_eflags"/> + <reg name="css1" bitsize="32" type="int32"/> + <reg name="sss1" bitsize="32" type="int32"/> + <reg name="dss1" bitsize="32" type="int32"/> + <reg name="ess1" bitsize="32" type="int32"/> + <reg name="fss1" bitsize="32" type="int32"/> + <reg name="gss1" bitsize="32" type="int32"/> + + <reg name="st0s1" bitsize="80" type="i387_ext"/> + <reg name="st1s1" bitsize="80" type="i387_ext"/> + <reg name="st2s1" bitsize="80" type="i387_ext"/> + <reg name="st3s1" bitsize="80" type="i387_ext"/> + <reg name="st4s1" bitsize="80" type="i387_ext"/> + <reg name="st5s1" bitsize="80" type="i387_ext"/> + <reg name="st6s1" bitsize="80" type="i387_ext"/> + <reg name="st7s1" bitsize="80" type="i387_ext"/> + + <reg name="fctrls1" bitsize="32" type="int" group="float"/> + <reg name="fstats1" bitsize="32" type="int" group="float"/> + <reg name="ftags1" bitsize="32" type="int" group="float"/> + <reg name="fisegs1" bitsize="32" type="int" group="float"/> + <reg name="fioffs1" bitsize="32" type="int" group="float"/> + <reg name="fosegs1" bitsize="32" type="int" group="float"/> + <reg name="fooffs1" bitsize="32" type="int" group="float"/> + <reg name="fops1" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-core-valgrind-s2.xml b/coregrind/m_gdbserver/64bit-core-valgrind-s2.xml new file mode 100644 index 00000000..14f27260 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-core-valgrind-s2.xml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.core.valgrind.s2"> + <flags id="i386_eflags" size="4"> + <field name="CF" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="PF" start="2" end="2"/> + <field name="AF" start="4" end="4"/> + <field name="ZF" start="6" end="6"/> + <field name="SF" start="7" end="7"/> + <field name="TF" start="8" end="8"/> + <field name="IF" start="9" end="9"/> + <field name="DF" start="10" end="10"/> + <field name="OF" start="11" end="11"/> + <field name="NT" start="14" end="14"/> + <field name="RF" start="16" end="16"/> + <field name="VM" start="17" end="17"/> + <field name="AC" start="18" end="18"/> + <field name="VIF" start="19" end="19"/> + <field name="VIP" start="20" end="20"/> + <field name="ID" start="21" end="21"/> + </flags> + + <reg name="raxs2" bitsize="64" type="int64"/> + <reg name="rbxs2" bitsize="64" type="int64"/> + <reg name="rcxs2" bitsize="64" type="int64"/> + <reg name="rdxs2" bitsize="64" type="int64"/> + <reg name="rsis2" bitsize="64" type="int64"/> + <reg name="rdis2" bitsize="64" type="int64"/> + <reg name="rbps2" bitsize="64" type="data_ptr"/> + <reg name="rsps2" bitsize="64" type="data_ptr"/> + <reg name="r8s2" bitsize="64" type="int64"/> + <reg name="r9s2" bitsize="64" type="int64"/> + <reg name="r10s2" bitsize="64" type="int64"/> + <reg name="r11s2" bitsize="64" type="int64"/> + <reg name="r12s2" bitsize="64" type="int64"/> + <reg name="r13s2" bitsize="64" type="int64"/> + <reg name="r14s2" bitsize="64" type="int64"/> + <reg name="r15s2" bitsize="64" type="int64"/> + + <reg name="rips2" bitsize="64" type="code_ptr"/> + <reg name="eflagss2" bitsize="32" type="i386_eflags"/> + <reg name="css2" bitsize="32" type="int32"/> + <reg name="sss2" bitsize="32" type="int32"/> + <reg name="dss2" bitsize="32" type="int32"/> + <reg name="ess2" bitsize="32" type="int32"/> + <reg name="fss2" bitsize="32" type="int32"/> + <reg name="gss2" bitsize="32" type="int32"/> + + <reg name="st0s2" bitsize="80" type="i387_ext"/> + <reg name="st1s2" bitsize="80" type="i387_ext"/> + <reg name="st2s2" bitsize="80" type="i387_ext"/> + <reg name="st3s2" bitsize="80" type="i387_ext"/> + <reg name="st4s2" bitsize="80" type="i387_ext"/> + <reg name="st5s2" bitsize="80" type="i387_ext"/> + <reg name="st6s2" bitsize="80" type="i387_ext"/> + <reg name="st7s2" bitsize="80" type="i387_ext"/> + + <reg name="fctrls2" bitsize="32" type="int" group="float"/> + <reg name="fstats2" bitsize="32" type="int" group="float"/> + <reg name="ftags2" bitsize="32" type="int" group="float"/> + <reg name="fisegs2" bitsize="32" type="int" group="float"/> + <reg name="fioffs2" bitsize="32" type="int" group="float"/> + <reg name="fosegs2" bitsize="32" type="int" group="float"/> + <reg name="fooffs2" bitsize="32" type="int" group="float"/> + <reg name="fops2" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-core.xml b/coregrind/m_gdbserver/64bit-core.xml new file mode 100644 index 00000000..8cfe3fe0 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-core.xml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.core"> + <flags id="i386_eflags" size="4"> + <field name="CF" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="PF" start="2" end="2"/> + <field name="AF" start="4" end="4"/> + <field name="ZF" start="6" end="6"/> + <field name="SF" start="7" end="7"/> + <field name="TF" start="8" end="8"/> + <field name="IF" start="9" end="9"/> + <field name="DF" start="10" end="10"/> + <field name="OF" start="11" end="11"/> + <field name="NT" start="14" end="14"/> + <field name="RF" start="16" end="16"/> + <field name="VM" start="17" end="17"/> + <field name="AC" start="18" end="18"/> + <field name="VIF" start="19" end="19"/> + <field name="VIP" start="20" end="20"/> + <field name="ID" start="21" end="21"/> + </flags> + + <reg name="rax" bitsize="64" type="int64"/> + <reg name="rbx" bitsize="64" type="int64"/> + <reg name="rcx" bitsize="64" type="int64"/> + <reg name="rdx" bitsize="64" type="int64"/> + <reg name="rsi" bitsize="64" type="int64"/> + <reg name="rdi" bitsize="64" type="int64"/> + <reg name="rbp" bitsize="64" type="data_ptr"/> + <reg name="rsp" bitsize="64" type="data_ptr"/> + <reg name="r8" bitsize="64" type="int64"/> + <reg name="r9" bitsize="64" type="int64"/> + <reg name="r10" bitsize="64" type="int64"/> + <reg name="r11" bitsize="64" type="int64"/> + <reg name="r12" bitsize="64" type="int64"/> + <reg name="r13" bitsize="64" type="int64"/> + <reg name="r14" bitsize="64" type="int64"/> + <reg name="r15" bitsize="64" type="int64"/> + + <reg name="rip" bitsize="64" type="code_ptr"/> + <reg name="eflags" bitsize="32" type="i386_eflags"/> + <reg name="cs" bitsize="32" type="int32"/> + <reg name="ss" bitsize="32" type="int32"/> + <reg name="ds" bitsize="32" type="int32"/> + <reg name="es" bitsize="32" type="int32"/> + <reg name="fs" bitsize="32" type="int32"/> + <reg name="gs" bitsize="32" type="int32"/> + + <reg name="st0" bitsize="80" type="i387_ext"/> + <reg name="st1" bitsize="80" type="i387_ext"/> + <reg name="st2" bitsize="80" type="i387_ext"/> + <reg name="st3" bitsize="80" type="i387_ext"/> + <reg name="st4" bitsize="80" type="i387_ext"/> + <reg name="st5" bitsize="80" type="i387_ext"/> + <reg name="st6" bitsize="80" type="i387_ext"/> + <reg name="st7" bitsize="80" type="i387_ext"/> + + <reg name="fctrl" bitsize="32" type="int" group="float"/> + <reg name="fstat" bitsize="32" type="int" group="float"/> + <reg name="ftag" bitsize="32" type="int" group="float"/> + <reg name="fiseg" bitsize="32" type="int" group="float"/> + <reg name="fioff" bitsize="32" type="int" group="float"/> + <reg name="foseg" bitsize="32" type="int" group="float"/> + <reg name="fooff" bitsize="32" type="int" group="float"/> + <reg name="fop" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-linux-valgrind-s1.xml b/coregrind/m_gdbserver/64bit-linux-valgrind-s1.xml new file mode 100644 index 00000000..fc1c2dd0 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-linux-valgrind-s1.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.linux.valgrind.s1"> + <reg name="orig_raxs1" bitsize="64" type="int" regnum="115"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-linux-valgrind-s2.xml b/coregrind/m_gdbserver/64bit-linux-valgrind-s2.xml new file mode 100644 index 00000000..452ddec5 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-linux-valgrind-s2.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.linux.valgrind.s2"> + <reg name="orig_raxs2" bitsize="64" type="int" regnum="173"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-linux.xml b/coregrind/m_gdbserver/64bit-linux.xml new file mode 100644 index 00000000..86092721 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-linux.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.linux"> + <reg name="orig_rax" bitsize="64" type="int" regnum="57"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-sse-valgrind-s1.xml b/coregrind/m_gdbserver/64bit-sse-valgrind-s1.xml new file mode 100644 index 00000000..9db6c746 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-sse-valgrind-s1.xml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.sse.valgrind.s1"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + <union id="vec128"> + <field name="v4_float" type="v4f"/> + <field name="v2_double" type="v2d"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + <flags id="i386_mxcsr" size="4"> + <field name="IE" start="0" end="0"/> + <field name="DE" start="1" end="1"/> + <field name="ZE" start="2" end="2"/> + <field name="OE" start="3" end="3"/> + <field name="UE" start="4" end="4"/> + <field name="PE" start="5" end="5"/> + <field name="DAZ" start="6" end="6"/> + <field name="IM" start="7" end="7"/> + <field name="DM" start="8" end="8"/> + <field name="ZM" start="9" end="9"/> + <field name="OM" start="10" end="10"/> + <field name="UM" start="11" end="11"/> + <field name="PM" start="12" end="12"/> + <field name="FZ" start="15" end="15"/> + </flags> + + <reg name="xmm0s1" bitsize="128" type="vec128"/> + <reg name="xmm1s1" bitsize="128" type="vec128"/> + <reg name="xmm2s1" bitsize="128" type="vec128"/> + <reg name="xmm3s1" bitsize="128" type="vec128"/> + <reg name="xmm4s1" bitsize="128" type="vec128"/> + <reg name="xmm5s1" bitsize="128" type="vec128"/> + <reg name="xmm6s1" bitsize="128" type="vec128"/> + <reg name="xmm7s1" bitsize="128" type="vec128"/> + <reg name="xmm8s1" bitsize="128" type="vec128"/> + <reg name="xmm9s1" bitsize="128" type="vec128"/> + <reg name="xmm10s1" bitsize="128" type="vec128"/> + <reg name="xmm11s1" bitsize="128" type="vec128"/> + <reg name="xmm12s1" bitsize="128" type="vec128"/> + <reg name="xmm13s1" bitsize="128" type="vec128"/> + <reg name="xmm14s1" bitsize="128" type="vec128"/> + <reg name="xmm15s1" bitsize="128" type="vec128"/> + + <reg name="mxcsrs1" bitsize="32" type="i386_mxcsr" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-sse-valgrind-s2.xml b/coregrind/m_gdbserver/64bit-sse-valgrind-s2.xml new file mode 100644 index 00000000..189910e8 --- /dev/null +++ b/coregrind/m_gdbserver/64bit-sse-valgrind-s2.xml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.sse.valgrind.s2"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + <union id="vec128"> + <field name="v4_float" type="v4f"/> + <field name="v2_double" type="v2d"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + <flags id="i386_mxcsr" size="4"> + <field name="IE" start="0" end="0"/> + <field name="DE" start="1" end="1"/> + <field name="ZE" start="2" end="2"/> + <field name="OE" start="3" end="3"/> + <field name="UE" start="4" end="4"/> + <field name="PE" start="5" end="5"/> + <field name="DAZ" start="6" end="6"/> + <field name="IM" start="7" end="7"/> + <field name="DM" start="8" end="8"/> + <field name="ZM" start="9" end="9"/> + <field name="OM" start="10" end="10"/> + <field name="UM" start="11" end="11"/> + <field name="PM" start="12" end="12"/> + <field name="FZ" start="15" end="15"/> + </flags> + + <reg name="xmm0s2" bitsize="128" type="vec128"/> + <reg name="xmm1s2" bitsize="128" type="vec128"/> + <reg name="xmm2s2" bitsize="128" type="vec128"/> + <reg name="xmm3s2" bitsize="128" type="vec128"/> + <reg name="xmm4s2" bitsize="128" type="vec128"/> + <reg name="xmm5s2" bitsize="128" type="vec128"/> + <reg name="xmm6s2" bitsize="128" type="vec128"/> + <reg name="xmm7s2" bitsize="128" type="vec128"/> + <reg name="xmm8s2" bitsize="128" type="vec128"/> + <reg name="xmm9s2" bitsize="128" type="vec128"/> + <reg name="xmm10s2" bitsize="128" type="vec128"/> + <reg name="xmm11s2" bitsize="128" type="vec128"/> + <reg name="xmm12s2" bitsize="128" type="vec128"/> + <reg name="xmm13s2" bitsize="128" type="vec128"/> + <reg name="xmm14s2" bitsize="128" type="vec128"/> + <reg name="xmm15s2" bitsize="128" type="vec128"/> + + <reg name="mxcsrs2" bitsize="32" type="i386_mxcsr" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/64bit-sse.xml b/coregrind/m_gdbserver/64bit-sse.xml new file mode 100644 index 00000000..d7f7925d --- /dev/null +++ b/coregrind/m_gdbserver/64bit-sse.xml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.i386.sse"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + <union id="vec128"> + <field name="v4_float" type="v4f"/> + <field name="v2_double" type="v2d"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + <flags id="i386_mxcsr" size="4"> + <field name="IE" start="0" end="0"/> + <field name="DE" start="1" end="1"/> + <field name="ZE" start="2" end="2"/> + <field name="OE" start="3" end="3"/> + <field name="UE" start="4" end="4"/> + <field name="PE" start="5" end="5"/> + <field name="DAZ" start="6" end="6"/> + <field name="IM" start="7" end="7"/> + <field name="DM" start="8" end="8"/> + <field name="ZM" start="9" end="9"/> + <field name="OM" start="10" end="10"/> + <field name="UM" start="11" end="11"/> + <field name="PM" start="12" end="12"/> + <field name="FZ" start="15" end="15"/> + </flags> + + <reg name="xmm0" bitsize="128" type="vec128" regnum="40"/> + <reg name="xmm1" bitsize="128" type="vec128"/> + <reg name="xmm2" bitsize="128" type="vec128"/> + <reg name="xmm3" bitsize="128" type="vec128"/> + <reg name="xmm4" bitsize="128" type="vec128"/> + <reg name="xmm5" bitsize="128" type="vec128"/> + <reg name="xmm6" bitsize="128" type="vec128"/> + <reg name="xmm7" bitsize="128" type="vec128"/> + <reg name="xmm8" bitsize="128" type="vec128"/> + <reg name="xmm9" bitsize="128" type="vec128"/> + <reg name="xmm10" bitsize="128" type="vec128"/> + <reg name="xmm11" bitsize="128" type="vec128"/> + <reg name="xmm12" bitsize="128" type="vec128"/> + <reg name="xmm13" bitsize="128" type="vec128"/> + <reg name="xmm14" bitsize="128" type="vec128"/> + <reg name="xmm15" bitsize="128" type="vec128"/> + + <reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/README_DEVELOPERS b/coregrind/m_gdbserver/README_DEVELOPERS new file mode 100644 index 00000000..23340787 --- /dev/null +++ b/coregrind/m_gdbserver/README_DEVELOPERS @@ -0,0 +1,433 @@ +This file contains various notes/ideas/history/... related +to gdbserver in valgrind. + +How to use Valgrind gdbserver ? +------------------------------- +This is described in the Valgrind user manual. +Before reading the below, you better read the user manual first. + +What is gdbserver ? +------------------- +gdb debugger typically is used to debug a process running +on the same machine : gdb uses system calls (such as ptrace) +to fetch data from the process being debugged +or to change data in the process +or interrupt the process +or ... + +gdb can also debug processes running in a different computer +(e.g. it can debug a process running on a small real time +board). + +gdb does this by sending some commands (e.g. using tcp/ip) to a piece +of code running on the remote computer. This piece of code (called a +gdb stub in small boards, or gdbserver when the remote computer runs +an OS such as GNU/linux) will provide a set of commands allowing gdb +to remotely debug the process. Examples of commands are: "get the +registers", "get the list of running threads", "read xxx bytes at +address yyyyyyyy", etc. The definition of all these commands and the +associated replies is the gdb remote serial protocol, which is +documented in Appendix D of gdb user manual. + +The standard gdb distribution has a standalone gdbserver (a small +executable) which implements this protocol and the needed system calls +to allow gdb to remotely debug process running on a linux or MacOS or +... + +Activation of gdbserver code inside valgrind +-------------------------------------------- +The gdbserver code (from gdb 6.6, GPL2+) has been modified so as to +link it with valgrind and allow the valgrind guest process to be +debugged by a gdb speaking to this gdbserver embedded in valgrind. +The ptrace system calls inside gdbserver have been replaced by reading +the state of the guest. + +The gdbserver functionality is activated with valgrind command line +options. If gdbserver is not enabled, then the impact on valgrind +runtime is minimal: basically it just checks at startup the command +line option to see that there is nothing to do for what concerns gdb +server: there is a "if gdbserver is active" check in the translate +function of translate.c and an "if" in the valgrind scheduler. +If the valgrind gdbserver is activated (--vgdb=yes), the impact +is minimal (from time to time, the valgrind scheduler checks a counter +in memory). Option --vgdb-poll=yyyyy controls how often the scheduler +will do a (somewhat) more heavy check to see if gdbserver needs to +stop execution of the guest to allow debugging. +If valgrind gdbserver is activated with --vgdb=full, then +each instruction is instrumented with an additional call to a dirty +helper. + +How does gdbserver code interacts with valgrind ? +------------------------------------------------- +When an error is reported, the gdbserver code is called. It reads +commands from gdb using read system call on a FIFO (e.g. a command +such as "get the registers"). It executes the command (e.g. fetches +the registers from the guest state) and writes the reply (e.g. a +packet containing the register data). When gdb instructs gdbserver to +"continue", the control is returned to valgrind, which then continues +to execute guest code. The FIFOs used to communication between +valgrind and gdb are created at startup if gdbserver is activated +according to the --vgdb=no/yes/full command line option. + +How are signals "handled" ? +--------------------------- +When a signal is to be given to the guest, valgrind core first calls +gdbserver (if a gdb is currently connected to valgrind, otherwise the +signal is delivered immediately). If gdb instructs to give the signal +to the process, the signal is delivered to the guest. Otherwise, the +signal is ignored (not given to the guest). The user can +with gdb further decide to pass (or not pass) the signal. +Note that some (fatal) signals cannot be ignored. + +How are "break/step/stepi/next/..." implemented ? +------------------------------------------------- +When a break is put by gdb on an instruction, a command is sent to the +gdbserver in valgrind. This causes the basic block of this instruction +to be discarded and then re-instrumented so as to insert calls to a +dirty helper which calls the gdb server code. When a block is +instrumented for gdbserver, all the "jump targets" of this block are +invalidated, so as to allow step/stepi/next to properly work: these +blocks will themselves automatically be re-instrumented for gdbserver +if they are jumped to. +The valgrind gdbserver remembers which blocks have been instrumented +due to this "lazy 'jump targets' debugging instrumentation" so as to +discard these "debugging translation" when gdb instructs to continue +the execution normally. +The blocks in which an explicit break has been put by the user +are kept instrumented for gdbserver. +(but note that by default, gdb removes all breaks when the +process is stopped, and re-inserts all breaks when the process +is continued). This behaviour can be changed using the gdb +command 'set breakpoint always-inserted'. + +How are watchpoints implemented ? +--------------------------------- +Watchpoints implies support from the tool to detect that +a location is read and/or written. Currently, only memcheck +supports this : when a watchpoint is placed, memcheck changes +the addressability bits of the watched memory zone to be unacessible. +Before an access, memcheck then detects an error, but sees this error +is due to a watchpoint and gives the control back to gdb. +Stopping on the exact instruction for a write watchpoint implies +to use --vgdb=full. This is because the error is detected by memcheck +before modifying the value. gdb checks that the value has not changed +and so "does not believe" the information that the write watchpoint +was triggered, and continues the execution. At the next watchpoint +occurence, gdb sees the value has changed. But the watchpoints are all +reported "off by one". To avoid this, Valgrind gdbserver must +terminate the current instruction before reporting the write watchpoint. +Terminating precisely the current instruction implies to have +instrumented all the instructions of the block for gdbserver even +if there is no break in this block. This is ensured by --vgdb=full. +See m_gdbserver.c Bool VG_(is_watched) where watchpoint handling +is implemented. + +How is the Valgrind gdbserver receiving commands/packets from gdb ? +------------------------------------------------------------------- +The embedded gdbserver reads gdb commands on a named pipe having +(by default) the name /tmp/vgdb-pipe-from-vgdb-to-%d +where %d will be replaced by the pid. +The embedded gdbserver will reply to gdb commands on a named pipe +/tmp/vgdb-pipe-to-vgdb-from-%d + +gdb does not speak directly with gdbserver in valgrind: a relay application +called vgdb is needed between gdb and the valgrind-ified process. +gdb writes commands on the stdin of vgdb. vgdb reads these +commands and writes them on FIFO /tmp/vgdb-pipe-from-vgdb-to-%d. +vgdb reads replies on FIFO /tmp/vgdb-pipe-to-vgdb-from-%d and writes +them on its stdout. + +Note: The solution of named pipes was preferred to tcp ip connections as +it allows a discovery of which valgrind-ified processes are ready to accept +command by looking at files starting with the /tmp/vgdb-pipe- prefix +(changeable by a command line option). +Also, the usual unix protections are protecting +the valgrind process against other users sending commands. +The relay process also takes into account the wake up of the valgrind +process in case all threads are blocked in a system call. +The relay process can also be used in a shell to send commands +without a gdb (this allows to have a standard mechanism to control +valgrind tools from the command line, rather than specialized mechanism +e.g. in callgrind). + +How is gdbserver activated if all Valgrind threads are blocked in a syscall ? +----------------------------------------------------------------------------- +vgdb relays characters from gdb to valgrind. The scheduler will from +time to time check if gdbserver has to handle incoming characters. +(the check is efficient i.e. most of the time consists in checking +a counter in (shared) memory). + +However, it might be that all the threads in the valgrind process are +blocked in a system call. In such a case, no polling will be done by +the valgrind scheduler (as no activity takes place). By default, vgdb +will check after 100ms if the characters it has written have been read +by valgrind. If not, vgdb will force the invocation of the gdbserver +code inside the valgrind process. + +This forced invocation is implemented using the ptrace system call: +using ptrace, vgdb will cause the valgrind process to call the +gdbserver code. + +This wake up is *not* done using signals as this would imply to +implement a syscall restart logic in valgrind for all system +calls. When using ptrace as above, the linux kernel is responsible to +restart the system call. + +This wakeup is also *not* implemented by having a "system thread" +started by valgrind as this would transform all non-threaded programs +in threaded programs when running under valgrind. Also, such a 'system +thread' for gdbserver was tried by Greg Parker in the early MacOS +port, and was unreliable. + +So, the ptrace based solution was chosen instead. + +There used to be some bugs in the kernel when using ptrace on +a process blocked in a system call : the symptom is that the system +call fails with an unknown errno 512. This typically happens +with a vgdb in 64bits ptrace-ing a 32 bits process. +A bypass for old kernels has been integrated in vgdb.c (sign extend +register rax). + +At least on a fedora core 12 (kernel 2.6.32), syscall restart of read +and select are working ok and red-hat 5.3 (an old kernel), everything +works properly. + +Need to investigate if darwin and/or AIX can similarly do syscall +restart with ptrace. + +The vgdb argument --max-invoke-ms=xxx allows to control the nr of +milli-seconds after which vgdb will force the invocation of gdbserver +code. If xxx is 0, this disables the forced invocation. +Also, disabling this ptrace mechanism is necessary in case you are +debugging the valgrind code at the same time as debugging the guest +process using gdbserver. + +Do not kill -9 vgdb while it has interrupted the valgrind process, +otherwise the valgrind process will very probably stay stopped or die. + + +Implementation is based on the gdbserver code from gdb 6.6 +---------------------------------------------------------- +The gdbserver implementation is derived from the gdbserver included +in the gdb distribution. +The files originating from gdb are : inferiors.c, regcache.[ch], +regdef.h, remote-utils.c, server.[ch], signals.c, target.[ch], utils.c, +version.c. +valgrind-low-* are inspired from gdb files. + +This code had to be changed to integrate properly within valgrind +(e.g. no libc usage). Some of these changes have been ensured by +using the preprocessor to replace calls by valgrind equivalent, +e.g. #define memcpy(...) VG_(memcpy) (...). + +Some "control flow" changes are due to the fact that gdbserver inside +valgrind must return the control to valgrind when the 'debugged' +process has to run, while in a classical gdbserver usage, the +gdbserver process waits for a debugged process to stop on a break or +similar. This has implied to have some variables to remember the +state of gdbserver before returning to valgrind (search for +resume_packet_needed in server.c) and "goto" the place where gdbserver +expects a stopped process to return control to gdbserver. + +How does a tool need to be changed to be "debuggable" ? +------------------------------------------------------- +There is no need to modify a tool to have it "debuggable" via +gdbserver : e.g. reports of errors, break etc will work "out of the +box". If an interactive usage of tool client requests or similar is +desired for a tool, then simple code can be written for that via a +specific client request VG_USERREQ__GDB_MONITOR_COMMAND code. The tool +function "handle_client_request" must then parse the string received +in argument and call the expected valgrind or tool code. See +e.g. massif ms_handle_client_request as an example. + + +Automatic regression tests: +--------------------------- +Automatic Valgrind gdbserver tests are in the directory +$(top_srcdir)/gdbserver_tests. +Read $(top_srcdir)/gdbserver_tests/README_DEVELOPPERS for more +info about testing. + +How to integrate support for a new architecture xxx? +---------------------------------------------------- +Let's imagine a new architecture hal9000 has to be supported. + +Mandatory: +The main thing to do is to make a file valgrind-low-hal9000.c. +Start from an existing file (e.g. valgrind-low-x86.c). +The data structures 'struct reg regs' +and 'const char *expedite_regs' are build from files +in the gdb sources, e.g. for an new arch hal9000 + cd gdb/regformats + ./regdat.sh reg-hal9000.dat hal9000 + +From the generated file hal9000, you copy/paste in +valgrind-low-hal9000.c the two needed data structures and change their +name to 'regs' and 'expedite_regs' + +Then adapt the set of functions needed to initialize the structure +'static struct valgrind_target_ops low_target'. + +Optional but heavily recommended: +To have a proper wake up of a Valgrind process with all threads +blocked in a system call, some architecture specific code +has to be done in vgdb.c : search for PTRACEINVOKER processor symbol +to see what has to be completed. + +For Linux based platforms, all the ptrace calls should be ok. +The only thing needed is the code needed to "push a dummy call" on the stack, +i.e. assign the relevant registers in the struct user_regs_struct, and push +values on the stack according to the ABI. + +For other platforms (i.e. Macos), more work is needed as the ptrace calls +on Macos are either different and/or incomplete (and so, 'Mach' specific +things are needed e.g. to attach to threads etc). +A courageous Mac aficionado is welcome on this aspect. + +Optional: +To let gdb see the Valgrind shadow registers, xml description +files have to be provided + valgrind-low-hal9000.c has +to give the top xml file. +Start from the xml files found in the gdb distribution directory +gdb/features. You need to duplicate and modify these files to provide +shadow1 and shadow2 register sets description. + +Modify coregrind/Makefile.am: + add valgrind-low-hal9000.c + If you have target xml description, also add them in pkglib_DATA + + +A not handled comment given by Julian at FOSDEM. +------------------------------------------------ +* the check for vgdb-poll in scheduler.c could/should be moved to another place: + instead of having it in run_thread_for_a_while + the vgdb poll check could be in VG_(scheduler). + (not clear to me why one is better than the other ???) + +TODO and/or additional nice things to have +------------------------------------------ +* many options can be changed on-line without problems. + => would be nice to have a vg.option command that would evaluate + its arguments like the startup options of m_main.c and tool clo processing. + +* have a mc.who_points_at <address> | <loss_record_nr> + that would describe the addresses where a pointer is found + to address (or address leaked at loss_record_nr>) + This would allow to interactively searching who is "keeping" a piece + of memory. + +* some GDBTD in the code + +(GDBTD = GDB To Do = something still to look at and/or a question) + +* All architectures and platforms are done. + But there are still some "GDBTD" to convert between gdb registers + and VEX registers : + e.g. some registers in x86 or amd64 that I could not + translate to VEX registers. Someone with a good knowledge + of these architectures might complete this + (see the GDBTD in valgrind-low-*.c) + +* "hardware" watchpoint (read/write/access watchpoints) are implemented + but can't persuade gdb to insert a hw watchpoint of what valgrind + supports (i.e. of whatever length). + The reason why gdb does not accept a hardware watch of let's say + 10 bytes is: +default_region_ok_for_hw_watchpoint (addr=134520360, len=10) at target.c:2738 +2738 return (len <= gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT); +#0 default_region_ok_for_hw_watchpoint (addr=134520360, len=10) + at target.c:2738 +2738 return (len <= gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT); +#1 0x08132e65 in can_use_hardware_watchpoint (v=0x85a8ef0) + at breakpoint.c:8300 +8300 if (!target_region_ok_for_hw_watchpoint (vaddr, len)) +#2 0x0813bd17 in watch_command_1 (arg=0x84169f0 "", accessflag=2, + from_tty=<value optimized out>) at breakpoint.c:8140 + A small patch in gdb remote.c allowed to control the remote target watchpoint + length limit. This patch is to be submitted. + +* Currently, at least on recent linux kernel, vgdb can properly wake + up a valgrind process which is blocked in system calls. Maybe we + need to see till which kernel version the ptrace + syscall restart + is broken, and put the default value of --max-invoke-ms to 0 in this + case. + +* more client requests can be programmed in various tools. Currently, + there are only a few standard valgrind or memcheck client requests + implemented. + vg.suppression [generate|add|delete] might be an interesting command: + generate would output a suppression, add/delete would add a suppression + in memory for the last (or selected?) error. + vg.break on fn calls/entry/exit + commands associated to it + (such as search leaks)? + + + +* currently jump(s) and inferior call(s) are somewhat dangerous + when called from a block not yet instrumented : instead + of continuing till the next Imark, where there will be a + debugger call that can properly jump at an instruction boundary, + the jump/call will quit the "middle" of an instruction. + We could detect if the current block is instrumented by a trick + like this: + /* Each time helperc_CallDebugger is called, we will store + the address from which is it called and the nr of bbs_done + when called. This allows to detect that gdbserver is called + from a block which is instrumented. */ + static HWord CallDebugger_addr; + static ULong CallDebugger_bbs_done; + + Bool VG_(gdbserver_current_IP_instrumented) (ThreadId tid) + { + if (VG_(get_IP) (tid) != CallDebugger_addr + || CallDebugger_bbs_done != VG_(bbs_done)()) + return False; + return True; + } + + Alternatively, we ensure we can re-instrument the current + block for gdbserver while executing it. + Something like: + keep current block till the end of the current instruction, then + go back to scheduler. + Unsure if and how this is do-able. + + +* ensure that all non static symbols of gdbserver files are #define + xxxxx VG_(xxxxx) ???? Is this really needed ? I have tried to put in + a test program variables and functions with the same name as valgrind + stuff, and everything seems to be ok. + I see that all exported symbols in valgrind have a unique prefix + created with VG_ or MC_ or ... + This is not done for the "gdb gdbserver code", where I have kept + the original names. Is this a problem ? I could not create + a "symbol" collision between the user symbol and the valgrind + core gdbserver symbol. + +* currently, gdbserver can only stop/continue the whole process. It + might be interesting to have a fine-grained thread control (vCont + packet) maybe for tools such as helgrind, drd. This would allow the + user to stop/resume specific threads. Also, maybe this would solve + the following problem: wait for a breakpoint to be encountered, + switch thread, next. This sometimes causes an internal error in gdb, + probably because gdb believes the current thread will be continued ? + +* would be nice to have some more tests. + +* better valgrind target support in gdb (see comments of Tom Tromey). + + +-------- description of how gdb invokes a function in the inferior +to call a function in the inferior (below is for x86): +gdb writes ESP and EBP to have some more stack space +push a return address equal to 0x8048390 <_start> +puts a break at 0x8048390 +put address of the function to call (e.g. hello_world in EIP (0x8048444)) +continue +break encountered at 0x8048391 (90 after decrement) + => report stop to gdb + => gdb restores esp/ebp/eip to what it was (eg. 0x804848C) + => gdb "s" => causes the EIP to go to the new EIP (i.e. 0x804848C) + gdbserver tells "resuming from 0x804848c" + "stop pc is 0x8048491" => informed gdb of this + diff --git a/coregrind/m_gdbserver/amd64-coresse-valgrind.xml b/coregrind/m_gdbserver/amd64-coresse-valgrind.xml new file mode 100644 index 00000000..3008d5fa --- /dev/null +++ b/coregrind/m_gdbserver/amd64-coresse-valgrind.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- AMD64 - core and sse. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>i386:x86-64</architecture> + <xi:include href="64bit-core.xml"/> + <xi:include href="64bit-sse.xml"/> + <xi:include href="64bit-core-valgrind-s1.xml"/> + <xi:include href="64bit-sse-valgrind-s1.xml"/> + <xi:include href="64bit-core-valgrind-s2.xml"/> + <xi:include href="64bit-sse-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/amd64-linux-valgrind.xml b/coregrind/m_gdbserver/amd64-linux-valgrind.xml new file mode 100644 index 00000000..a18e557f --- /dev/null +++ b/coregrind/m_gdbserver/amd64-linux-valgrind.xml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- AMD64 - Includes Linux-only special "register". --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>i386:x86-64</architecture> + <osabi>GNU/Linux</osabi> + <xi:include href="64bit-core.xml"/> + <xi:include href="64bit-sse.xml"/> + <xi:include href="64bit-linux.xml"/> + <xi:include href="64bit-core-valgrind-s1.xml"/> + <xi:include href="64bit-sse-valgrind-s1.xml"/> + <xi:include href="64bit-linux-valgrind-s1.xml"/> + <xi:include href="64bit-core-valgrind-s2.xml"/> + <xi:include href="64bit-sse-valgrind-s2.xml"/> + <xi:include href="64bit-linux-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/arm-core-valgrind-s1.xml b/coregrind/m_gdbserver/arm-core-valgrind-s1.xml new file mode 100644 index 00000000..cc033a08 --- /dev/null +++ b/coregrind/m_gdbserver/arm-core-valgrind-s1.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.arm.core.valgrind.s1"> + <reg name="r0s1" bitsize="32"/> + <reg name="r1s1" bitsize="32"/> + <reg name="r2s1" bitsize="32"/> + <reg name="r3s1" bitsize="32"/> + <reg name="r4s1" bitsize="32"/> + <reg name="r5s1" bitsize="32"/> + <reg name="r6s1" bitsize="32"/> + <reg name="r7s1" bitsize="32"/> + <reg name="r8s1" bitsize="32"/> + <reg name="r9s1" bitsize="32"/> + <reg name="r10s1" bitsize="32"/> + <reg name="r11s1" bitsize="32"/> + <reg name="r12s1" bitsize="32"/> + <reg name="sps1" bitsize="32" type="data_ptr"/> + <reg name="lrs1" bitsize="32"/> + <reg name="pcs1" bitsize="32" type="code_ptr"/> + + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + <reg name="cpsrs1" bitsize="32" regnum="25"/> +</feature> diff --git a/coregrind/m_gdbserver/arm-core-valgrind-s2.xml b/coregrind/m_gdbserver/arm-core-valgrind-s2.xml new file mode 100644 index 00000000..9c3aa69f --- /dev/null +++ b/coregrind/m_gdbserver/arm-core-valgrind-s2.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.arm.core.valgrind.s2"> + <reg name="r0s2" bitsize="32"/> + <reg name="r1s2" bitsize="32"/> + <reg name="r2s2" bitsize="32"/> + <reg name="r3s2" bitsize="32"/> + <reg name="r4s2" bitsize="32"/> + <reg name="r5s2" bitsize="32"/> + <reg name="r6s2" bitsize="32"/> + <reg name="r7s2" bitsize="32"/> + <reg name="r8s2" bitsize="32"/> + <reg name="r9s2" bitsize="32"/> + <reg name="r10s2" bitsize="32"/> + <reg name="r11s2" bitsize="32"/> + <reg name="r12s2" bitsize="32"/> + <reg name="sps2" bitsize="32" type="data_ptr"/> + <reg name="lrs2" bitsize="32"/> + <reg name="pcs2" bitsize="32" type="code_ptr"/> + + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + <reg name="cpsrs2" bitsize="32" regnum="25"/> +</feature> diff --git a/coregrind/m_gdbserver/arm-core.xml b/coregrind/m_gdbserver/arm-core.xml new file mode 100644 index 00000000..16249011 --- /dev/null +++ b/coregrind/m_gdbserver/arm-core.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.arm.core"> + <reg name="r0" bitsize="32"/> + <reg name="r1" bitsize="32"/> + <reg name="r2" bitsize="32"/> + <reg name="r3" bitsize="32"/> + <reg name="r4" bitsize="32"/> + <reg name="r5" bitsize="32"/> + <reg name="r6" bitsize="32"/> + <reg name="r7" bitsize="32"/> + <reg name="r8" bitsize="32"/> + <reg name="r9" bitsize="32"/> + <reg name="r10" bitsize="32"/> + <reg name="r11" bitsize="32"/> + <reg name="r12" bitsize="32"/> + <reg name="sp" bitsize="32" type="data_ptr"/> + <reg name="lr" bitsize="32"/> + <reg name="pc" bitsize="32" type="code_ptr"/> + + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + <reg name="cpsr" bitsize="32" regnum="25"/> +</feature> diff --git a/coregrind/m_gdbserver/arm-vfpv3-valgrind-s1.xml b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s1.xml new file mode 100644 index 00000000..619f73fc --- /dev/null +++ b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s1.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.arm.vfp.valgrind.s1"> + <reg name="d0s1" bitsize="64" type="ieee_double"/> + <reg name="d1s1" bitsize="64" type="ieee_double"/> + <reg name="d2s1" bitsize="64" type="ieee_double"/> + <reg name="d3s1" bitsize="64" type="ieee_double"/> + <reg name="d4s1" bitsize="64" type="ieee_double"/> + <reg name="d5s1" bitsize="64" type="ieee_double"/> + <reg name="d6s1" bitsize="64" type="ieee_double"/> + <reg name="d7s1" bitsize="64" type="ieee_double"/> + <reg name="d8s1" bitsize="64" type="ieee_double"/> + <reg name="d9s1" bitsize="64" type="ieee_double"/> + <reg name="d10s1" bitsize="64" type="ieee_double"/> + <reg name="d11s1" bitsize="64" type="ieee_double"/> + <reg name="d12s1" bitsize="64" type="ieee_double"/> + <reg name="d13s1" bitsize="64" type="ieee_double"/> + <reg name="d14s1" bitsize="64" type="ieee_double"/> + <reg name="d15s1" bitsize="64" type="ieee_double"/> + <reg name="d16s1" bitsize="64" type="ieee_double"/> + <reg name="d17s1" bitsize="64" type="ieee_double"/> + <reg name="d18s1" bitsize="64" type="ieee_double"/> + <reg name="d19s1" bitsize="64" type="ieee_double"/> + <reg name="d20s1" bitsize="64" type="ieee_double"/> + <reg name="d21s1" bitsize="64" type="ieee_double"/> + <reg name="d22s1" bitsize="64" type="ieee_double"/> + <reg name="d23s1" bitsize="64" type="ieee_double"/> + <reg name="d24s1" bitsize="64" type="ieee_double"/> + <reg name="d25s1" bitsize="64" type="ieee_double"/> + <reg name="d26s1" bitsize="64" type="ieee_double"/> + <reg name="d27s1" bitsize="64" type="ieee_double"/> + <reg name="d28s1" bitsize="64" type="ieee_double"/> + <reg name="d29s1" bitsize="64" type="ieee_double"/> + <reg name="d30s1" bitsize="64" type="ieee_double"/> + <reg name="d31s1" bitsize="64" type="ieee_double"/> + + <reg name="fpscrs1" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/arm-vfpv3-valgrind-s2.xml b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s2.xml new file mode 100644 index 00000000..c0e86771 --- /dev/null +++ b/coregrind/m_gdbserver/arm-vfpv3-valgrind-s2.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.arm.vfp.valgrind.s2"> + <reg name="d0s2" bitsize="64" type="ieee_double"/> + <reg name="d1s2" bitsize="64" type="ieee_double"/> + <reg name="d2s2" bitsize="64" type="ieee_double"/> + <reg name="d3s2" bitsize="64" type="ieee_double"/> + <reg name="d4s2" bitsize="64" type="ieee_double"/> + <reg name="d5s2" bitsize="64" type="ieee_double"/> + <reg name="d6s2" bitsize="64" type="ieee_double"/> + <reg name="d7s2" bitsize="64" type="ieee_double"/> + <reg name="d8s2" bitsize="64" type="ieee_double"/> + <reg name="d9s2" bitsize="64" type="ieee_double"/> + <reg name="d10s2" bitsize="64" type="ieee_double"/> + <reg name="d11s2" bitsize="64" type="ieee_double"/> + <reg name="d12s2" bitsize="64" type="ieee_double"/> + <reg name="d13s2" bitsize="64" type="ieee_double"/> + <reg name="d14s2" bitsize="64" type="ieee_double"/> + <reg name="d15s2" bitsize="64" type="ieee_double"/> + <reg name="d16s2" bitsize="64" type="ieee_double"/> + <reg name="d17s2" bitsize="64" type="ieee_double"/> + <reg name="d18s2" bitsize="64" type="ieee_double"/> + <reg name="d19s2" bitsize="64" type="ieee_double"/> + <reg name="d20s2" bitsize="64" type="ieee_double"/> + <reg name="d21s2" bitsize="64" type="ieee_double"/> + <reg name="d22s2" bitsize="64" type="ieee_double"/> + <reg name="d23s2" bitsize="64" type="ieee_double"/> + <reg name="d24s2" bitsize="64" type="ieee_double"/> + <reg name="d25s2" bitsize="64" type="ieee_double"/> + <reg name="d26s2" bitsize="64" type="ieee_double"/> + <reg name="d27s2" bitsize="64" type="ieee_double"/> + <reg name="d28s2" bitsize="64" type="ieee_double"/> + <reg name="d29s2" bitsize="64" type="ieee_double"/> + <reg name="d30s2" bitsize="64" type="ieee_double"/> + <reg name="d31s2" bitsize="64" type="ieee_double"/> + + <reg name="fpscrs2" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/arm-vfpv3.xml b/coregrind/m_gdbserver/arm-vfpv3.xml new file mode 100644 index 00000000..d0e9a59e --- /dev/null +++ b/coregrind/m_gdbserver/arm-vfpv3.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.arm.vfp"> + <reg name="d0" bitsize="64" type="ieee_double"/> + <reg name="d1" bitsize="64" type="ieee_double"/> + <reg name="d2" bitsize="64" type="ieee_double"/> + <reg name="d3" bitsize="64" type="ieee_double"/> + <reg name="d4" bitsize="64" type="ieee_double"/> + <reg name="d5" bitsize="64" type="ieee_double"/> + <reg name="d6" bitsize="64" type="ieee_double"/> + <reg name="d7" bitsize="64" type="ieee_double"/> + <reg name="d8" bitsize="64" type="ieee_double"/> + <reg name="d9" bitsize="64" type="ieee_double"/> + <reg name="d10" bitsize="64" type="ieee_double"/> + <reg name="d11" bitsize="64" type="ieee_double"/> + <reg name="d12" bitsize="64" type="ieee_double"/> + <reg name="d13" bitsize="64" type="ieee_double"/> + <reg name="d14" bitsize="64" type="ieee_double"/> + <reg name="d15" bitsize="64" type="ieee_double"/> + <reg name="d16" bitsize="64" type="ieee_double"/> + <reg name="d17" bitsize="64" type="ieee_double"/> + <reg name="d18" bitsize="64" type="ieee_double"/> + <reg name="d19" bitsize="64" type="ieee_double"/> + <reg name="d20" bitsize="64" type="ieee_double"/> + <reg name="d21" bitsize="64" type="ieee_double"/> + <reg name="d22" bitsize="64" type="ieee_double"/> + <reg name="d23" bitsize="64" type="ieee_double"/> + <reg name="d24" bitsize="64" type="ieee_double"/> + <reg name="d25" bitsize="64" type="ieee_double"/> + <reg name="d26" bitsize="64" type="ieee_double"/> + <reg name="d27" bitsize="64" type="ieee_double"/> + <reg name="d28" bitsize="64" type="ieee_double"/> + <reg name="d29" bitsize="64" type="ieee_double"/> + <reg name="d30" bitsize="64" type="ieee_double"/> + <reg name="d31" bitsize="64" type="ieee_double"/> + + <reg name="fpscr" bitsize="32" type="int" group="float"/> +</feature> diff --git a/coregrind/m_gdbserver/arm-with-vfpv3-valgrind.xml b/coregrind/m_gdbserver/arm-with-vfpv3-valgrind.xml new file mode 100644 index 00000000..6501c9b8 --- /dev/null +++ b/coregrind/m_gdbserver/arm-with-vfpv3-valgrind.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <xi:include href="arm-core.xml"/> + <xi:include href="arm-vfpv3.xml"/> + <xi:include href="arm-core-valgrind-s1.xml"/> + <xi:include href="arm-vfpv3-valgrind-s1.xml"/> + <xi:include href="arm-core-valgrind-s2.xml"/> + <xi:include href="arm-vfpv3-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/arm-with-vfpv3.xml b/coregrind/m_gdbserver/arm-with-vfpv3.xml new file mode 100644 index 00000000..319da1a7 --- /dev/null +++ b/coregrind/m_gdbserver/arm-with-vfpv3.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <xi:include href="arm-core.xml"/> + <xi:include href="arm-vfpv3.xml"/> +</target> diff --git a/coregrind/m_gdbserver/gdb/signals.h b/coregrind/m_gdbserver/gdb/signals.h new file mode 100644 index 00000000..c240f6b3 --- /dev/null +++ b/coregrind/m_gdbserver/gdb/signals.h @@ -0,0 +1,238 @@ +/* Target signal numbers for GDB and the GDB remote protocol. + Copyright 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + 1997, 1998, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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 GDB_SIGNALS_H +#define GDB_SIGNALS_H + +/* The numbering of these signals is chosen to match traditional unix + signals (insofar as various unices use the same numbers, anyway). + It is also the numbering of the GDB remote protocol. Other remote + protocols, if they use a different numbering, should make sure to + translate appropriately. + + Since these numbers have actually made it out into other software + (stubs, etc.), you mustn't disturb the assigned numbering. If you + need to add new signals here, add them to the end of the explicitly + numbered signals, at the comment marker. Add them unconditionally, + not within any #if or #ifdef. + + This is based strongly on Unix/POSIX signals for several reasons: + (1) This set of signals represents a widely-accepted attempt to + represent events of this sort in a portable fashion, (2) we want a + signal to make it from wait to child_wait to the user intact, (3) many + remote protocols use a similar encoding. However, it is + recognized that this set of signals has limitations (such as not + distinguishing between various kinds of SIGSEGV, or not + distinguishing hitting a breakpoint from finishing a single step). + So in the future we may get around this either by adding additional + signals for breakpoint, single-step, etc., or by adding signal + codes; the latter seems more in the spirit of what BSD, System V, + etc. are doing to address these issues. */ + +/* For an explanation of what each signal means, see + signals.c. */ + +enum target_signal + { + /* Used some places (e.g. stop_signal) to record the concept that + there is no signal. */ + TARGET_SIGNAL_0 = 0, + TARGET_SIGNAL_FIRST = 0, + TARGET_SIGNAL_HUP = 1, + TARGET_SIGNAL_INT = 2, + TARGET_SIGNAL_QUIT = 3, + TARGET_SIGNAL_ILL = 4, + TARGET_SIGNAL_TRAP = 5, + TARGET_SIGNAL_ABRT = 6, + TARGET_SIGNAL_EMT = 7, + TARGET_SIGNAL_FPE = 8, + TARGET_SIGNAL_KILL = 9, + TARGET_SIGNAL_BUS = 10, + TARGET_SIGNAL_SEGV = 11, + TARGET_SIGNAL_SYS = 12, + TARGET_SIGNAL_PIPE = 13, + TARGET_SIGNAL_ALRM = 14, + TARGET_SIGNAL_TERM = 15, + TARGET_SIGNAL_URG = 16, + TARGET_SIGNAL_STOP = 17, + TARGET_SIGNAL_TSTP = 18, + TARGET_SIGNAL_CONT = 19, + TARGET_SIGNAL_CHLD = 20, + TARGET_SIGNAL_TTIN = 21, + TARGET_SIGNAL_TTOU = 22, + TARGET_SIGNAL_IO = 23, + TARGET_SIGNAL_XCPU = 24, + TARGET_SIGNAL_XFSZ = 25, + TARGET_SIGNAL_VTALRM = 26, + TARGET_SIGNAL_PROF = 27, + TARGET_SIGNAL_WINCH = 28, + TARGET_SIGNAL_LOST = 29, + TARGET_SIGNAL_USR1 = 30, + TARGET_SIGNAL_USR2 = 31, + TARGET_SIGNAL_PWR = 32, + /* Similar to SIGIO. Perhaps they should have the same number. */ + TARGET_SIGNAL_POLL = 33, + TARGET_SIGNAL_WIND = 34, + TARGET_SIGNAL_PHONE = 35, + TARGET_SIGNAL_WAITING = 36, + TARGET_SIGNAL_LWP = 37, + TARGET_SIGNAL_DANGER = 38, + TARGET_SIGNAL_GRANT = 39, + TARGET_SIGNAL_RETRACT = 40, + TARGET_SIGNAL_MSG = 41, + TARGET_SIGNAL_SOUND = 42, + TARGET_SIGNAL_SAK = 43, + TARGET_SIGNAL_PRIO = 44, + TARGET_SIGNAL_REALTIME_33 = 45, + TARGET_SIGNAL_REALTIME_34 = 46, + TARGET_SIGNAL_REALTIME_35 = 47, + TARGET_SIGNAL_REALTIME_36 = 48, + TARGET_SIGNAL_REALTIME_37 = 49, + TARGET_SIGNAL_REALTIME_38 = 50, + TARGET_SIGNAL_REALTIME_39 = 51, + TARGET_SIGNAL_REALTIME_40 = 52, + TARGET_SIGNAL_REALTIME_41 = 53, + TARGET_SIGNAL_REALTIME_42 = 54, + TARGET_SIGNAL_REALTIME_43 = 55, + TARGET_SIGNAL_REALTIME_44 = 56, + TARGET_SIGNAL_REALTIME_45 = 57, + TARGET_SIGNAL_REALTIME_46 = 58, + TARGET_SIGNAL_REALTIME_47 = 59, + TARGET_SIGNAL_REALTIME_48 = 60, + TARGET_SIGNAL_REALTIME_49 = 61, + TARGET_SIGNAL_REALTIME_50 = 62, + TARGET_SIGNAL_REALTIME_51 = 63, + TARGET_SIGNAL_REALTIME_52 = 64, + TARGET_SIGNAL_REALTIME_53 = 65, + TARGET_SIGNAL_REALTIME_54 = 66, + TARGET_SIGNAL_REALTIME_55 = 67, + TARGET_SIGNAL_REALTIME_56 = 68, + TARGET_SIGNAL_REALTIME_57 = 69, + TARGET_SIGNAL_REALTIME_58 = 70, + TARGET_SIGNAL_REALTIME_59 = 71, + TARGET_SIGNAL_REALTIME_60 = 72, + TARGET_SIGNAL_REALTIME_61 = 73, + TARGET_SIGNAL_REALTIME_62 = 74, + TARGET_SIGNAL_REALTIME_63 = 75, + + /* Used internally by Solaris threads. See signal(5) on Solaris. */ + TARGET_SIGNAL_CANCEL = 76, + + /* Yes, this pains me, too. But LynxOS didn't have SIG32, and now + GNU/Linux does, and we can't disturb the numbering, since it's + part of the remote protocol. Note that in some GDB's + TARGET_SIGNAL_REALTIME_32 is number 76. */ + TARGET_SIGNAL_REALTIME_32, + /* Yet another pain, IRIX 6 has SIG64. */ + TARGET_SIGNAL_REALTIME_64, + /* Yet another pain, GNU/Linux MIPS might go up to 128. */ + TARGET_SIGNAL_REALTIME_65, + TARGET_SIGNAL_REALTIME_66, + TARGET_SIGNAL_REALTIME_67, + TARGET_SIGNAL_REALTIME_68, + TARGET_SIGNAL_REALTIME_69, + TARGET_SIGNAL_REALTIME_70, + TARGET_SIGNAL_REALTIME_71, + TARGET_SIGNAL_REALTIME_72, + TARGET_SIGNAL_REALTIME_73, + TARGET_SIGNAL_REALTIME_74, + TARGET_SIGNAL_REALTIME_75, + TARGET_SIGNAL_REALTIME_76, + TARGET_SIGNAL_REALTIME_77, + TARGET_SIGNAL_REALTIME_78, + TARGET_SIGNAL_REALTIME_79, + TARGET_SIGNAL_REALTIME_80, + TARGET_SIGNAL_REALTIME_81, + TARGET_SIGNAL_REALTIME_82, + TARGET_SIGNAL_REALTIME_83, + TARGET_SIGNAL_REALTIME_84, + TARGET_SIGNAL_REALTIME_85, + TARGET_SIGNAL_REALTIME_86, + TARGET_SIGNAL_REALTIME_87, + TARGET_SIGNAL_REALTIME_88, + TARGET_SIGNAL_REALTIME_89, + TARGET_SIGNAL_REALTIME_90, + TARGET_SIGNAL_REALTIME_91, + TARGET_SIGNAL_REALTIME_92, + TARGET_SIGNAL_REALTIME_93, + TARGET_SIGNAL_REALTIME_94, + TARGET_SIGNAL_REALTIME_95, + TARGET_SIGNAL_REALTIME_96, + TARGET_SIGNAL_REALTIME_97, + TARGET_SIGNAL_REALTIME_98, + TARGET_SIGNAL_REALTIME_99, + TARGET_SIGNAL_REALTIME_100, + TARGET_SIGNAL_REALTIME_101, + TARGET_SIGNAL_REALTIME_102, + TARGET_SIGNAL_REALTIME_103, + TARGET_SIGNAL_REALTIME_104, + TARGET_SIGNAL_REALTIME_105, + TARGET_SIGNAL_REALTIME_106, + TARGET_SIGNAL_REALTIME_107, + TARGET_SIGNAL_REALTIME_108, + TARGET_SIGNAL_REALTIME_109, + TARGET_SIGNAL_REALTIME_110, + TARGET_SIGNAL_REALTIME_111, + TARGET_SIGNAL_REALTIME_112, + TARGET_SIGNAL_REALTIME_113, + TARGET_SIGNAL_REALTIME_114, + TARGET_SIGNAL_REALTIME_115, + TARGET_SIGNAL_REALTIME_116, + TARGET_SIGNAL_REALTIME_117, + TARGET_SIGNAL_REALTIME_118, + TARGET_SIGNAL_REALTIME_119, + TARGET_SIGNAL_REALTIME_120, + TARGET_SIGNAL_REALTIME_121, + TARGET_SIGNAL_REALTIME_122, + TARGET_SIGNAL_REALTIME_123, + TARGET_SIGNAL_REALTIME_124, + TARGET_SIGNAL_REALTIME_125, + TARGET_SIGNAL_REALTIME_126, + TARGET_SIGNAL_REALTIME_127, + + TARGET_SIGNAL_INFO, + + /* Some signal we don't know about. */ + TARGET_SIGNAL_UNKNOWN, + + /* Use whatever signal we use when one is not specifically specified + (for passing to proceed and so on). */ + TARGET_SIGNAL_DEFAULT, + + /* Mach exceptions. In versions of GDB before 5.2, these were just before + TARGET_SIGNAL_INFO if you were compiling on a Mach host (and missing + otherwise). */ + TARGET_EXC_BAD_ACCESS, + TARGET_EXC_BAD_INSTRUCTION, + TARGET_EXC_ARITHMETIC, + TARGET_EXC_EMULATION, + TARGET_EXC_SOFTWARE, + TARGET_EXC_BREAKPOINT, + + /* If you are adding a new signal, add it just above this comment. */ + + /* Last and unused enum value, for sizing arrays, etc. */ + TARGET_SIGNAL_LAST + }; + +#endif /* #ifndef GDB_SIGNALS_H */ diff --git a/coregrind/m_gdbserver/i386-coresse-valgrind.xml b/coregrind/m_gdbserver/i386-coresse-valgrind.xml new file mode 100644 index 00000000..1ec7c9eb --- /dev/null +++ b/coregrind/m_gdbserver/i386-coresse-valgrind.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- I386 with SSE --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>i386</architecture> + <xi:include href="32bit-core.xml"/> + <xi:include href="32bit-sse.xml"/> + <xi:include href="32bit-core-valgrind-s1.xml"/> + <xi:include href="32bit-sse-valgrind-s1.xml"/> + <xi:include href="32bit-core-valgrind-s2.xml"/> + <xi:include href="32bit-sse-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/i386-linux-valgrind.xml b/coregrind/m_gdbserver/i386-linux-valgrind.xml new file mode 100644 index 00000000..87204409 --- /dev/null +++ b/coregrind/m_gdbserver/i386-linux-valgrind.xml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- I386 with SSE - Includes Linux-only special "register". --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>i386</architecture> + <osabi>GNU/Linux</osabi> + <xi:include href="32bit-core.xml"/> + <xi:include href="32bit-sse.xml"/> + <xi:include href="32bit-linux.xml"/> + <xi:include href="32bit-core-valgrind-s1.xml"/> + <xi:include href="32bit-sse-valgrind-s1.xml"/> + <xi:include href="32bit-linux-valgrind-s1.xml"/> + <xi:include href="32bit-core-valgrind-s2.xml"/> + <xi:include href="32bit-sse-valgrind-s2.xml"/> + <xi:include href="32bit-linux-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/inferiors.c b/coregrind/m_gdbserver/inferiors.c new file mode 100644 index 00000000..cdfea0bc --- /dev/null +++ b/coregrind/m_gdbserver/inferiors.c @@ -0,0 +1,227 @@ +/* Inferior process information for the remote server for GDB. + Copyright (C) 2002, 2005, 2011 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" + +struct thread_info +{ + struct inferior_list_entry entry; + void *target_data; + void *regcache_data; + unsigned int gdb_id; +}; + +struct inferior_list all_threads; + +struct thread_info *current_inferior; + +#define get_thread(inf) ((struct thread_info *)(inf)) + +void add_inferior_to_list (struct inferior_list *list, + struct inferior_list_entry *new_inferior) +{ + new_inferior->next = NULL; + if (list->tail != NULL) + list->tail->next = new_inferior; + else + list->head = new_inferior; + list->tail = new_inferior; +} + +void for_each_inferior (struct inferior_list *list, + void (*action) (struct inferior_list_entry *)) +{ + struct inferior_list_entry *cur = list->head, *next; + + while (cur != NULL) { + next = cur->next; + (*action) (cur); + cur = next; + } +} + +void change_inferior_id (struct inferior_list *list, + unsigned long new_id) +{ + if (list->head != list->tail) + error ("tried to change thread ID after multiple threads are created\n"); + + list->head->id = new_id; +} + +void remove_inferior (struct inferior_list *list, + struct inferior_list_entry *entry) +{ + struct inferior_list_entry **cur; + + if (list->head == entry) { + list->head = entry->next; + if (list->tail == entry) + list->tail = list->head; + return; + } + + cur = &list->head; + while (*cur && (*cur)->next != entry) + cur = &(*cur)->next; + + if (*cur == NULL) + return; + + (*cur)->next = entry->next; + + if (list->tail == entry) + list->tail = *cur; +} + +void add_thread (unsigned long thread_id, void *target_data, unsigned int gdb_id) +{ + struct thread_info *new_thread + = (struct thread_info *) malloc (sizeof (*new_thread)); + + VG_(memset) (new_thread, 0, sizeof (*new_thread)); + + new_thread->entry.id = thread_id; + + add_inferior_to_list (&all_threads, & new_thread->entry); + + if (current_inferior == NULL) + current_inferior = new_thread; + + new_thread->target_data = target_data; + set_inferior_regcache_data (new_thread, new_register_cache ()); + new_thread->gdb_id = gdb_id; +} + +unsigned int thread_id_to_gdb_id (unsigned long thread_id) +{ + struct inferior_list_entry *inf = all_threads.head; + + while (inf != NULL) { + struct thread_info *thread = get_thread (inf); + if (inf->id == thread_id) + return thread->gdb_id; + inf = inf->next; + } + + return 0; +} + +unsigned int thread_to_gdb_id (struct thread_info *thread) +{ + return thread->gdb_id; +} + +struct thread_info * gdb_id_to_thread (unsigned int gdb_id) +{ + struct inferior_list_entry *inf = all_threads.head; + + while (inf != NULL) { + struct thread_info *thread = get_thread (inf); + if (thread->gdb_id == gdb_id) + return thread; + inf = inf->next; + } + + return NULL; +} + +unsigned long gdb_id_to_thread_id (unsigned int gdb_id) +{ + struct thread_info *thread = gdb_id_to_thread (gdb_id); + + return thread ? thread->entry.id : 0; +} + +static +void free_one_thread (struct inferior_list_entry *inf) +{ + struct thread_info *thread = get_thread (inf); + free_register_cache (inferior_regcache_data (thread)); + free (thread); +} + +void remove_thread (struct thread_info *thread) +{ + remove_inferior (&all_threads, (struct inferior_list_entry *) thread); + free_one_thread (&thread->entry); +} + +void clear_inferiors (void) +{ + for_each_inferior (&all_threads, free_one_thread); + + all_threads.head = all_threads.tail = NULL; +} + +struct inferior_list_entry * find_inferior (struct inferior_list *list, + int (*func) + (struct inferior_list_entry *, + void *), + void *arg) +{ + struct inferior_list_entry *inf = list->head; + + while (inf != NULL) { + if ((*func) (inf, arg)) + return inf; + inf = inf->next; + } + + return NULL; +} + +struct inferior_list_entry * find_inferior_id (struct inferior_list *list, + unsigned long id) +{ + struct inferior_list_entry *inf = list->head; + + while (inf != NULL) { + if (inf->id == id) + return inf; + inf = inf->next; + } + + return NULL; +} + +void * inferior_target_data (struct thread_info *inferior) +{ + return inferior->target_data; +} + +void set_inferior_target_data (struct thread_info *inferior, void *data) +{ + inferior->target_data = data; +} + +void * inferior_regcache_data (struct thread_info *inferior) +{ + return inferior->regcache_data; +} + +void set_inferior_regcache_data (struct thread_info *inferior, void *data) +{ + inferior->regcache_data = data; +} diff --git a/coregrind/m_gdbserver/m_gdbserver.c b/coregrind/m_gdbserver/m_gdbserver.c new file mode 100644 index 00000000..cfde922e --- /dev/null +++ b/coregrind/m_gdbserver/m_gdbserver.c @@ -0,0 +1,1289 @@ + +/*--------------------------------------------------------------------*/ +/*--- Handle remote gdb protocol. m_gdbserver.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2011 Philippe Waroquiers + + 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. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_debuglog.h" +#include "pub_core_libcproc.h" +#include "pub_core_libcprint.h" +#include "pub_core_mallocfree.h" +#include "pub_core_gdbserver.h" +#include "pub_core_options.h" +#include "pub_core_libcsetjmp.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_tool_hashtable.h" +#include "pub_core_libcassert.h" +#include "pub_tool_libcbase.h" +#include "pub_core_libcsignal.h" +#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry) +#include "pub_tool_debuginfo.h" +#include "pub_core_scheduler.h" +#include "pub_core_syswrap.h" + +#include "server.h" + +Int VG_(dyn_vgdb_error); + +/* forward declarations */ +VG_REGPARM(1) +void VG_(helperc_CallDebugger) ( HWord iaddr ); +VG_REGPARM(1) +void VG_(helperc_invalidate_if_not_gdbserved) ( Addr addr ); +static void invalidate_current_ip (ThreadId tid, char *who); + +/* reasons of call to call_gdbserver. */ +typedef + enum { + init_reason, // initialises gdbserver resources + vgdb_reason, // gdbserver invocation by vgdb doing ptrace + core_reason, // gdbserver invocation by core (e.g. error encountered) + break_reason, // break encountered + watch_reason, // watchpoint detected by tool + signal_reason} // signal encountered + CallReason; + +static char* ppCallReason(CallReason reason) +{ + switch (reason) { + case init_reason: return "init_reason"; + case vgdb_reason: return "vgdb_reason"; + case core_reason: return "core_reason"; + case break_reason: return "break_reason"; + case watch_reason: return "watch_reason"; + case signal_reason: return "signal_reason"; + default: vg_assert (0); + } +} + +/* An instruction instrumented for gdbserver looks like this: + 1. Ist_Mark (0x1234) + 2. helperc_CallDebugger (0x1234) + This will give control to gdb if there is a break at 0x1234 + or if we are single stepping + 3. ... here the real IR for the instruction at 0x1234 + + When there is a break at 0x1234: + if user does "continue" or "step" or similar, + then - the call to debugger returns + - valgrind executes at 3. the real IR(s) for 0x1234 + + if as part of helperc_CallDebugger, the user calls + some code in gdb e.g print hello_world() + then - gdb prepares a dummy stack frame with a specific + return address (typically it uses _start) and + inserts a break at this address + - gdb then puts in EIP the address of hello_world() + - gdb then continues (so the helperc_CallDebugger + returns) + - call_gdbserver() function will then return the + control to the scheduler (using VG_MINIMAL_LONGJMP) + to allow the block of the new EIP + to be executed. + - hello_world code is executed. + - when hello_world() returns, it returns to + _start and encounters the break at _start. + - gdb then removes this break, put 0x1234 in EIP + and does a "step". This causes to jump from + _start to 0x1234, where the call to + helperc_CallDebugger is redone. + - This is all ok, the user can then give new gdb + commands. + + However, when continue is given, address 0x1234 is to + be executed: gdb gives a single step, which must not + report again the break at 0x1234. To avoid a 2nd report + of the same break, the below tells that the next + helperc_CallDebugger call must ignore a break/stop at + this address. +*/ +static Addr ignore_this_break_once = 0; + + +static void call_gdbserver ( ThreadId tid , CallReason reason); + +/* convert from CORE_ADDR to void* */ +static +void* C2v(CORE_ADDR addr) +{ + return (void*) addr; +} + +/* Describes the address addr (for debugging/printing purposes). + Last two results are kept. A third call will replace the + oldest result. */ +static char* sym (Addr addr, Bool is_code) +{ + static char buf[2][200]; + static int w = 0; + PtrdiffT offset; + if (w == 2) w = 0; + if (is_code) { + VG_(describe_IP) (addr, buf[w], 200); + } else { + VG_(get_datasym_and_offset) (addr, buf[w], 200, &offset); + } + return buf[w++]; +} + +/* Each time gdbserver is called, gdbserver_called is incremented + gdbserver_exited is incremented when gdbserver is asked to exit */ +static int gdbserver_called = 0; +static int gdbserver_exited = 0; + +typedef + enum { + GS_break, + GS_jump + } + GS_Kind; + +typedef + struct _GS_Address { + struct _GS_Address* next; + Addr addr; + GS_Kind kind; + } + GS_Address; + +/* gs_addresses contains a list of all addresses that have been invalidated + because they have been (or must be) instrumented for gdbserver. + An entry is added in this table when there is a break at this + address (kind == GS_break) or if this address is the jump target of an + exit of a block that has been instrumented for gdbserver while + single stepping (kind == GS_jump). + When gdbserver is not single stepping anymore, all GS_jump entries + are removed, their translations are invalidated. +*/ +static VgHashTable gs_addresses = NULL; + +static void add_gs_address (Addr addr, GS_Kind kind, char* from) +{ + GS_Address *p; + + p = VG_(arena_malloc)(VG_AR_CORE, from, sizeof(GS_Address)); + p->addr = addr; + p->kind = kind; + VG_(HT_add_node)(gs_addresses, p); + VG_(discard_translations) (addr, 1, from); +} + +static void remove_gs_address (GS_Address* g, char* from) +{ + VG_(HT_remove) (gs_addresses, g->addr); + VG_(discard_translations) (g->addr, 1, from); + VG_(arena_free) (VG_AR_CORE, g); +} + +char* VG_(ppPointKind) (PointKind kind) +{ + switch(kind) { + case software_breakpoint: return "software_breakpoint"; + case hardware_breakpoint: return "hardware_breakpoint"; + case write_watchpoint: return "write_watchpoint"; + case read_watchpoint: return "read_watchpoint"; + case access_watchpoint: return "access_watchpoint"; + default: vg_assert(0); + } +} + +typedef + struct _GS_Watch { + struct _GS_Watch* next; + Addr addr; + SizeT len; + PointKind kind; + } + GS_Watch; + +/* gs_watches contains a list of all addresses+len that are being watched. */ +static VgHashTable gs_watches = NULL; + + +/* protocol spec tells the below must be idempotent. */ +static void breakpoint (Bool insert, CORE_ADDR addr) +{ + GS_Address *g; + + g = VG_(HT_lookup) (gs_addresses, (UWord)addr); + if (insert) { + /* insert a breakpoint at addr or upgrade its kind */ + if (g == NULL) { + add_gs_address (addr, GS_break, "m_gdbserver breakpoint insert"); + } else { + /* already gdbserved. Normally, it must be because of a jump. + However, due to idempotent or if connection with gdb was + lost (kept breaks from the previous gdb), if already existing, + we just upgrade its kind. */ + g->kind = GS_break; + } + } else { + /* delete a breakpoint at addr or downgrade its kind */ + if (g != NULL && g->kind == GS_break) { + if (valgrind_single_stepping()) { + /* keep gdbserved instrumentation while single stepping */ + g->kind = GS_jump; + } else { + remove_gs_address (g, "m_gdbserver breakpoint remove"); + } + } else { + dlog (1, "remove break addr %p %s\n", + C2v(addr), (g == NULL ? + "NULL" : + (g->kind == GS_jump ? "GS_jump" : "GS_break"))); + } + } +} + +static Bool (*tool_watchpoint) (PointKind kind, + Bool insert, + Addr addr, + SizeT len) = NULL; +void VG_(needs_watchpoint) (Bool (*watchpoint) (PointKind kind, + Bool insert, + Addr addr, + SizeT len)) +{ + tool_watchpoint = watchpoint; +} + +Bool VG_(gdbserver_point) (PointKind kind, Bool insert, + CORE_ADDR addr, int len) +{ + Bool res; + GS_Watch *g; + Bool is_code = kind == software_breakpoint || kind == hardware_breakpoint; + + dlog(1, "%s %s at addr %p %s\n", + (insert ? "insert" : "remove"), + VG_(ppPointKind) (kind), + C2v(addr), + sym(addr, is_code)); + + if (is_code) { + breakpoint (insert, addr); + return True; + } + + vg_assert (kind == access_watchpoint + || kind == read_watchpoint + || kind == write_watchpoint); + + if (tool_watchpoint == NULL) + return False; + + res = (*tool_watchpoint) (kind, insert, addr, len); + if (!res) + return False; /* error or unsupported */ + + g = VG_(HT_lookup) (gs_watches, (UWord)addr); + if (insert) { + if (g == NULL) { + g = VG_(arena_malloc)(VG_AR_CORE, "gdbserver_point watchpoint", + sizeof(GS_Watch)); + g->addr = addr; + g->len = len; + g->kind = kind; + VG_(HT_add_node)(gs_watches, g); + } else { + g->kind = kind; + } + } else { + vg_assert (g != NULL); + VG_(HT_remove) (gs_watches, g->addr); + VG_(arena_free) (VG_AR_CORE, g); + } + return True; +} + +Bool VG_(is_watched)(PointKind kind, Addr addr, Int szB) +{ + GS_Watch* g; + Bool watched = False; + const ThreadId tid = VG_(running_tid); + + if (!gdbserver_called) + return False; + + Addr to = addr + szB; // semi-open interval [addr, to[ + + vg_assert (kind == access_watchpoint + || kind == read_watchpoint + || kind == write_watchpoint); + dlog(1, "tid %d VG_(is_watched) %s addr %p szB %d\n", + tid, VG_(ppPointKind) (kind), C2v(addr), szB); + VG_(HT_ResetIter) (gs_watches); + while ((g = VG_(HT_Next) (gs_watches))) { + switch (g->kind) { + case software_breakpoint: + case hardware_breakpoint: + break; + case access_watchpoint: + case read_watchpoint: + case write_watchpoint: + if (to <= g->addr || addr >= (g->addr + g->len)) + /* If no overlap, examine next watchpoint: */ + continue; + + watched = True; /* We have an overlap */ + + /* call gdbserver if access kind reported by the tool + matches the watchpoint kind. */ + if (kind == access_watchpoint + || g->kind == access_watchpoint + || g->kind == kind) { + /* Watchpoint encountered. + If this is a read watchpoint, we directly call gdbserver + to report it to gdb. + Otherwise, for a write watchpoint, we have to finish + the instruction so as to modify the value. + If we do not finish the instruction, then gdb sees no + value change and continues. + For a read watchpoint, we better call gdbserver directly: + in case the current block is not gdbserved, Valgrind + will execute instructions till the next block. */ + + /* set the watchpoint stop address to the first read or written. */ + if (g->addr <= addr) { + VG_(set_watchpoint_stop_address) (addr); + } else { + VG_(set_watchpoint_stop_address) (g->addr); + } + + if (kind == write_watchpoint) { + /* Let Valgrind stop as early as possible after this instruction + by switching to Single Stepping mode. */ + valgrind_set_single_stepping (True); + invalidate_current_ip (tid, "m_gdbserver write watchpoint"); + } else { + call_gdbserver (tid, watch_reason); + VG_(set_watchpoint_stop_address) ((Addr) 0); + } + return True; // we are watched here. + } + break; + default: + vg_assert (0); + } + } + return watched; +} + +/* Returns the reason for which gdbserver instrumentation is needed */ +static VgVgdb VG_(gdbserver_instrumentation_needed) (VexGuestExtents* vge) +{ + GS_Address* g; + int e; + + if (!gdbserver_called) + return Vg_VgdbNo; + + if (valgrind_single_stepping()) { + dlog(2, "gdbserver_instrumentation_needed due to single stepping\n"); + return Vg_VgdbYes; + } + + if (VG_(clo_vgdb) == Vg_VgdbYes && VG_(HT_count_nodes) (gs_addresses) == 0) + return Vg_VgdbNo; + + /* We assume we do not have a huge nr of breakpoints. + Otherwise, we need something more efficient e.g. + a sorted list of breakpoints or associate extents to it or ... + */ + VG_(HT_ResetIter) (gs_addresses); + while ((g = VG_(HT_Next) (gs_addresses))) { + for (e = 0; e < vge->n_used; e++) { + if (g->addr >= vge->base[e] && g->addr < vge->base[e] + vge->len[e]) { + dlog(2, + "gdbserver_instrumentation_needed %p %s reason %s\n", + C2v(g->addr), sym(g->addr, /* is_code */ True), + (g->kind == GS_jump ? "GS_jump" : "GS_break")); + return Vg_VgdbYes; + } + } + } + + if (VG_(clo_vgdb) == Vg_VgdbFull) { + dlog(4, "gdbserver_instrumentation_needed" + " due to VG_(clo_vgdb) == Vg_VgdbFull\n"); + return Vg_VgdbFull; + } + + + return Vg_VgdbNo; +} + +// Clear gdbserved_addresses in gs_addresses. +// If clear_only_jumps, clears only the addresses that are served +// for jump reasons. +// Otherwise, clear all the addresses. +// Cleared addresses are invalidated so as to have them re-translated. +static void clear_gdbserved_addresses(Bool clear_only_jumps) +{ + GS_Address** ag; + UInt n_elems; + int i; + + dlog(1, + "clear_gdbserved_addresses: scanning hash table nodes %d\n", + VG_(HT_count_nodes) (gs_addresses)); + ag = (GS_Address**) VG_(HT_to_array) (gs_addresses, &n_elems); + for (i = 0; i < n_elems; i++) + if (!clear_only_jumps || ag[i]->kind == GS_jump) + remove_gs_address (ag[i], "clear_gdbserved_addresses"); + VG_(free) (ag); +} + +// Clear watched addressed in gs_watches +static void clear_watched_addresses(void) +{ + GS_Watch** ag; + UInt n_elems; + int i; + + dlog(1, + "clear_watched_addresses: scanning hash table nodes %d\n", + VG_(HT_count_nodes) (gs_watches)); + ag = (GS_Watch**) VG_(HT_to_array) (gs_watches, &n_elems); + for (i = 0; i < n_elems; i++) { + if (!VG_(gdbserver_point) (ag[i]->kind, + /* insert */ False, + ag[i]->addr, + ag[i]->len)) { + vg_assert (0); + } + } + VG_(free) (ag); +} + +static void invalidate_if_jump_not_yet_gdbserved (Addr addr, char* from) +{ + if (VG_(HT_lookup) (gs_addresses, (UWord)addr)) + return; + add_gs_address (addr, GS_jump, from); +} + +static void invalidate_current_ip (ThreadId tid, char *who) +{ + invalidate_if_jump_not_yet_gdbserved (VG_(get_IP) (tid), who); +} + +/* when fork is done, various cleanup is needed in the child process. + In particular, child must have its own connection to avoid stealing + data from its parent */ +static void gdbserver_cleanup_in_child_after_fork(ThreadId me) +{ + dlog(1, "thread %d gdbserver_cleanup_in_child_after_fork pid %d\n", + me, VG_(getpid) ()); + + /* finish connection inheritated from parent */ + remote_finish(reset_after_fork); + + /* ensure next call to gdbserver will be considered as a brand + new call that will initialize a fresh gdbserver. */ + if (gdbserver_called) { + gdbserver_called = 0; + vg_assert (gs_addresses != NULL); + vg_assert (gs_watches != NULL); + clear_gdbserved_addresses(/* clear only jumps */ False); + VG_(HT_destruct) (gs_addresses); + gs_addresses = NULL; + clear_watched_addresses(); + VG_(HT_destruct) (gs_watches); + gs_watches = NULL; + } else { + vg_assert (gs_addresses == NULL); + vg_assert (gs_watches == NULL); + } +} + +/* If reason is init_reason, creates the connection resources (e.g. + the FIFOs) to allow a gdb connection to be detected by polling + using remote_desc_activity. + Otherwise (other reasons): + If connection with gdb not yet opened, opens the connection with gdb. + reads gdb remote protocol packets and executes the requested commands. +*/ +static void call_gdbserver ( ThreadId tid , CallReason reason) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int stepping; + Addr saved_pc; + + dlog(1, + "entering call_gdbserver %s ... pid %d tid %d status %s " + "sched_jmpbuf_valid %d\n", + ppCallReason (reason), + VG_(getpid) (), tid, VG_(name_of_ThreadStatus)(tst->status), + tst->sched_jmpbuf_valid); + + vg_assert(VG_(is_valid_tid)(tid)); + saved_pc = VG_(get_IP) (tid); + + if (gdbserver_exited) { + dlog(0, "call_gdbserver called when gdbserver_exited %d\n", + gdbserver_exited); + return; + } + + if (gdbserver_called == 0) { + vg_assert (gs_addresses == NULL); + vg_assert (gs_watches == NULL); + gs_addresses = VG_(HT_construct)( "gdbserved_addresses" ); + gs_watches = VG_(HT_construct)( "gdbserved_watches" ); + VG_(atfork)(NULL, NULL, gdbserver_cleanup_in_child_after_fork); + } + vg_assert (gs_addresses != NULL); + vg_assert (gs_watches != NULL); + + gdbserver_called++; + + /* call gdbserver_init if this is the first call to gdbserver. */ + if (gdbserver_called == 1) + gdbserver_init(); + + if (reason == init_reason || gdbserver_called == 1) + remote_open(VG_(clo_vgdb_prefix)); + + /* if the call reason is to initialize, then return control to + valgrind. After this initialization, gdbserver will be called + again either if there is an error detected by valgrind or + if vgdb sends data to the valgrind process. */ + if (reason == init_reason) { + return; + } + + stepping = valgrind_single_stepping(); + + server_main(); + + ignore_this_break_once = valgrind_get_ignore_break_once(); + if (ignore_this_break_once) + dlog(1, "!!! will ignore_this_break_once %s\n", + sym(ignore_this_break_once, /* is_code */ True)); + + + if (valgrind_single_stepping()) { + /* we are single stepping. If we were not stepping on entry, + then invalidate the current program counter so as to properly + do single step. In case the program counter was changed by + gdb, this will also invalidate the target address we will + jump to. */ + if (!stepping && tid != 0) { + invalidate_current_ip (tid, "m_gdbserver single step"); + } + } else { + /* We are not single stepping. If we were stepping on entry, + then clear the gdbserved addresses. This will cause all + these gdbserved blocks to be invalidated so that they can be + re-translated without being gdbserved. */ + if (stepping) + clear_gdbserved_addresses(/* clear only jumps */ True); + } + + /* can't do sanity check at beginning. At least the stack + check is not yet possible. */ + if (gdbserver_called > 1) + VG_(sanity_check_general) (/* force_expensive */ False); + + /* If the PC has been changed by gdb, then we VG_MINIMAL_LONGJMP to + the scheduler to execute the block of the new PC. + Otherwise we just return to continue executing the + current block. */ + if (VG_(get_IP) (tid) != saved_pc) { + dlog(1, "tid %d %s PC changed from %s to %s\n", + tid, VG_(name_of_ThreadStatus) (tst->status), + sym(saved_pc, /* is_code */ True), + sym(VG_(get_IP) (tid), /* is_code */ True)); + if (tst->status == VgTs_Yielding) { + SysRes sres; + VG_(memset)(&sres, 0, sizeof(SysRes)); + VG_(acquire_BigLock)(tid, "gdbsrv VG_MINIMAL_LONGJMP"); + } + if (tst->sched_jmpbuf_valid) { + /* resume scheduler */ + VG_MINIMAL_LONGJMP(tst->sched_jmpbuf); + } + /* else continue to run */ + } + /* continue to run */ +} + +/* busy > 0 when gdbserver is currently being called. + busy is used to to avoid vgdb invoking gdbserver + while gdbserver by Valgrind. */ +static volatile int busy = 0; + +void VG_(gdbserver) ( ThreadId tid ) +{ + busy++; + /* called by the rest of valgrind for + --vgdb-error=0 reason + or by scheduler "poll/debug/interrupt" reason + or to terminate. */ + if (tid != 0) { + call_gdbserver (tid, core_reason); + } else { + if (gdbserver_called == 0) { + dlog(1, "VG_(gdbserver) called to terminate, nothing to terminate\n"); + } else if (gdbserver_exited) { + dlog(0, "VG_(gdbserver) called to terminate again %d\n", + gdbserver_exited); + } else { + gdbserver_terminate(); + gdbserver_exited++; + } + } + busy--; +} + +// nr of invoke_gdbserver while gdbserver is already executing. +static int interrupts_while_busy = 0; + +// nr of invoke_gdbserver while gdbserver is not executing. +static int interrupts_non_busy = 0; + +// nr of invoke_gdbserver when some threads are not interruptible. +static int interrupts_non_interruptible = 0; + +/* When all threads are blocked in a system call, the Valgrind + scheduler cannot poll the shared memory for gdbserver activity. In + such a case, vgdb will force the invokation of gdbserver using + ptrace. To do that, vgdb 'pushes' a call to invoke_gdbserver + on the stack using ptrace. invoke_gdbserver must not return. + Instead, it must call give_control_back_to_vgdb. + vgdb expects to receive a SIGTRAP, which this function generates. + When vgdb gets this SIGTRAP, it knows invoke_gdbserver call + is finished and can reset the Valgrind process in the state prior to + the 'pushed call' (using ptrace again). + This all works well. However, the user must avoid + 'kill-9ing' vgdb during such a pushed call, otherwise + the SIGTRAP generated below will be seen by the Valgrind core, + instead of being handled by vgdb. When the Valgrind core gets + such a SIGTRAP, it will assert. */ + +static void give_control_back_to_vgdb(void) +{ + /* cause a SIGTRAP to be sent to ourself, so that vgdb takes control. + vgdb will then restore the stack so as to resume the activity + before the ptrace (typically do_syscall_WRK). */ + if (VG_(kill)(VG_(getpid)(), VKI_SIGTRAP) != 0) + vg_assert2(0, "SIGTRAP for vgdb could not be generated\n"); + + /* If we arrive here, it means a call was pushed on the stack + by vgdb, but during this call, vgdb and/or connection + died. Alternatively, it is a bug in the vgdb<=>Valgrind gdbserver + ptrace handling. */ + vg_assert2(0, + "vgdb did not took control. Did you kill vgdb ?\n" + "busy %d vgdb_interrupted_tid %d\n", + busy, vgdb_interrupted_tid); +} + +/* Using ptrace calls, vgdb will force an invocation of gdbserver. + VG_(invoke_gdbserver) is the entry point called through the + vgdb ptrace technique. */ +void VG_(invoke_gdbserver) ( int check ) +{ + /* ******* Avoid non-reentrant function call from here ..... + till the ".... till here" below. */ + + /* We need to determine the state of the various threads to decide + if we directly invoke gdbserver or if we rather indicate to the + scheduler to invoke the gdbserver. To decide that, it is + critical to avoid any "coregrind" function call as the ptrace + might have stopped the process in the middle of this (possibly) + non-rentrant function. So, it is only when all threads are in + an "interruptible" state that we can safely invoke + gdbserver. Otherwise, we let the valgrind scheduler invoke + gdbserver at the next poll. This poll will be made very soon + thanks to a call to VG_(force_vgdb_poll). */ + int n_tid; + + vg_assert (check == 0x8BADF00D); + + if (busy) { + interrupts_while_busy++; + give_control_back_to_vgdb(); + } + interrupts_non_busy++; + + /* check if all threads are in an "interruptible" state. If yes, + we invoke gdbserver. Otherwise, we tell the scheduler to wake up + asap. */ + for (n_tid = 1; n_tid < VG_N_THREADS; n_tid++) { + switch (VG_(threads)[n_tid].status) { + /* interruptible states. */ + case VgTs_WaitSys: + case VgTs_Yielding: + if (vgdb_interrupted_tid == 0) vgdb_interrupted_tid = n_tid; + break; + + case VgTs_Empty: + case VgTs_Zombie: + break; + + /* non interruptible states. */ + case VgTs_Init: + case VgTs_Runnable: + interrupts_non_interruptible++; + VG_(force_vgdb_poll) (); + give_control_back_to_vgdb(); + + default: vg_assert(0); + } + } + + /* .... till here. + From here onwards, function calls are ok: it is + safe to call valgrind core functions: all threads are blocked in + a system call or are yielding or ... */ + dlog(1, "invoke_gdbserver running_tid %d vgdb_interrupted_tid %d\n", + VG_(running_tid), vgdb_interrupted_tid); + call_gdbserver (vgdb_interrupted_tid, vgdb_reason); + vgdb_interrupted_tid = 0; + dlog(1, + "exit invoke_gdbserver running_tid %d\n", VG_(running_tid)); + give_control_back_to_vgdb(); + + vg_assert2(0, "end of invoke_gdbserver reached"); + +} + +Bool VG_(gdbserver_activity) (ThreadId tid) +{ + Bool ret; + busy++; + if (!gdbserver_called) + call_gdbserver (tid, init_reason); + switch (remote_desc_activity("VG_(gdbserver_activity)")) { + case 0: ret = False; break; + case 1: ret = True; break; + case 2: call_gdbserver (tid, init_reason); ret = False; break; + default: vg_assert (0); + } + busy--; + return ret; +} + +Bool VG_(gdbserver_report_signal) (Int sigNo, ThreadId tid) +{ + dlog(1, "signal %d tid %d\n", sigNo, tid); + + /* if gdbserver is currently not connected, then signal + is to be given to the process */ + if (!remote_connected()) { + dlog(1, "not connected => pass\n"); + return True; + } + if (pass_signals[sigNo]) { + dlog(1, "pass_signals => pass\n"); + return False; + } + + /* indicate to gdbserver that there is a signal */ + gdbserver_signal_encountered (sigNo); + + /* let gdbserver do some work, e.g. show the signal to the user */ + call_gdbserver (tid, signal_reason); + + /* ask gdbserver what is the final decision */ + if (gdbserver_deliver_signal (sigNo)) { + dlog(1, "gdbserver deliver signal\n"); + return True; + } else { + dlog(1, "gdbserver ignore signal\n"); + return False; + } +} + +// Check if single_stepping or if there is a break requested at iaddr. +// If yes, call debugger +VG_REGPARM(1) +void VG_(helperc_CallDebugger) ( HWord iaddr ) +{ + GS_Address* g; + + // For Vg_VgdbFull, after a fork, we might have calls to this helper + // while gdbserver is not yet initialized. + if (!gdbserver_called) + return; + + if (valgrind_single_stepping() || + ((g = VG_(HT_lookup) (gs_addresses, (UWord)iaddr)) && + (g->kind == GS_break))) { + if (iaddr == ignore_this_break_once) { + dlog(1, "ignoring ignore_this_break_once %s\n", + sym(ignore_this_break_once, /* is_code */ True)); + ignore_this_break_once = 0; + } else { + call_gdbserver (VG_(get_running_tid)(), break_reason); + } + } +} + +/* software_breakpoint support --------------------------------------*/ +/* When a block is instrumented for gdbserver, single step and breaks + will be obeyed in this block. However, if a jump to another block + is executed while single_stepping is active, we must ensure that + this block is also instrumented. For this, when a block is + instrumented for gdbserver while single_stepping, the target of all + the Jump instructions in this block will be checked to verify if + the block is already instrumented for gdbserver. The below will + ensure that if not already instrumented for gdbserver, the target + block translation containing addr will be invalidated. The list of + gdbserved Addr will also be kept so that translations can be + dropped automatically by gdbserver when going out of single step + mode. + + Call the below at translation time if the jump target is a constant. + Otherwise, rather use VG_(add_stmt_call_invalidate_if_not_gdbserved). + + To instrument the target exit statement, you can call + VG_(add_stmt_call_invalidate_exit_target_if_not_gdbserved) rather + than check the kind of target exit. */ +static void VG_(invalidate_if_not_gdbserved) (Addr addr) +{ + if (valgrind_single_stepping()) + invalidate_if_jump_not_yet_gdbserved + (addr, "gdbserver target jump (instrument)"); +} + +// same as VG_(invalidate_if_not_gdbserved) but is intended to be called +// at runtime (only difference is the invalidate reason which traces +// it is at runtime) +VG_REGPARM(1) +void VG_(helperc_invalidate_if_not_gdbserved) ( Addr addr ) +{ + if (valgrind_single_stepping()) + invalidate_if_jump_not_yet_gdbserved + (addr, "gdbserver target jump (runtime)"); +} + +static void VG_(add_stmt_call_invalidate_if_not_gdbserved) + ( IRSB* sb_in, + VexGuestLayout* layout, + VexGuestExtents* vge, + IRTemp jmp, + IRSB* irsb) +{ + + void* fn; + HChar* nm; + IRExpr** args; + Int nargs; + IRDirty* di; + + fn = &VG_(helperc_invalidate_if_not_gdbserved); + nm = "VG_(helperc_invalidate_if_not_gdbserved)"; + args = mkIRExprVec_1(IRExpr_RdTmp (jmp)); + nargs = 1; + + di = unsafeIRDirty_0_N( nargs/*regparms*/, nm, + VG_(fnptr_to_fnentry)( fn ), args ); + + di->nFxState = 0; + + addStmtToIRSB(irsb, IRStmt_Dirty(di)); +} + +/* software_breakpoint support --------------------------------------*/ +/* If a tool wants to allow gdbserver to do something at Addr, then + VG_(add_stmt_call_gdbserver) will add in IRSB a call to a helper + function. This helper function will check if the process must be + stopped at the instruction Addr: either there is a break at Addr or + the process is being single-stepped. Typical usage of the below is to + instrument an Ist_IMark to allow the debugger to interact at any + instruction being executed. As soon as there is one break in a block, + then to allow single stepping in this block (and possible insertions + of other breaks in the same sb_in while the process is stopped), a + debugger statement will be inserted for all instructions of a block. */ +static void VG_(add_stmt_call_gdbserver) + (IRSB* sb_in, /* block being translated */ + VexGuestLayout* layout, + VexGuestExtents* vge, + IRType gWordTy, IRType hWordTy, + Addr iaddr, /* Addr of instruction being instrumented */ + IRSB* irsb) /* irsb block to which call is added */ +{ + void* fn; + HChar* nm; + IRExpr** args; + Int nargs; + IRDirty* di; + + /* first store the address in the program counter so that the check + done by VG_(helperc_CallDebugger) will be based on the correct + program counter. We might make this more efficient by rather + searching for assignement to program counter and instrumenting + that but the below is easier and I guess that the optimiser will + remove the redundant store. And in any case, when debugging a + piece of code, the efficiency requirement is not critical: very + few blocks will be instrumented for debugging. */ + + addStmtToIRSB(irsb, IRStmt_Put(layout->offset_IP , mkIRExpr_HWord(iaddr))); + + fn = &VG_(helperc_CallDebugger); + nm = "VG_(helperc_CallDebugger)"; + args = mkIRExprVec_1(mkIRExpr_HWord (iaddr)); + nargs = 1; + + di = unsafeIRDirty_0_N( nargs/*regparms*/, nm, + VG_(fnptr_to_fnentry)( fn ), args ); + + /* Note: in fact, a debugger call can read whatever register + or memory. It can also write whatever register or memory. + So, in theory, we have to indicate the whole universe + can be read and modified. It is however not critical + to indicate precisely what is being read/written + as such indications are needed for tool error detection + and we do not want to have errors being detected for + gdb interactions. */ + + di->nFxState = 2; + di->fxState[0].fx = Ifx_Read; + di->fxState[0].offset = layout->offset_SP; + di->fxState[0].size = layout->sizeof_SP; + di->fxState[1].fx = Ifx_Modify; + di->fxState[1].offset = layout->offset_IP; + di->fxState[1].size = layout->sizeof_IP; + + addStmtToIRSB(irsb, IRStmt_Dirty(di)); + +} + + +/* Invalidate the target of the exit if needed: + If target is constant, it is invalidated at translation time. + Otherwise, a call to a helper function is generated to invalidate + the translation at run time. + The below is thus calling either VG_(invalidate_if_not_gdbserved) + or VG_(add_stmt_call_invalidate_if_not_gdbserved). */ +static void VG_(add_stmt_call_invalidate_exit_target_if_not_gdbserved) + (IRSB* sb_in, + VexGuestLayout* layout, + VexGuestExtents* vge, + IRType gWordTy, + IRSB* irsb) +{ + if (sb_in->next->tag == Iex_Const) { + VG_(invalidate_if_not_gdbserved) (gWordTy == Ity_I64 ? + sb_in->next->Iex.Const.con->Ico.U64 + : sb_in->next->Iex.Const.con->Ico.U32); + } else if (sb_in->next->tag == Iex_RdTmp) { + VG_(add_stmt_call_invalidate_if_not_gdbserved) + (sb_in, layout, vge, sb_in->next->Iex.RdTmp.tmp, irsb); + } else { + vg_assert (0); /* unexpected expression tag in exit. */ + } +} + +IRSB* VG_(instrument_for_gdbserver_if_needed) + (IRSB* sb_in, + VexGuestLayout* layout, + VexGuestExtents* vge, + IRType gWordTy, IRType hWordTy) +{ + IRSB* sb_out; + Int i; + const VgVgdb instr_needed = VG_(gdbserver_instrumentation_needed) (vge); + + if (instr_needed == Vg_VgdbNo) + return sb_in; + + + /* here, we need to instrument for gdbserver */ + sb_out = deepCopyIRSBExceptStmts(sb_in); + + for (i = 0; i < sb_in->stmts_used; i++) { + IRStmt* st = sb_in->stmts[i]; + + if (!st || st->tag == Ist_NoOp) continue; + + if (st->tag == Ist_Exit && instr_needed == Vg_VgdbYes) { + VG_(invalidate_if_not_gdbserved) + (hWordTy == Ity_I64 ? + st->Ist.Exit.dst->Ico.U64 : + st->Ist.Exit.dst->Ico.U32); + } + addStmtToIRSB( sb_out, st ); + if (st->tag == Ist_IMark) { + /* For an Ist_Mark, add a call to debugger. */ + switch (instr_needed) { + case Vg_VgdbNo: vg_assert (0); + case Vg_VgdbYes: + case Vg_VgdbFull: + VG_(add_stmt_call_gdbserver) ( sb_in, layout, vge, + gWordTy, hWordTy, + st->Ist.IMark.addr, + sb_out); + /* There is an optimisation possible here for Vg_VgdbFull: + Put a guard ensuring we only call gdbserver if 'FullCallNeeded'. + FullCallNeeded would be set to 1 we have just switched on + Single Stepping or have just encountered a watchpoint + or have just inserted a breakpoint. + (as gdb by default removes and re-insert breakpoints), we would + need to also implement the notion of 'breakpoint pending removal' + to remove at the next 'continue/step' packet. */ + break; + default: vg_assert (0); + } + } + } + + if (instr_needed == Vg_VgdbYes) { + VG_(add_stmt_call_invalidate_exit_target_if_not_gdbserved) (sb_in, + layout, vge, + gWordTy, + sb_out); + } + + return sb_out; +} + +struct mon_out_buf { + char buf[DATASIZ+1]; + int next; + UInt ret; +}; + +static void mon_out (HChar c, void *opaque) +{ + struct mon_out_buf *b = (struct mon_out_buf *) opaque; + b->ret++; + b->buf[b->next] = c; + b->next++; + if (b->next == DATASIZ) { + b->buf[b->next] = '\0'; + monitor_output(b->buf); + b->next = 0; + } +} +UInt VG_(gdb_printf) ( const HChar *format, ... ) +{ + struct mon_out_buf b; + + b.next = 0; + b.ret = 0; + + va_list vargs; + va_start(vargs, format); + VG_(vcbprintf) (mon_out, &b, format, vargs); + va_end(vargs); + + if (b.next > 0) { + b.buf[b.next] = '\0'; + monitor_output(b.buf); + } + return b.ret; +} + +Int VG_(keyword_id) (Char* keywords, Char* input_word, kwd_report_error report) +{ + const Int il = (input_word == NULL ? 0 : VG_(strlen) (input_word)); + Char iw[il+1]; + Char kwds[VG_(strlen)(keywords)+1]; + Char *kwdssaveptr; + + Char* kw; /* current keyword, its length, its position */ + Int kwl; + Int kpos = -1; + + Int pass; + /* pass 0 = search, optional pass 1 = output message multiple matches */ + + Int pass1needed = 0; + + Int partial_match = -1; + Int full_match = -1; + + if (input_word == NULL) { + iw[0] = 0; + partial_match = 0; /* to force an empty string to cause an error */ + } else { + VG_(strcpy) (iw, input_word); + } + + for (pass = 0; pass < 2; pass++) { + VG_(strcpy) (kwds, keywords); + if (pass == 1) + VG_(gdb_printf) ("%s can match", + (il == 0 ? "<empty string>" : (char *) iw)); + for (kw = VG_(strtok_r) (kwds, " ", &kwdssaveptr); + kw != NULL; + kw = VG_(strtok_r) (NULL, " ", &kwdssaveptr)) { + kwl = VG_(strlen) (kw); + kpos++; + + if (il > kwl) { + ; /* ishtar !~ is */ + } else if (il == kwl) { + if (VG_(strcmp) (kw, iw) == 0) { + /* exact match */ + if (pass == 1) + VG_(gdb_printf) (" %s", kw); + if (full_match != -1) + pass1needed++; + full_match = kpos; + } + } else { + /* il < kwl */ + if (VG_(strncmp) (iw, kw, il) == 0) { + /* partial match */ + if (pass == 1) + VG_(gdb_printf) (" %s", kw); + if (partial_match != -1) + pass1needed++; + partial_match = kpos; + } + } + } + /* check for success or for no match at all */ + if (pass1needed == 0) { + if (full_match != -1) { + return full_match; + } else { + if (report == kwd_report_all && partial_match == -1) { + VG_(gdb_printf) ("%s does not match any of '%s'\n", + iw, keywords); + } + return partial_match; + } + } + + /* here we have duplicated match error */ + if (pass == 1 || report == kwd_report_none) { + if (report != kwd_report_none) { + VG_(gdb_printf) ("\n"); + } + if (partial_match != -1 || full_match != -1) + return -2; + else + return -1; + } + } + /* UNREACHED */ + vg_assert (0); +} + +/* True if string can be a 0x number */ +static Bool is_zero_x (Char *s) +{ + if (strlen (s) >= 3 && s[0] == '0' && s[1] == 'x') + return True; + else + return False; +} + +/* True if string can be a 0b number */ +static Bool is_zero_b (Char *s) +{ + if (strlen (s) >= 3 && s[0] == '0' && s[1] == 'b') + return True; + else + return False; +} + +void VG_(strtok_get_address_and_size) (Addr* address, + SizeT* szB, + Char **ssaveptr) +{ + Char* wa; + Char* ws; + Char* endptr; + UChar *ppc; + + wa = VG_(strtok_r) (NULL, " ", ssaveptr); + ppc = wa; + if (ppc == NULL || !VG_(parse_Addr) (&ppc, address)) { + VG_(gdb_printf) ("missing or malformed address\n"); + *address = (Addr) 0; + *szB = 0; + return; + } + ws = VG_(strtok_r) (NULL, " ", ssaveptr); + if (ws == NULL) { + /* Do nothing, i.e. keep current value of szB. */ ; + } else if (is_zero_x (ws)) { + *szB = VG_(strtoull16) (ws, &endptr); + } else if (is_zero_b (ws)) { + Int j; + Char *parsews = ws; + Int n_bits = VG_(strlen) (ws) - 2; + *szB = 0; + ws = NULL; // assume the below loop gives a correct nr. + for (j = 0; j < n_bits; j++) { + if ('0' == parsews[j+2]) { /* do nothing */ } + else if ('1' == parsews[j+2]) *szB |= (1 << (n_bits-j-1)); + else { + /* report malformed binary integer */ + ws = parsews; + endptr = ws + j + 2; + break; + } + } + } else { + *szB = VG_(strtoull10) (ws, &endptr); + } + + if (ws != NULL && *endptr != '\0') { + VG_(gdb_printf) ("malformed integer, expecting " + "hex 0x..... or dec ...... or binary .....b\n"); + *address = (Addr) 0; + *szB = 0; + return; + } +} + +void VG_(gdbserver_status_output)(void) +{ + const int nr_gdbserved_addresses + = (gs_addresses == NULL ? -1 : VG_(HT_count_nodes) (gs_addresses)); + const int nr_watchpoints + = (gs_watches == NULL ? -1 : VG_(HT_count_nodes) (gs_watches)); + remote_utils_output_status(); + VG_(umsg) + ("nr of calls to gdbserver: %d\n" + "single stepping %d\n" + "interrupts intr_tid %d gs_non_busy %d gs_busy %d tid_non_intr %d\n" + "gdbserved addresses %d (-1 = not initialized)\n" + "watchpoints %d (-1 = not initialized)\n" + "vgdb-error %d\n", + gdbserver_called, + valgrind_single_stepping(), + + vgdb_interrupted_tid, + interrupts_non_busy, + interrupts_while_busy, + interrupts_non_interruptible, + + nr_gdbserved_addresses, + nr_watchpoints, + VG_(dyn_vgdb_error)); +} diff --git a/coregrind/m_gdbserver/power-altivec-valgrind-s1.xml b/coregrind/m_gdbserver/power-altivec-valgrind-s1.xml new file mode 100644 index 00000000..8073622a --- /dev/null +++ b/coregrind/m_gdbserver/power-altivec-valgrind-s1.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.altivec-valgrind-s1"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v16i8" type="int8" count="16"/> + <union id="vec128"> + <field name="uint128" type="uint128"/> + <field name="v4_float" type="v4f"/> + <field name="v4_int32" type="v4i32"/> + <field name="v8_int16" type="v8i16"/> + <field name="v16_int8" type="v16i8"/> + </union> + + <reg name="vr0s1" bitsize="128" type="vec128"/> + <reg name="vr1s1" bitsize="128" type="vec128"/> + <reg name="vr2s1" bitsize="128" type="vec128"/> + <reg name="vr3s1" bitsize="128" type="vec128"/> + <reg name="vr4s1" bitsize="128" type="vec128"/> + <reg name="vr5s1" bitsize="128" type="vec128"/> + <reg name="vr6s1" bitsize="128" type="vec128"/> + <reg name="vr7s1" bitsize="128" type="vec128"/> + <reg name="vr8s1" bitsize="128" type="vec128"/> + <reg name="vr9s1" bitsize="128" type="vec128"/> + <reg name="vr10s1" bitsize="128" type="vec128"/> + <reg name="vr11s1" bitsize="128" type="vec128"/> + <reg name="vr12s1" bitsize="128" type="vec128"/> + <reg name="vr13s1" bitsize="128" type="vec128"/> + <reg name="vr14s1" bitsize="128" type="vec128"/> + <reg name="vr15s1" bitsize="128" type="vec128"/> + <reg name="vr16s1" bitsize="128" type="vec128"/> + <reg name="vr17s1" bitsize="128" type="vec128"/> + <reg name="vr18s1" bitsize="128" type="vec128"/> + <reg name="vr19s1" bitsize="128" type="vec128"/> + <reg name="vr20s1" bitsize="128" type="vec128"/> + <reg name="vr21s1" bitsize="128" type="vec128"/> + <reg name="vr22s1" bitsize="128" type="vec128"/> + <reg name="vr23s1" bitsize="128" type="vec128"/> + <reg name="vr24s1" bitsize="128" type="vec128"/> + <reg name="vr25s1" bitsize="128" type="vec128"/> + <reg name="vr26s1" bitsize="128" type="vec128"/> + <reg name="vr27s1" bitsize="128" type="vec128"/> + <reg name="vr28s1" bitsize="128" type="vec128"/> + <reg name="vr29s1" bitsize="128" type="vec128"/> + <reg name="vr30s1" bitsize="128" type="vec128"/> + <reg name="vr31s1" bitsize="128" type="vec128"/> + + <reg name="vscrs1" bitsize="32" group="vector"/> + <reg name="vrsaves1" bitsize="32" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/power-altivec-valgrind-s2.xml b/coregrind/m_gdbserver/power-altivec-valgrind-s2.xml new file mode 100644 index 00000000..fe3a427a --- /dev/null +++ b/coregrind/m_gdbserver/power-altivec-valgrind-s2.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.altivec-valgrind-s2"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v16i8" type="int8" count="16"/> + <union id="vec128"> + <field name="uint128" type="uint128"/> + <field name="v4_float" type="v4f"/> + <field name="v4_int32" type="v4i32"/> + <field name="v8_int16" type="v8i16"/> + <field name="v16_int8" type="v16i8"/> + </union> + + <reg name="vr0s2" bitsize="128" type="vec128"/> + <reg name="vr1s2" bitsize="128" type="vec128"/> + <reg name="vr2s2" bitsize="128" type="vec128"/> + <reg name="vr3s2" bitsize="128" type="vec128"/> + <reg name="vr4s2" bitsize="128" type="vec128"/> + <reg name="vr5s2" bitsize="128" type="vec128"/> + <reg name="vr6s2" bitsize="128" type="vec128"/> + <reg name="vr7s2" bitsize="128" type="vec128"/> + <reg name="vr8s2" bitsize="128" type="vec128"/> + <reg name="vr9s2" bitsize="128" type="vec128"/> + <reg name="vr10s2" bitsize="128" type="vec128"/> + <reg name="vr11s2" bitsize="128" type="vec128"/> + <reg name="vr12s2" bitsize="128" type="vec128"/> + <reg name="vr13s2" bitsize="128" type="vec128"/> + <reg name="vr14s2" bitsize="128" type="vec128"/> + <reg name="vr15s2" bitsize="128" type="vec128"/> + <reg name="vr16s2" bitsize="128" type="vec128"/> + <reg name="vr17s2" bitsize="128" type="vec128"/> + <reg name="vr18s2" bitsize="128" type="vec128"/> + <reg name="vr19s2" bitsize="128" type="vec128"/> + <reg name="vr20s2" bitsize="128" type="vec128"/> + <reg name="vr21s2" bitsize="128" type="vec128"/> + <reg name="vr22s2" bitsize="128" type="vec128"/> + <reg name="vr23s2" bitsize="128" type="vec128"/> + <reg name="vr24s2" bitsize="128" type="vec128"/> + <reg name="vr25s2" bitsize="128" type="vec128"/> + <reg name="vr26s2" bitsize="128" type="vec128"/> + <reg name="vr27s2" bitsize="128" type="vec128"/> + <reg name="vr28s2" bitsize="128" type="vec128"/> + <reg name="vr29s2" bitsize="128" type="vec128"/> + <reg name="vr30s2" bitsize="128" type="vec128"/> + <reg name="vr31s2" bitsize="128" type="vec128"/> + + <reg name="vscrs2" bitsize="32" group="vector"/> + <reg name="vrsaves2" bitsize="32" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/power-altivec.xml b/coregrind/m_gdbserver/power-altivec.xml new file mode 100644 index 00000000..45d31afa --- /dev/null +++ b/coregrind/m_gdbserver/power-altivec.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.altivec"> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v16i8" type="int8" count="16"/> + <union id="vec128"> + <field name="uint128" type="uint128"/> + <field name="v4_float" type="v4f"/> + <field name="v4_int32" type="v4i32"/> + <field name="v8_int16" type="v8i16"/> + <field name="v16_int8" type="v16i8"/> + </union> + + <reg name="vr0" bitsize="128" type="vec128"/> + <reg name="vr1" bitsize="128" type="vec128"/> + <reg name="vr2" bitsize="128" type="vec128"/> + <reg name="vr3" bitsize="128" type="vec128"/> + <reg name="vr4" bitsize="128" type="vec128"/> + <reg name="vr5" bitsize="128" type="vec128"/> + <reg name="vr6" bitsize="128" type="vec128"/> + <reg name="vr7" bitsize="128" type="vec128"/> + <reg name="vr8" bitsize="128" type="vec128"/> + <reg name="vr9" bitsize="128" type="vec128"/> + <reg name="vr10" bitsize="128" type="vec128"/> + <reg name="vr11" bitsize="128" type="vec128"/> + <reg name="vr12" bitsize="128" type="vec128"/> + <reg name="vr13" bitsize="128" type="vec128"/> + <reg name="vr14" bitsize="128" type="vec128"/> + <reg name="vr15" bitsize="128" type="vec128"/> + <reg name="vr16" bitsize="128" type="vec128"/> + <reg name="vr17" bitsize="128" type="vec128"/> + <reg name="vr18" bitsize="128" type="vec128"/> + <reg name="vr19" bitsize="128" type="vec128"/> + <reg name="vr20" bitsize="128" type="vec128"/> + <reg name="vr21" bitsize="128" type="vec128"/> + <reg name="vr22" bitsize="128" type="vec128"/> + <reg name="vr23" bitsize="128" type="vec128"/> + <reg name="vr24" bitsize="128" type="vec128"/> + <reg name="vr25" bitsize="128" type="vec128"/> + <reg name="vr26" bitsize="128" type="vec128"/> + <reg name="vr27" bitsize="128" type="vec128"/> + <reg name="vr28" bitsize="128" type="vec128"/> + <reg name="vr29" bitsize="128" type="vec128"/> + <reg name="vr30" bitsize="128" type="vec128"/> + <reg name="vr31" bitsize="128" type="vec128"/> + + <reg name="vscr" bitsize="32" group="vector"/> + <reg name="vrsave" bitsize="32" group="vector"/> +</feature> diff --git a/coregrind/m_gdbserver/power-core.xml b/coregrind/m_gdbserver/power-core.xml new file mode 100644 index 00000000..9acb3ad5 --- /dev/null +++ b/coregrind/m_gdbserver/power-core.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.core"> + <reg name="r0" bitsize="32" type="uint32"/> + <reg name="r1" bitsize="32" type="uint32"/> + <reg name="r2" bitsize="32" type="uint32"/> + <reg name="r3" bitsize="32" type="uint32"/> + <reg name="r4" bitsize="32" type="uint32"/> + <reg name="r5" bitsize="32" type="uint32"/> + <reg name="r6" bitsize="32" type="uint32"/> + <reg name="r7" bitsize="32" type="uint32"/> + <reg name="r8" bitsize="32" type="uint32"/> + <reg name="r9" bitsize="32" type="uint32"/> + <reg name="r10" bitsize="32" type="uint32"/> + <reg name="r11" bitsize="32" type="uint32"/> + <reg name="r12" bitsize="32" type="uint32"/> + <reg name="r13" bitsize="32" type="uint32"/> + <reg name="r14" bitsize="32" type="uint32"/> + <reg name="r15" bitsize="32" type="uint32"/> + <reg name="r16" bitsize="32" type="uint32"/> + <reg name="r17" bitsize="32" type="uint32"/> + <reg name="r18" bitsize="32" type="uint32"/> + <reg name="r19" bitsize="32" type="uint32"/> + <reg name="r20" bitsize="32" type="uint32"/> + <reg name="r21" bitsize="32" type="uint32"/> + <reg name="r22" bitsize="32" type="uint32"/> + <reg name="r23" bitsize="32" type="uint32"/> + <reg name="r24" bitsize="32" type="uint32"/> + <reg name="r25" bitsize="32" type="uint32"/> + <reg name="r26" bitsize="32" type="uint32"/> + <reg name="r27" bitsize="32" type="uint32"/> + <reg name="r28" bitsize="32" type="uint32"/> + <reg name="r29" bitsize="32" type="uint32"/> + <reg name="r30" bitsize="32" type="uint32"/> + <reg name="r31" bitsize="32" type="uint32"/> + + <reg name="pc" bitsize="32" type="code_ptr" regnum="64"/> + <reg name="msr" bitsize="32" type="uint32"/> + <reg name="cr" bitsize="32" type="uint32"/> + <reg name="lr" bitsize="32" type="code_ptr"/> + <reg name="ctr" bitsize="32" type="uint32"/> + <reg name="xer" bitsize="32" type="uint32"/> +</feature> diff --git a/coregrind/m_gdbserver/power-fpu-valgrind-s1.xml b/coregrind/m_gdbserver/power-fpu-valgrind-s1.xml new file mode 100644 index 00000000..01b852e8 --- /dev/null +++ b/coregrind/m_gdbserver/power-fpu-valgrind-s1.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.fpu-valgrind-s1"> + <reg name="f0s1" bitsize="64" type="ieee_double" regnum="32"/> + <reg name="f1s1" bitsize="64" type="ieee_double"/> + <reg name="f2s1" bitsize="64" type="ieee_double"/> + <reg name="f3s1" bitsize="64" type="ieee_double"/> + <reg name="f4s1" bitsize="64" type="ieee_double"/> + <reg name="f5s1" bitsize="64" type="ieee_double"/> + <reg name="f6s1" bitsize="64" type="ieee_double"/> + <reg name="f7s1" bitsize="64" type="ieee_double"/> + <reg name="f8s1" bitsize="64" type="ieee_double"/> + <reg name="f9s1" bitsize="64" type="ieee_double"/> + <reg name="f10s1" bitsize="64" type="ieee_double"/> + <reg name="f11s1" bitsize="64" type="ieee_double"/> + <reg name="f12s1" bitsize="64" type="ieee_double"/> + <reg name="f13s1" bitsize="64" type="ieee_double"/> + <reg name="f14s1" bitsize="64" type="ieee_double"/> + <reg name="f15s1" bitsize="64" type="ieee_double"/> + <reg name="f16s1" bitsize="64" type="ieee_double"/> + <reg name="f17s1" bitsize="64" type="ieee_double"/> + <reg name="f18s1" bitsize="64" type="ieee_double"/> + <reg name="f19s1" bitsize="64" type="ieee_double"/> + <reg name="f20s1" bitsize="64" type="ieee_double"/> + <reg name="f21s1" bitsize="64" type="ieee_double"/> + <reg name="f22s1" bitsize="64" type="ieee_double"/> + <reg name="f23s1" bitsize="64" type="ieee_double"/> + <reg name="f24s1" bitsize="64" type="ieee_double"/> + <reg name="f25s1" bitsize="64" type="ieee_double"/> + <reg name="f26s1" bitsize="64" type="ieee_double"/> + <reg name="f27s1" bitsize="64" type="ieee_double"/> + <reg name="f28s1" bitsize="64" type="ieee_double"/> + <reg name="f29s1" bitsize="64" type="ieee_double"/> + <reg name="f30s1" bitsize="64" type="ieee_double"/> + <reg name="f31s1" bitsize="64" type="ieee_double"/> + + <reg name="fpscrs1" bitsize="32" group="float" regnum="70"/> +</feature> diff --git a/coregrind/m_gdbserver/power-fpu-valgrind-s2.xml b/coregrind/m_gdbserver/power-fpu-valgrind-s2.xml new file mode 100644 index 00000000..2db1a4ab --- /dev/null +++ b/coregrind/m_gdbserver/power-fpu-valgrind-s2.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.fpu-valgrind-s2"> + <reg name="f0s2" bitsize="64" type="ieee_double" regnum="32"/> + <reg name="f1s2" bitsize="64" type="ieee_double"/> + <reg name="f2s2" bitsize="64" type="ieee_double"/> + <reg name="f3s2" bitsize="64" type="ieee_double"/> + <reg name="f4s2" bitsize="64" type="ieee_double"/> + <reg name="f5s2" bitsize="64" type="ieee_double"/> + <reg name="f6s2" bitsize="64" type="ieee_double"/> + <reg name="f7s2" bitsize="64" type="ieee_double"/> + <reg name="f8s2" bitsize="64" type="ieee_double"/> + <reg name="f9s2" bitsize="64" type="ieee_double"/> + <reg name="f10s2" bitsize="64" type="ieee_double"/> + <reg name="f11s2" bitsize="64" type="ieee_double"/> + <reg name="f12s2" bitsize="64" type="ieee_double"/> + <reg name="f13s2" bitsize="64" type="ieee_double"/> + <reg name="f14s2" bitsize="64" type="ieee_double"/> + <reg name="f15s2" bitsize="64" type="ieee_double"/> + <reg name="f16s2" bitsize="64" type="ieee_double"/> + <reg name="f17s2" bitsize="64" type="ieee_double"/> + <reg name="f18s2" bitsize="64" type="ieee_double"/> + <reg name="f19s2" bitsize="64" type="ieee_double"/> + <reg name="f20s2" bitsize="64" type="ieee_double"/> + <reg name="f21s2" bitsize="64" type="ieee_double"/> + <reg name="f22s2" bitsize="64" type="ieee_double"/> + <reg name="f23s2" bitsize="64" type="ieee_double"/> + <reg name="f24s2" bitsize="64" type="ieee_double"/> + <reg name="f25s2" bitsize="64" type="ieee_double"/> + <reg name="f26s2" bitsize="64" type="ieee_double"/> + <reg name="f27s2" bitsize="64" type="ieee_double"/> + <reg name="f28s2" bitsize="64" type="ieee_double"/> + <reg name="f29s2" bitsize="64" type="ieee_double"/> + <reg name="f30s2" bitsize="64" type="ieee_double"/> + <reg name="f31s2" bitsize="64" type="ieee_double"/> + + <reg name="fpscrs2" bitsize="32" group="float" regnum="70"/> +</feature> diff --git a/coregrind/m_gdbserver/power-fpu.xml b/coregrind/m_gdbserver/power-fpu.xml new file mode 100644 index 00000000..d896b47d --- /dev/null +++ b/coregrind/m_gdbserver/power-fpu.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.fpu"> + <reg name="f0" bitsize="64" type="ieee_double" regnum="32"/> + <reg name="f1" bitsize="64" type="ieee_double"/> + <reg name="f2" bitsize="64" type="ieee_double"/> + <reg name="f3" bitsize="64" type="ieee_double"/> + <reg name="f4" bitsize="64" type="ieee_double"/> + <reg name="f5" bitsize="64" type="ieee_double"/> + <reg name="f6" bitsize="64" type="ieee_double"/> + <reg name="f7" bitsize="64" type="ieee_double"/> + <reg name="f8" bitsize="64" type="ieee_double"/> + <reg name="f9" bitsize="64" type="ieee_double"/> + <reg name="f10" bitsize="64" type="ieee_double"/> + <reg name="f11" bitsize="64" type="ieee_double"/> + <reg name="f12" bitsize="64" type="ieee_double"/> + <reg name="f13" bitsize="64" type="ieee_double"/> + <reg name="f14" bitsize="64" type="ieee_double"/> + <reg name="f15" bitsize="64" type="ieee_double"/> + <reg name="f16" bitsize="64" type="ieee_double"/> + <reg name="f17" bitsize="64" type="ieee_double"/> + <reg name="f18" bitsize="64" type="ieee_double"/> + <reg name="f19" bitsize="64" type="ieee_double"/> + <reg name="f20" bitsize="64" type="ieee_double"/> + <reg name="f21" bitsize="64" type="ieee_double"/> + <reg name="f22" bitsize="64" type="ieee_double"/> + <reg name="f23" bitsize="64" type="ieee_double"/> + <reg name="f24" bitsize="64" type="ieee_double"/> + <reg name="f25" bitsize="64" type="ieee_double"/> + <reg name="f26" bitsize="64" type="ieee_double"/> + <reg name="f27" bitsize="64" type="ieee_double"/> + <reg name="f28" bitsize="64" type="ieee_double"/> + <reg name="f29" bitsize="64" type="ieee_double"/> + <reg name="f30" bitsize="64" type="ieee_double"/> + <reg name="f31" bitsize="64" type="ieee_double"/> + + <reg name="fpscr" bitsize="32" group="float" regnum="70"/> +</feature> diff --git a/coregrind/m_gdbserver/power-linux-valgrind-s1.xml b/coregrind/m_gdbserver/power-linux-valgrind-s1.xml new file mode 100644 index 00000000..a02dd8ec --- /dev/null +++ b/coregrind/m_gdbserver/power-linux-valgrind-s1.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.linux-valgrind-s1"> + <reg name="orig_r3s1" bitsize="32" regnum="71"/> + <reg name="traps1" bitsize="32"/> +</feature> diff --git a/coregrind/m_gdbserver/power-linux-valgrind-s2.xml b/coregrind/m_gdbserver/power-linux-valgrind-s2.xml new file mode 100644 index 00000000..59f6ee3d --- /dev/null +++ b/coregrind/m_gdbserver/power-linux-valgrind-s2.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.linux-valgrind-s2"> + <reg name="orig_r3s2" bitsize="32" regnum="71"/> + <reg name="traps2" bitsize="32"/> +</feature> diff --git a/coregrind/m_gdbserver/power-linux.xml b/coregrind/m_gdbserver/power-linux.xml new file mode 100644 index 00000000..b8b75199 --- /dev/null +++ b/coregrind/m_gdbserver/power-linux.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.linux"> + <reg name="orig_r3" bitsize="32" regnum="71"/> + <reg name="trap" bitsize="32"/> +</feature> diff --git a/coregrind/m_gdbserver/power64-core-valgrind-s1.xml b/coregrind/m_gdbserver/power64-core-valgrind-s1.xml new file mode 100644 index 00000000..f6296bf7 --- /dev/null +++ b/coregrind/m_gdbserver/power64-core-valgrind-s1.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.core-valgrind-s1"> + <reg name="r0s1" bitsize="64" type="uint64"/> + <reg name="r1s1" bitsize="64" type="uint64"/> + <reg name="r2s1" bitsize="64" type="uint64"/> + <reg name="r3s1" bitsize="64" type="uint64"/> + <reg name="r4s1" bitsize="64" type="uint64"/> + <reg name="r5s1" bitsize="64" type="uint64"/> + <reg name="r6s1" bitsize="64" type="uint64"/> + <reg name="r7s1" bitsize="64" type="uint64"/> + <reg name="r8s1" bitsize="64" type="uint64"/> + <reg name="r9s1" bitsize="64" type="uint64"/> + <reg name="r10s1" bitsize="64" type="uint64"/> + <reg name="r11s1" bitsize="64" type="uint64"/> + <reg name="r12s1" bitsize="64" type="uint64"/> + <reg name="r13s1" bitsize="64" type="uint64"/> + <reg name="r14s1" bitsize="64" type="uint64"/> + <reg name="r15s1" bitsize="64" type="uint64"/> + <reg name="r16s1" bitsize="64" type="uint64"/> + <reg name="r17s1" bitsize="64" type="uint64"/> + <reg name="r18s1" bitsize="64" type="uint64"/> + <reg name="r19s1" bitsize="64" type="uint64"/> + <reg name="r20s1" bitsize="64" type="uint64"/> + <reg name="r21s1" bitsize="64" type="uint64"/> + <reg name="r22s1" bitsize="64" type="uint64"/> + <reg name="r23s1" bitsize="64" type="uint64"/> + <reg name="r24s1" bitsize="64" type="uint64"/> + <reg name="r25s1" bitsize="64" type="uint64"/> + <reg name="r26s1" bitsize="64" type="uint64"/> + <reg name="r27s1" bitsize="64" type="uint64"/> + <reg name="r28s1" bitsize="64" type="uint64"/> + <reg name="r29s1" bitsize="64" type="uint64"/> + <reg name="r30s1" bitsize="64" type="uint64"/> + <reg name="r31s1" bitsize="64" type="uint64"/> + + <reg name="pcs1" bitsize="64" type="code_ptr" regnum="64"/> + <reg name="msrs1" bitsize="64" type="uint64"/> + <reg name="crs1" bitsize="32" type="uint32"/> + <reg name="lrs1" bitsize="64" type="code_ptr"/> + <reg name="ctrs1" bitsize="64" type="uint64"/> + <reg name="xers1" bitsize="32" type="uint32"/> +</feature> diff --git a/coregrind/m_gdbserver/power64-core-valgrind-s2.xml b/coregrind/m_gdbserver/power64-core-valgrind-s2.xml new file mode 100644 index 00000000..663232e2 --- /dev/null +++ b/coregrind/m_gdbserver/power64-core-valgrind-s2.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.core-valgrind-s2"> + <reg name="r0s2" bitsize="64" type="uint64"/> + <reg name="r1s2" bitsize="64" type="uint64"/> + <reg name="r2s2" bitsize="64" type="uint64"/> + <reg name="r3s2" bitsize="64" type="uint64"/> + <reg name="r4s2" bitsize="64" type="uint64"/> + <reg name="r5s2" bitsize="64" type="uint64"/> + <reg name="r6s2" bitsize="64" type="uint64"/> + <reg name="r7s2" bitsize="64" type="uint64"/> + <reg name="r8s2" bitsize="64" type="uint64"/> + <reg name="r9s2" bitsize="64" type="uint64"/> + <reg name="r10s2" bitsize="64" type="uint64"/> + <reg name="r11s2" bitsize="64" type="uint64"/> + <reg name="r12s2" bitsize="64" type="uint64"/> + <reg name="r13s2" bitsize="64" type="uint64"/> + <reg name="r14s2" bitsize="64" type="uint64"/> + <reg name="r15s2" bitsize="64" type="uint64"/> + <reg name="r16s2" bitsize="64" type="uint64"/> + <reg name="r17s2" bitsize="64" type="uint64"/> + <reg name="r18s2" bitsize="64" type="uint64"/> + <reg name="r19s2" bitsize="64" type="uint64"/> + <reg name="r20s2" bitsize="64" type="uint64"/> + <reg name="r21s2" bitsize="64" type="uint64"/> + <reg name="r22s2" bitsize="64" type="uint64"/> + <reg name="r23s2" bitsize="64" type="uint64"/> + <reg name="r24s2" bitsize="64" type="uint64"/> + <reg name="r25s2" bitsize="64" type="uint64"/> + <reg name="r26s2" bitsize="64" type="uint64"/> + <reg name="r27s2" bitsize="64" type="uint64"/> + <reg name="r28s2" bitsize="64" type="uint64"/> + <reg name="r29s2" bitsize="64" type="uint64"/> + <reg name="r30s2" bitsize="64" type="uint64"/> + <reg name="r31s2" bitsize="64" type="uint64"/> + + <reg name="pcs2" bitsize="64" type="code_ptr" regnum="64"/> + <reg name="msrs2" bitsize="64" type="uint64"/> + <reg name="crs2" bitsize="32" type="uint32"/> + <reg name="lrs2" bitsize="64" type="code_ptr"/> + <reg name="ctrs2" bitsize="64" type="uint64"/> + <reg name="xers2" bitsize="32" type="uint32"/> +</feature> diff --git a/coregrind/m_gdbserver/power64-core.xml b/coregrind/m_gdbserver/power64-core.xml new file mode 100644 index 00000000..e0a6ee32 --- /dev/null +++ b/coregrind/m_gdbserver/power64-core.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.core"> + <reg name="r0" bitsize="64" type="uint64"/> + <reg name="r1" bitsize="64" type="uint64"/> + <reg name="r2" bitsize="64" type="uint64"/> + <reg name="r3" bitsize="64" type="uint64"/> + <reg name="r4" bitsize="64" type="uint64"/> + <reg name="r5" bitsize="64" type="uint64"/> + <reg name="r6" bitsize="64" type="uint64"/> + <reg name="r7" bitsize="64" type="uint64"/> + <reg name="r8" bitsize="64" type="uint64"/> + <reg name="r9" bitsize="64" type="uint64"/> + <reg name="r10" bitsize="64" type="uint64"/> + <reg name="r11" bitsize="64" type="uint64"/> + <reg name="r12" bitsize="64" type="uint64"/> + <reg name="r13" bitsize="64" type="uint64"/> + <reg name="r14" bitsize="64" type="uint64"/> + <reg name="r15" bitsize="64" type="uint64"/> + <reg name="r16" bitsize="64" type="uint64"/> + <reg name="r17" bitsize="64" type="uint64"/> + <reg name="r18" bitsize="64" type="uint64"/> + <reg name="r19" bitsize="64" type="uint64"/> + <reg name="r20" bitsize="64" type="uint64"/> + <reg name="r21" bitsize="64" type="uint64"/> + <reg name="r22" bitsize="64" type="uint64"/> + <reg name="r23" bitsize="64" type="uint64"/> + <reg name="r24" bitsize="64" type="uint64"/> + <reg name="r25" bitsize="64" type="uint64"/> + <reg name="r26" bitsize="64" type="uint64"/> + <reg name="r27" bitsize="64" type="uint64"/> + <reg name="r28" bitsize="64" type="uint64"/> + <reg name="r29" bitsize="64" type="uint64"/> + <reg name="r30" bitsize="64" type="uint64"/> + <reg name="r31" bitsize="64" type="uint64"/> + + <reg name="pc" bitsize="64" type="code_ptr" regnum="64"/> + <reg name="msr" bitsize="64" type="uint64"/> + <reg name="cr" bitsize="32" type="uint32"/> + <reg name="lr" bitsize="64" type="code_ptr"/> + <reg name="ctr" bitsize="64" type="uint64"/> + <reg name="xer" bitsize="32" type="uint32"/> +</feature> diff --git a/coregrind/m_gdbserver/power64-linux-valgrind-s1.xml b/coregrind/m_gdbserver/power64-linux-valgrind-s1.xml new file mode 100644 index 00000000..7f1d0acc --- /dev/null +++ b/coregrind/m_gdbserver/power64-linux-valgrind-s1.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.linux-valgrind-s1"> + <reg name="orig_r3s1" bitsize="64" regnum="71"/> + <reg name="traps1" bitsize="64"/> +</feature> diff --git a/coregrind/m_gdbserver/power64-linux-valgrind-s2.xml b/coregrind/m_gdbserver/power64-linux-valgrind-s2.xml new file mode 100644 index 00000000..007bd04a --- /dev/null +++ b/coregrind/m_gdbserver/power64-linux-valgrind-s2.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.linux-valgrind-s2"> + <reg name="orig_r3s2" bitsize="64" regnum="71"/> + <reg name="traps2" bitsize="64"/> +</feature> diff --git a/coregrind/m_gdbserver/power64-linux.xml b/coregrind/m_gdbserver/power64-linux.xml new file mode 100644 index 00000000..a72a058c --- /dev/null +++ b/coregrind/m_gdbserver/power64-linux.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.power.linux"> + <reg name="orig_r3" bitsize="64" regnum="71"/> + <reg name="trap" bitsize="64"/> +</feature> diff --git a/coregrind/m_gdbserver/powerpc-altivec32l-valgrind.xml b/coregrind/m_gdbserver/powerpc-altivec32l-valgrind.xml new file mode 100644 index 00000000..07094a4d --- /dev/null +++ b/coregrind/m_gdbserver/powerpc-altivec32l-valgrind.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only + view of the PowerPC. Includes Linux-only special "registers" and AltiVec + vector registers. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>powerpc:common</architecture> + <xi:include href="power-core.xml"/> + <xi:include href="power-fpu.xml"/> + <xi:include href="power-linux.xml"/> + <xi:include href="power-altivec.xml"/> + <xi:include href="power-core-valgrind-s1.xml"/> + <xi:include href="power-fpu-valgrind-s1.xml"/> + <xi:include href="power-linux-valgrind-s1.xml"/> + <xi:include href="power-altivec-valgrind-s1.xml"/> + <xi:include href="power-core-valgrind-s2.xml"/> + <xi:include href="power-fpu-valgrind-s2.xml"/> + <xi:include href="power-linux-valgrind-s2.xml"/> + <xi:include href="power-altivec-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/powerpc-altivec32l.xml b/coregrind/m_gdbserver/powerpc-altivec32l.xml new file mode 100644 index 00000000..8d77e107 --- /dev/null +++ b/coregrind/m_gdbserver/powerpc-altivec32l.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only + view of the PowerPC. Includes Linux-only special "registers" and AltiVec + vector registers. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>powerpc:common</architecture> + <xi:include href="power-core.xml"/> + <xi:include href="power-fpu.xml"/> + <xi:include href="power-linux.xml"/> + <xi:include href="power-altivec.xml"/> +</target> diff --git a/coregrind/m_gdbserver/powerpc-altivec64l-valgrind.xml b/coregrind/m_gdbserver/powerpc-altivec64l-valgrind.xml new file mode 100644 index 00000000..a2cd6150 --- /dev/null +++ b/coregrind/m_gdbserver/powerpc-altivec64l-valgrind.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only + view of the PowerPC. Includes Linux-only special "registers" and AltiVec + vector registers. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>powerpc:common64</architecture> + <xi:include href="power64-core.xml"/> + <xi:include href="power-fpu.xml"/> + <xi:include href="power64-linux.xml"/> + <xi:include href="power-altivec.xml"/> + <xi:include href="power64-core-valgrind-s1.xml"/> + <xi:include href="power-fpu-valgrind-s1.xml"/> + <xi:include href="power64-linux-valgrind-s1.xml"/> + <xi:include href="power-altivec-valgrind-s1.xml"/> + <xi:include href="power64-core-valgrind-s2.xml"/> + <xi:include href="power-fpu-valgrind-s2.xml"/> + <xi:include href="power64-linux-valgrind-s2.xml"/> + <xi:include href="power-altivec-valgrind-s2.xml"/> +</target> diff --git a/coregrind/m_gdbserver/powerpc-altivec64l.xml b/coregrind/m_gdbserver/powerpc-altivec64l.xml new file mode 100644 index 00000000..d06dad9b --- /dev/null +++ b/coregrind/m_gdbserver/powerpc-altivec64l.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only + view of the PowerPC. Includes Linux-only special "registers" and AltiVec + vector registers. --> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>powerpc:common64</architecture> + <xi:include href="power64-core.xml"/> + <xi:include href="power-fpu.xml"/> + <xi:include href="power64-linux.xml"/> + <xi:include href="power-altivec.xml"/> +</target> diff --git a/coregrind/m_gdbserver/regcache.c b/coregrind/m_gdbserver/regcache.c new file mode 100644 index 00000000..a9202316 --- /dev/null +++ b/coregrind/m_gdbserver/regcache.c @@ -0,0 +1,263 @@ +/* Register support routines for the remote server for GDB. + Copyright (C) 2001, 2002, 2004, 2005, 2011 + Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "regdef.h" + +/* The private data for the register cache. Note that we have one + per inferior; this is primarily for simplicity, as the performance + benefit is minimal. */ + +struct inferior_regcache_data +{ + int registers_valid; + unsigned char *registers; + Bool *register_supplied; /* set to True once it has been supplied */ +}; + +static int register_bytes; + +static struct reg *reg_defs; +static int num_registers; + +const char **gdbserver_expedite_regs; + +static +struct inferior_regcache_data * get_regcache (struct thread_info *inf, + int fetch) +{ + struct inferior_regcache_data *regcache; + + regcache = (struct inferior_regcache_data *) inferior_regcache_data (inf); + + if (regcache == NULL) + fatal ("no register cache\n"); + + /* FIXME - fetch registers for INF */ + if (fetch && regcache->registers_valid == 0) { + fetch_inferior_registers (0); + regcache->registers_valid = 1; + } + + return regcache; +} + +void regcache_invalidate_one (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct inferior_regcache_data *regcache; + + regcache = (struct inferior_regcache_data *) inferior_regcache_data (thread); + + if (regcache->registers_valid) { + struct thread_info *saved_inferior = current_inferior; + + current_inferior = thread; + store_inferior_registers (-1); + current_inferior = saved_inferior; + } + + regcache->registers_valid = 0; +} + +void regcache_invalidate () +{ + for_each_inferior (&all_threads, regcache_invalidate_one); +} + +int registers_length (void) +{ + return 2 * register_bytes; +} + +void *new_register_cache (void) +{ + struct inferior_regcache_data *regcache; + + regcache = malloc (sizeof (*regcache)); + + /* Make sure to zero-initialize the register cache when it is created, + in case there are registers the target never fetches. This way they'll + read as zero instead of garbage. */ + regcache->registers = calloc (1, register_bytes); + if (regcache->registers == NULL) + fatal ("Could not allocate register cache.\n"); + + regcache->register_supplied = calloc (1, num_registers); + if (regcache->register_supplied == NULL) + fatal ("Could not allocate register_supplied cache.\n"); + + regcache->registers_valid = 0; + + return regcache; +} + +void free_register_cache (void *regcache_p) +{ + struct inferior_regcache_data *regcache + = (struct inferior_regcache_data *) regcache_p; + + free (regcache->registers); + free (regcache->register_supplied); + free (regcache); +} + +/* if a regcache exists for entry, reallocate it. + This is needed if the shadow registers are added. + In such a case, a 2nd call to set_register_cache is done + which will cause the reallocation of already created caches. */ +static +void regcache_realloc_one (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct inferior_regcache_data *regcache; + + regcache = (struct inferior_regcache_data *) inferior_regcache_data (thread); + + if (regcache) { + free_register_cache (regcache); + set_inferior_regcache_data (thread, new_register_cache ()); + } +} + +void set_register_cache (struct reg *regs, int n) +{ + int offset, i; + + reg_defs = regs; + num_registers = n; + + offset = 0; + for (i = 0; i < n; i++) { + regs[i].offset = offset; + offset += regs[i].size; + } + + register_bytes = offset / 8; + + for_each_inferior (&all_threads, regcache_realloc_one); +} + +void registers_to_string (char *buf) +{ + unsigned char *registers = get_regcache (current_inferior, 1)->registers; + + convert_int_to_ascii (registers, buf, register_bytes); +} + +void registers_from_string (char *buf) +{ + int len = strlen (buf); + unsigned char *registers = get_regcache (current_inferior, 1)->registers; + + if (len != register_bytes * 2) { + warning ("Wrong sized register packet (expected %d bytes, got %d)\n", + 2*register_bytes, len); + if (len > register_bytes * 2) + len = register_bytes * 2; + } + convert_ascii_to_int (buf, registers, len / 2); +} + +int find_regno (const char *name) +{ + int i; + + for (i = 0; i < num_registers; i++) + if (!strcmp (name, reg_defs[i].name)) + return i; + fatal ("Unknown register %s requested\n", name); + return -1; +} + +struct reg *find_register_by_number (int n) +{ + return ®_defs[n]; +} + +int register_size (int n) +{ + return reg_defs[n].size / 8; +} + +static +unsigned char *register_data (int n, int fetch) +{ + unsigned char *registers + = get_regcache (current_inferior, fetch)->registers; + + return registers + (reg_defs[n].offset / 8); +} +static +unsigned char *register_data_for_supply (int n, int fetch, Bool *mod) +{ + struct inferior_regcache_data * cache + = get_regcache (current_inferior, fetch); + unsigned char *registers = cache->registers; + + if (cache->register_supplied[n]) + *mod = False; + else + *mod = True; + cache->register_supplied[n] = True; + return registers + (reg_defs[n].offset / 8); +} + +void supply_register (int n, const void *buf, Bool *mod) +{ + Bool new; + VG_(dmemcpy) (register_data_for_supply (n, 0, &new), + buf, register_size (n), mod); + if (new) + *mod = True; +} + +void supply_register_from_string (int n, const char *buf, Bool *mod) +{ + Bool new; + unsigned char bytes_register[register_size (n)]; + convert_ascii_to_int ((char *) buf, bytes_register, register_size (n)); + VG_(dmemcpy) (register_data_for_supply (n, 0, &new), + bytes_register, register_size (n), mod); + if (new) + *mod = True; +} + +void supply_register_by_name (const char *name, const void *buf, Bool *mod) +{ + supply_register (find_regno (name), buf, mod); +} + +void collect_register (int n, void *buf) +{ + VG_(memcpy) (buf, register_data (n, 1), register_size (n)); +} + +void collect_register_as_string (int n, char *buf) +{ + convert_int_to_ascii (register_data (n, 1), buf, register_size (n)); +} + +void collect_register_by_name (const char *name, void *buf) +{ + collect_register (find_regno (name), buf); +} diff --git a/coregrind/m_gdbserver/regcache.h b/coregrind/m_gdbserver/regcache.h new file mode 100644 index 00000000..2f17d0a2 --- /dev/null +++ b/coregrind/m_gdbserver/regcache.h @@ -0,0 +1,80 @@ +/* Register support routines for the remote server for GDB. + Copyright (C) 2001, 2002 Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef REGCACHE_H +#define REGCACHE_H + +struct inferior_list_entry; + +/* Create a new register cache for INFERIOR. */ + +void *new_register_cache (void); + +/* Release all memory associated with the register cache for INFERIOR. */ + +void free_register_cache (void *regcache); + +/* Invalidate cached registers for one or all threads. */ + +void regcache_invalidate_one (struct inferior_list_entry *); +void regcache_invalidate (void); + +/* Convert all registers to a string in the currently specified remote + format. */ + +void registers_to_string (char *buf); + +/* Convert a string to register values and fill our register cache. */ + +void registers_from_string (char *buf); + +/* Return the size in bytes of a string-encoded register packet. */ + +int registers_length (void); + +/* Return a pointer to the description of register ``n''. */ + +struct reg *find_register_by_number (int n); + +int register_size (int n); + +int find_regno (const char *name); + +extern const char **gdbserver_expedite_regs; + +/* *mod set to True if *buf provides a new value. */ +void supply_register (int n, const void *buf, Bool *mod); + +/* Reads register data from buf (hex string in target byte order) + and stores it in the register cache. + *mod set to True if *buf provides a new value. */ +void supply_register_from_string (int n, const char *buf, Bool *mod); + +/* *mod set to True if *buf provides a new value. */ +void supply_register_by_name (const char *name, const void *buf, Bool *mod); + +void collect_register (int n, void *buf); + +void collect_register_as_string (int n, char *buf); + +void collect_register_by_name (const char *name, void *buf); + +#endif /* REGCACHE_H */ diff --git a/coregrind/m_gdbserver/regdef.h b/coregrind/m_gdbserver/regdef.h new file mode 100644 index 00000000..60400fd6 --- /dev/null +++ b/coregrind/m_gdbserver/regdef.h @@ -0,0 +1,47 @@ +/* Register protocol definition structures for the GNU Debugger + Copyright 2001, 2002 Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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 REGDEF_H +#define REGDEF_H + +struct reg +{ + /* The name of this register - NULL for pad entries. */ + const char *name; + + /* At the moment, both of the following bit counts must be divisible + by eight (to match the representation as two hex digits) and divisible + by the size of a byte (to match the layout of each register in + memory). */ + + /* The offset (in bits) of the value of this register in the buffer. */ + int offset; + + /* The size (in bits) of the value of this register, as transmitted. */ + int size; +}; + +/* Set the current remote protocol and register cache according to the array + ``regs'', with ``n'' elements. */ + +void set_register_cache (struct reg *regs, int n); + +#endif /* REGDEF_H */ diff --git a/coregrind/m_gdbserver/remote-utils.c b/coregrind/m_gdbserver/remote-utils.c new file mode 100644 index 00000000..eb2f0113 --- /dev/null +++ b/coregrind/m_gdbserver/remote-utils.c @@ -0,0 +1,1036 @@ +/* Remote utility routines for the remote server for GDB. + Copyright (C) 1986, 1989, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2011 + Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_vkiscnums.h" +#include "pub_core_libcsignal.h" +#include "pub_core_options.h" + +#include "server.h" + +# if defined(VGO_linux) +#include <sys/prctl.h> +# endif + +Bool noack_mode; + +static int readchar (int single); + +void remote_utils_output_status(void); + +static int remote_desc; + +static VgdbShared *shared; +static int last_looked_cntr = -1; +static struct vki_pollfd remote_desc_pollfdread_activity; +#define INVALID_DESCRIPTOR -1 + +/* for a gdbserver embedded in valgrind, we read from a FIFO and write + to another FIFO So, we need two descriptors */ +static int write_remote_desc = INVALID_DESCRIPTOR; +static int pid_from_to_creator; +/* only this pid will remove the FIFOs: if an exec fails, we must avoid + that the exiting child believes it has to remove the FIFOs of its parent */ +static int mknod_done = 0; + +static char *from_gdb = NULL; +static char *to_gdb = NULL; +static char *shared_mem = NULL; + +static +int open_fifo (char *side, char *path, int flags) +{ + SysRes o; + int fd; + dlog(1, "Opening %s side %s\n", side, path); + o = VG_(open) (path, flags, 0); + if (sr_isError (o)) { + sr_perror(o, "open fifo %s\n", path); + fatal ("valgrind: fatal error: vgdb FIFO cannot be opened.\n"); + } else { + fd = sr_Res(o); + dlog(1, "result fd %d\n", fd); + } + fd = VG_(safe_fd)(fd); + dlog(1, "result safe_fd %d\n", fd); + if (fd == -1) + fatal("safe_fd for vgdb FIFO failed\n"); + return fd; +} + +void remote_utils_output_status(void) +{ + if (shared == NULL) + VG_(umsg)("remote communication not initialized\n"); + else + VG_(umsg)("shared->written_by_vgdb %d shared->seen_by_valgrind %d\n", + shared->written_by_vgdb, shared->seen_by_valgrind); +} + +/* Returns 0 if vgdb and connection state looks good, + otherwise returns an int value telling which check failed. */ +static +int vgdb_state_looks_bad(char* where) +{ + if (VG_(kill)(shared->vgdb_pid, 0) != 0) + return 1; // vgdb process does not exist anymore. + + if (remote_desc_activity(where) == 2) + return 2; // check for error on remote desc shows a problem + + if (remote_desc == INVALID_DESCRIPTOR) + return 3; // after check, remote_desc not ok anymore + + return 0; // all is ok. +} + +/* On systems that defines PR_SET_PTRACER, verify if ptrace_scope is + is permissive enough for vgdb. Otherwise, call set_ptracer. + This is especially aimed at Ubuntu >= 10.10 which has added + the ptrace_scope context. */ +static +void set_ptracer(void) +{ +#ifdef PR_SET_PTRACER + SysRes o; + char *ptrace_scope_setting_file = "/proc/sys/kernel/yama/ptrace_scope"; + int fd; + char ptrace_scope; + int ret; + + o = VG_(open) (ptrace_scope_setting_file, VKI_O_RDONLY, 0); + if (sr_isError(o)) { + sr_perror(o, "error VG_(open) %s\n", ptrace_scope_setting_file); + /* can't read setting. Assuming ptrace can be called by vgdb. */ + return; + } + fd = sr_Res(o); + if (VG_(read) (fd, &ptrace_scope, 1) == 1) { + dlog(1, "ptrace_scope %c\n", ptrace_scope); + if (ptrace_scope != '0') { + /* insufficient default ptrace_scope. + Indicate to the kernel that we accept to be + ptraced by our vgdb. */ + ret = VG_(prctl) (PR_SET_PTRACER, shared->vgdb_pid, 0, 0, 0); + dlog(1, "set_ptracer to vgdb_pid %d result %d\n", + shared->vgdb_pid, ret); + } + } else { + dlog(0, "Could not read the ptrace_scope setting from %s\n", + ptrace_scope_setting_file); + } + + VG_(close) (fd); +#endif +} + +/* returns 1 if one or more poll "errors" is set. + Errors are: VKI_POLLERR or VKI_POLLHUP or VKI_POLLNAL */ +static +int poll_cond (short revents) +{ + return (revents & (VKI_POLLERR | VKI_POLLHUP | VKI_POLLNVAL)); +} + +/* Ensures we have a valid write file descriptor. + Returns 1 if we have a valid write file descriptor, + 0 if the write fd could not be opened. */ +static +int ensure_write_remote_desc(void) +{ + struct vki_pollfd write_remote_desc_ok; + int ret; + if (write_remote_desc != INVALID_DESCRIPTOR) { + write_remote_desc_ok.fd = write_remote_desc; + write_remote_desc_ok.events = VKI_POLLOUT; + write_remote_desc_ok.revents = 0; + ret = VG_(poll)(&write_remote_desc_ok, 1, 0); + if (ret && poll_cond(write_remote_desc_ok.revents)) { + dlog(1, "POLLcond %d closing write_remote_desc %d\n", + write_remote_desc_ok.revents, write_remote_desc); + VG_(close) (write_remote_desc); + write_remote_desc = INVALID_DESCRIPTOR; + } + } + if (write_remote_desc == INVALID_DESCRIPTOR) { + /* open_fifo write will block if the receiving vgdb + process is dead. So, let's check for vgdb state to + be reasonably sure someone is reading on the other + side of the fifo. */ + if (!vgdb_state_looks_bad("bad?@ensure_write_remote_desc")) { + set_ptracer(); + write_remote_desc = open_fifo ("write", to_gdb, VKI_O_WRONLY); + } + } + + return (write_remote_desc != INVALID_DESCRIPTOR); +} + +#if defined(VGO_darwin) +#define VKI_S_IFIFO 0010000 +#endif +static +void safe_mknod (char *nod) +{ + SysRes m; + m = VG_(mknod) (nod, VKI_S_IFIFO|0666, 0); + if (sr_isError (m)) { + if (sr_Err (m) == VKI_EEXIST) { + if (VG_(clo_verbosity) > 1) { + VG_(umsg)("%s already created\n", nod); + } + } else { + sr_perror(m, "mknod %s\n", nod); + VG_(umsg) ("valgrind: fatal error: vgdb FIFOs cannot be created.\n"); + VG_(exit)(1); + } + } +} + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. + For Valgrind, name is the prefix for the two read and write FIFOs + The two FIFOs names will be build by appending + -from-vgdb-to-pid and -to-vgdb-from-pid + with pid being the pidnr of the valgrind process These two FIFOs + will be created if not existing yet. They will be removed when + the gdbserver connection is closed or the process exits */ + +void remote_open (char *name) +{ + int save_fcntl_flags; + VgdbShared vgdbinit = + {0, 0, 0, (Addr) VG_(invoke_gdbserver), + (Addr) VG_(threads), sizeof(ThreadState), + offsetof(ThreadState, status), + offsetof(ThreadState, os_state) + offsetof(ThreadOSstate, lwpid)}; + const int pid = VG_(getpid)(); + const int name_default = strcmp(name, VG_CLO_VGDB_PREFIX_DEFAULT) == 0; + Addr addr_shared; + SysRes o; + int shared_mem_fd = INVALID_DESCRIPTOR; + + if (from_gdb != NULL) + free (from_gdb); + from_gdb = malloc (strlen(name) + 30); + if (to_gdb != NULL) + free (to_gdb); + to_gdb = malloc (strlen(name) + 30); + if (shared_mem != NULL) + free (shared_mem); + shared_mem = malloc (strlen(name) + 30); + /* below 3 lines must match the equivalent in vgdb.c */ + VG_(sprintf) (from_gdb, "%s-from-vgdb-to-%d", name, pid); + VG_(sprintf) (to_gdb, "%s-to-vgdb-from-%d", name, pid); + VG_(sprintf) (shared_mem, "%s-shared-mem-vgdb-%d", name, pid); + if (VG_(clo_verbosity) > 1) { + VG_(umsg)("embedded gdbserver: reading from %s\n", from_gdb); + VG_(umsg)("embedded gdbserver: writing to %s\n", to_gdb); + VG_(umsg)("embedded gdbserver: shared mem %s\n", shared_mem); + VG_(umsg)("CONTROL ME using: vgdb --pid=%d%s%s ...command...\n", + pid, (name_default ? "" : " --vgdb="), + (name_default ? "" : name)); + VG_(umsg)("DEBUG ME using: (gdb) target remote | vgdb --pid=%d%s%s\n", + pid, (name_default ? "" : " --vgdb="), + (name_default ? "" : name)); + VG_(umsg)(" --pid optional if only one valgrind process is running\n"); + } + + if (!mknod_done) { + mknod_done++; + safe_mknod(from_gdb); + safe_mknod(to_gdb); + + pid_from_to_creator = pid; + + o = VG_(open) (shared_mem, VKI_O_CREAT|VKI_O_RDWR, 0666); + if (sr_isError (o)) { + sr_perror(o, "cannot create shared_mem file %s\n", shared_mem); + fatal(""); + } else { + shared_mem_fd = sr_Res(o); + } + + if (VG_(write)(shared_mem_fd, &vgdbinit, sizeof(VgdbShared)) + != sizeof(VgdbShared)) { + fatal("error writing %d bytes to shared mem %s\n", + (int) sizeof(VgdbShared), shared_mem); + } + shared_mem_fd = VG_(safe_fd)(shared_mem_fd); + if (shared_mem_fd == -1) { + fatal("safe_fd for vgdb shared_mem %s failed\n", shared_mem); + } + { + SysRes res = VG_(am_shared_mmap_file_float_valgrind) + (sizeof(VgdbShared), VKI_PROT_READ|VKI_PROT_WRITE, + shared_mem_fd, (Off64T)0); + if (sr_isError(res)) { + sr_perror(res, "error VG_(am_shared_mmap_file_float_valgrind) %s\n", + shared_mem); + fatal(""); + } + addr_shared = sr_Res (res); + } + shared = (VgdbShared*) addr_shared; + } + + /* we open the read side FIFO in non blocking mode + We then set the fd in blocking mode. + Opening in non-blocking read mode always succeeds while opening + in non-blocking write mode succeeds only if the fifo is already + opened in read mode. So, we wait till we have read the first + character from the read side before opening the write side. */ + remote_desc = open_fifo ("read", from_gdb, VKI_O_RDONLY|VKI_O_NONBLOCK); + save_fcntl_flags = VG_(fcntl) (remote_desc, VKI_F_GETFL, 0); + VG_(fcntl) (remote_desc, VKI_F_SETFL, save_fcntl_flags & ~VKI_O_NONBLOCK); + remote_desc_pollfdread_activity.fd = remote_desc; + remote_desc_pollfdread_activity.events = VKI_POLLIN; + remote_desc_pollfdread_activity.revents = 0; +} + +/* sync_gdb_connection wait a time long enough to let the connection + be properly closed if needed when closing the connection (in case + of detach or error), if we reopen it too quickly, it seems there + are some events queued in the kernel concerning the "old" + connection/remote_desc which are discovered with poll or select on + the "new" connection/remote_desc. We bypass this by waiting some + time to let a proper cleanup to be donex */ +void sync_gdb_connection(void) +{ + VG_(poll)(0, 0, 100); +} + +static +char * ppFinishReason (FinishReason reason) +{ + switch (reason) { + case orderly_finish: return "orderly_finish"; + case reset_after_error: return "reset_after_error"; + case reset_after_fork: return "reset_after_fork"; + default: vg_assert (0); + } +} + +void remote_finish (FinishReason reason) +{ + dlog(1, "remote_finish (reason %s) %d %d\n", + ppFinishReason(reason), remote_desc, write_remote_desc); + reset_valgrind_sink(ppFinishReason(reason)); + if (write_remote_desc != INVALID_DESCRIPTOR) + VG_(close) (write_remote_desc); + write_remote_desc = INVALID_DESCRIPTOR; + if (remote_desc != INVALID_DESCRIPTOR) { + remote_desc_pollfdread_activity.fd = INVALID_DESCRIPTOR; + remote_desc_pollfdread_activity.events = 0; + remote_desc_pollfdread_activity.revents = 0; + VG_(close) (remote_desc); + } + remote_desc = INVALID_DESCRIPTOR; + noack_mode = False; + + /* ensure the child will create its own FIFOs */ + if (reason == reset_after_fork) + mknod_done = 0; + + if (reason == reset_after_error) + sync_gdb_connection(); +} + +/* orderly close, cleans up everything */ +void remote_close (void) +{ + const int pid = VG_(getpid)(); + remote_finish(orderly_finish); + if (pid == pid_from_to_creator) { + dlog(1, "unlinking\n %s\n %s\n %s\n", + from_gdb, to_gdb, shared_mem); + if (VG_(unlink) (from_gdb) == -1) + warning ("could not unlink %s\n", from_gdb); + if (VG_(unlink) (to_gdb) == -1) + warning ("could not unlink %s\n", to_gdb); + if (VG_(unlink) (shared_mem) == -1) + warning ("could not unlink %s\n", shared_mem); + } + else { + dlog(1, "not creator => not unlinking %s and %s\n", from_gdb, to_gdb); + } + free (from_gdb); + free (to_gdb); +} + +Bool remote_connected(void) +{ + return write_remote_desc != INVALID_DESCRIPTOR; +} + +/* cleanup after an error detected by poll_cond */ +static +void error_poll_cond(void) +{ + /* if we will close the connection, we assume either that + all characters have been seen or that they will be dropped. */ + shared->seen_by_valgrind = shared->written_by_vgdb; + remote_finish(reset_after_error); +} + +/* remote_desc_activity might be used at high frequency if the user + gives a small value to --vgdb-poll. So, the function avoids + doing repetitively system calls by rather looking at the + counter values maintained in shared memory by vgdb. */ +int remote_desc_activity(char *msg) +{ + int ret; + const int looking_at = shared->written_by_vgdb; + if (shared->seen_by_valgrind == looking_at) + // if (last_looked_cntr == looking_at) + return 0; + if (remote_desc == INVALID_DESCRIPTOR) + return 0; + + /* poll the remote desc */ + remote_desc_pollfdread_activity.revents = 0; + ret = VG_(poll) (&remote_desc_pollfdread_activity, 1, 0); + if (ret && poll_cond(remote_desc_pollfdread_activity.revents)) { + dlog(1, "POLLcond %d remote_desc_pollfdread %d\n", + remote_desc_pollfdread_activity.revents, remote_desc); + error_poll_cond(); + ret = 2; + } + dlog(1, + "remote_desc_activity %s %d last_looked_cntr %d looking_at %d" + " shared->written_by_vgdb %d shared->seen_by_valgrind %d" + " ret %d\n", + msg, remote_desc, last_looked_cntr, looking_at, + shared->written_by_vgdb, shared->seen_by_valgrind, + ret); + /* if no error from poll, indicate we have "seen" up to looking_at */ + if (ret != 2) + last_looked_cntr = looking_at; + return ret; +} + +/* Convert hex digit A to a number. */ + +static +int fromhex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + error ("Reply contains invalid hex digit 0x%x\n", a); + return 0; +} + +int unhexify (char *bin, const char *hex, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (hex[0] == 0 || hex[1] == 0) { + /* Hex string is short, or of uneven length. + Return the count that has been converted so far. */ + return i; + } + *bin++ = fromhex (hex[0]) * 16 + fromhex (hex[1]); + hex += 2; + } + return i; +} + +void decode_address (CORE_ADDR *addrp, const char *start, int len) +{ + CORE_ADDR addr; + char ch; + int i; + + addr = 0; + for (i = 0; i < len; i++) { + ch = start[i]; + addr = addr << 4; + addr = addr | (fromhex (ch) & 0x0f); + } + *addrp = addr; +} + +/* Convert number NIB to a hex digit. */ + +static +int tohex (int nib) +{ + if (nib < 10) + return '0' + nib; + else + return 'a' + nib - 10; +} + +int hexify (char *hex, const char *bin, int count) +{ + int i; + + /* May use a length, or a nul-terminated string as input. */ + if (count == 0) + count = strlen (bin); + + for (i = 0; i < count; i++) { + *hex++ = tohex ((*bin >> 4) & 0xf); + *hex++ = tohex (*bin++ & 0xf); + } + *hex = 0; + return i; +} + +/* Convert BUFFER, binary data at least LEN bytes long, into escaped + binary data in OUT_BUF. Set *OUT_LEN to the length of the data + encoded in OUT_BUF, and return the number of bytes in OUT_BUF + (which may be more than *OUT_LEN due to escape characters). The + total number of bytes in the output buffer will be at most + OUT_MAXLEN. */ + +int +remote_escape_output (const gdb_byte *buffer, int len, + gdb_byte *out_buf, int *out_len, + int out_maxlen) +{ + int input_index, output_index; + + output_index = 0; + for (input_index = 0; input_index < len; input_index++) { + gdb_byte b = buffer[input_index]; + + if (b == '$' || b == '#' || b == '}' || b == '*') { + /* These must be escaped. */ + if (output_index + 2 > out_maxlen) + break; + out_buf[output_index++] = '}'; + out_buf[output_index++] = b ^ 0x20; + } else { + if (output_index + 1 > out_maxlen) + break; + out_buf[output_index++] = b; + } + } + + *out_len = input_index; + return output_index; +} + +/* Convert BUFFER, escaped data LEN bytes long, into binary data + in OUT_BUF. Return the number of bytes written to OUT_BUF. + Raise an error if the total number of bytes exceeds OUT_MAXLEN. + + This function reverses remote_escape_output. It allows more + escaped characters than that function does, in particular because + '*' must be escaped to avoid the run-length encoding processing + in reading packets. */ + +static +int remote_unescape_input (const gdb_byte *buffer, int len, + gdb_byte *out_buf, int out_maxlen) +{ + int input_index, output_index; + int escaped; + + output_index = 0; + escaped = 0; + for (input_index = 0; input_index < len; input_index++) { + gdb_byte b = buffer[input_index]; + + if (output_index + 1 > out_maxlen) + error ("Received too much data (len %d) from the target.\n", len); + + if (escaped) { + out_buf[output_index++] = b ^ 0x20; + escaped = 0; + } else if (b == '}') { + escaped = 1; + } else { + out_buf[output_index++] = b; + } + } + + if (escaped) + error ("Unmatched escape character in target response.\n"); + + return output_index; +} + +/* Look for a sequence of characters which can be run-length encoded. + If there are any, update *CSUM and *P. Otherwise, output the + single character. Return the number of characters consumed. */ + +static +int try_rle (char *buf, int remaining, unsigned char *csum, char **p) +{ + int n; + + /* Always output the character. */ + *csum += buf[0]; + *(*p)++ = buf[0]; + + /* Don't go past '~'. */ + if (remaining > 97) + remaining = 97; + + for (n = 1; n < remaining; n++) + if (buf[n] != buf[0]) + break; + + /* N is the index of the first character not the same as buf[0]. + buf[0] is counted twice, so by decrementing N, we get the number + of characters the RLE sequence will replace. */ + n--; + + if (n < 3) + return 1; + + /* Skip the frame characters. The manual says to skip '+' and '-' + also, but there's no reason to. Unfortunately these two unusable + characters double the encoded length of a four byte zero + value. */ + while (n + 29 == '$' || n + 29 == '#') + n--; + + *csum += '*'; + *(*p)++ = '*'; + *csum += n + 29; + *(*p)++ = n + 29; + + return n + 1; +} + +/* Send a packet to the remote machine, with error checking. + The data of the packet is in BUF, and the length of the + packet is in CNT. Returns >= 0 on success, -1 otherwise. */ + +int putpkt_binary (char *buf, int cnt) +{ + int i; + unsigned char csum = 0; + char *buf2; + char *p; + int cc; + + buf2 = malloc (PBUFSIZ); + + /* Copy the packet into buffer BUF2, encapsulating it + and giving it a checksum. */ + + p = buf2; + *p++ = '$'; + + for (i = 0; i < cnt;) + i += try_rle (buf + i, cnt - i, &csum, &p); + + *p++ = '#'; + *p++ = tohex ((csum >> 4) & 0xf); + *p++ = tohex (csum & 0xf); + + *p = '\0'; + + /* we might have to write a pkt when out FIFO not yet/anymore opened */ + if (!ensure_write_remote_desc()) { + warning ("putpkt(write) error: no write_remote_desc\n"); + return -1; + } + + /* Send it once (noack_mode) + or send it over and over until we get a positive ack. */ + + do { + if (VG_(write) (write_remote_desc, buf2, p - buf2) != p - buf2) { + warning ("putpkt(write) error\n"); + return -1; + } + + if (noack_mode) + dlog(1, "putpkt (\"%s\"); [no ack]\n", buf2); + else + dlog(1,"putpkt (\"%s\"); [looking for ack]\n", buf2); + + if (noack_mode) + break; + + cc = readchar (1); + if (cc > 0) + dlog(1, "[received '%c' (0x%x)]\n", cc, cc); + + if (cc <= 0) { + if (cc == 0) + dlog(1, "putpkt(read): Got EOF\n"); + else + warning ("putpkt(read) error\n"); + + free (buf2); + return -1; + } + + /* Check for an input interrupt while we're here. */ + if (cc == '\003') + (*the_target->send_signal) (VKI_SIGINT); + } + while (cc != '+'); + + free (buf2); + return 1; /* Success! */ +} + +/* Send a packet to the remote machine, with error checking. The data + of the packet is in BUF, and the packet should be a NUL-terminated + string. Returns >= 0 on success, -1 otherwise. */ + +int putpkt (char *buf) +{ + return putpkt_binary (buf, strlen (buf)); +} + +void monitor_output (char *s) +{ + const int len = strlen(s); + char *buf = malloc(1 + 2*len + 1); + + buf[0] = 'O'; + hexify(buf+1, s, len); + if (putpkt (buf) < 0) { + /* We probably have lost the connection with vgdb. */ + reset_valgrind_sink("Error writing monitor output"); + /* write again after reset */ + VG_(printf) ("%s", s); + } + + free (buf); +} + +/* Returns next char from remote GDB. -1 if error. */ +/* if single, only one character maximum can be read with + read system call. Otherwise, when reading an ack character + we might pile up the next gdb command in the static buf. + The read loop is then blocked in poll till gdb times out. */ +static +int readchar (int single) +{ + static unsigned char buf[PBUFSIZ]; + static int bufcnt = 0; + static unsigned char *bufp; + int ret; + + if (bufcnt-- > 0) + return *bufp++; + + if (remote_desc == INVALID_DESCRIPTOR) + return -1; + + /* No characters available in buf => + wait for some characters to arrive */ + remote_desc_pollfdread_activity.revents = 0; + ret = VG_(poll)(&remote_desc_pollfdread_activity, 1, -1); + if (ret != 1) { + dlog(0, "readchar: poll got %d\n", ret); + return -1; + } + if (single) + bufcnt = VG_(read) (remote_desc, buf, 1); + else + bufcnt = VG_(read) (remote_desc, buf, sizeof (buf)); + + if (bufcnt <= 0) { + if (bufcnt == 0) + dlog (1, "readchar: Got EOF\n"); + else + warning ("readchar read error\n"); + + return -1; + } + + shared->seen_by_valgrind += bufcnt; + + /* If we have received a character and we do not yet have a + connection, we better open our "write" fifo to let vgdb open its + read fifo side */ + if (write_remote_desc == INVALID_DESCRIPTOR + && !ensure_write_remote_desc()) { + dlog(1, "reachar: write_remote_desc could not be created"); + } + + bufp = buf; + bufcnt--; + + if (poll_cond(remote_desc_pollfdread_activity.revents)) { + dlog(1, "readchar: POLLcond got %d\n", + remote_desc_pollfdread_activity.revents); + error_poll_cond(); + } + + return *bufp++; +} + + +/* Read a packet from the remote machine, with error checking, + and store it in BUF. Returns length of packet, or negative if error. */ + +int getpkt (char *buf) +{ + char *bp; + unsigned char csum, c1, c2; + int c; + + while (1) { + csum = 0; + + while (1) { + c = readchar (0); + if (c == '$') + break; + dlog(1, "[getpkt: discarding char '%c']\n", c); + if (c < 0) + return -1; + } + + bp = buf; + while (1) { + c = readchar (0); + if (c < 0) + return -1; + if (c == '#') + break; + *bp++ = c; + csum += c; + } + *bp = 0; + + c1 = fromhex (readchar (0)); + c2 = fromhex (readchar (0)); + + if (csum == (c1 << 4) + c2) + break; + + dlog (0, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n", + (c1 << 4) + c2, csum, buf); + if (!ensure_write_remote_desc()) { + dlog(1, "getpkt(write nack) no write_remote_desc"); + } + VG_(write) (write_remote_desc, "-", 1); + } + + if (noack_mode) + dlog(1, "getpkt (\"%s\"); [no ack] \n", buf); + else + dlog(1, "getpkt (\"%s\"); [sending ack] \n", buf); + + if (!noack_mode) { + if (!ensure_write_remote_desc()) { + dlog(1, "getpkt(write ack) no write_remote_desc"); + } + VG_(write) (write_remote_desc, "+", 1); + dlog(1, "[sent ack]\n"); + } + + return bp - buf; +} + +void write_ok (char *buf) +{ + buf[0] = 'O'; + buf[1] = 'K'; + buf[2] = '\0'; +} + +void write_enn (char *buf) +{ + /* Some day, we should define the meanings of the error codes... */ + buf[0] = 'E'; + buf[1] = '0'; + buf[2] = '1'; + buf[3] = '\0'; +} + +void convert_int_to_ascii (unsigned char *from, char *to, int n) +{ + int nib; + int ch; + while (n--) { + ch = *from++; + nib = ((ch & 0xf0) >> 4) & 0x0f; + *to++ = tohex (nib); + nib = ch & 0x0f; + *to++ = tohex (nib); + } + *to++ = 0; +} + + +void convert_ascii_to_int (char *from, unsigned char *to, int n) +{ + int nib1, nib2; + while (n--) { + nib1 = fromhex (*from++); + nib2 = fromhex (*from++); + *to++ = (((nib1 & 0x0f) << 4) & 0xf0) | (nib2 & 0x0f); + } +} + +static +char * outreg (int regno, char *buf) +{ + if ((regno >> 12) != 0) + *buf++ = tohex ((regno >> 12) & 0xf); + if ((regno >> 8) != 0) + *buf++ = tohex ((regno >> 8) & 0xf); + *buf++ = tohex ((regno >> 4) & 0xf); + *buf++ = tohex (regno & 0xf); + *buf++ = ':'; + collect_register_as_string (regno, buf); + buf += 2 * register_size (regno); + *buf++ = ';'; + + return buf; +} + +void prepare_resume_reply (char *buf, char status, unsigned char sig) +{ + int nib; + + *buf++ = status; + + nib = ((sig & 0xf0) >> 4); + *buf++ = tohex (nib); + nib = sig & 0x0f; + *buf++ = tohex (nib); + + if (status == 'T') { + const char **regp = gdbserver_expedite_regs; + + if (the_target->stopped_by_watchpoint != NULL + && (*the_target->stopped_by_watchpoint) ()) { + CORE_ADDR addr; + int i; + + strncpy (buf, "watch:", 6); + buf += 6; + + addr = (*the_target->stopped_data_address) (); + + /* Convert each byte of the address into two hexadecimal chars. + Note that we take sizeof (void *) instead of sizeof (addr); + this is to avoid sending a 64-bit address to a 32-bit GDB. */ + for (i = sizeof (void *) * 2; i > 0; i--) { + *buf++ = tohex ((addr >> (i - 1) * 4) & 0xf); + } + *buf++ = ';'; + } + + while (*regp) { + buf = outreg (find_regno (*regp), buf); + regp ++; + } + + { + unsigned int gdb_id_from_wait; + + /* FIXME right place to set this? */ + thread_from_wait = + ((struct inferior_list_entry *)current_inferior)->id; + gdb_id_from_wait = thread_to_gdb_id (current_inferior); + + dlog(1, "Writing resume reply for %ld\n", thread_from_wait); + /* This if (1) ought to be unnecessary. But remote_wait in GDB + will claim this event belongs to inferior_ptid if we do not + specify a thread, and there's no way for gdbserver to know + what inferior_ptid is. */ + if (1 || old_thread_from_wait != thread_from_wait) { + general_thread = thread_from_wait; + VG_(sprintf) (buf, "thread:%x;", gdb_id_from_wait); + buf += strlen (buf); + old_thread_from_wait = thread_from_wait; + } + } + } + /* For W and X, we're done. */ + *buf++ = 0; +} + +void decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr) +{ + int i = 0, j = 0; + char ch; + *mem_addr_ptr = *len_ptr = 0; + + while ((ch = from[i++]) != ',') { + *mem_addr_ptr = *mem_addr_ptr << 4; + *mem_addr_ptr |= fromhex (ch) & 0x0f; + } + + for (j = 0; j < 4; j++) { + if ((ch = from[i++]) == 0) + break; + *len_ptr = *len_ptr << 4; + *len_ptr |= fromhex (ch) & 0x0f; + } +} + +void decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr, + unsigned char *to) +{ + int i = 0; + char ch; + *mem_addr_ptr = *len_ptr = 0; + + while ((ch = from[i++]) != ',') { + *mem_addr_ptr = *mem_addr_ptr << 4; + *mem_addr_ptr |= fromhex (ch) & 0x0f; + } + + while ((ch = from[i++]) != ':') { + *len_ptr = *len_ptr << 4; + *len_ptr |= fromhex (ch) & 0x0f; + } + + convert_ascii_to_int (&from[i++], to, *len_ptr); +} + +int decode_X_packet (char *from, int packet_len, CORE_ADDR *mem_addr_ptr, + unsigned int *len_ptr, unsigned char *to) +{ + int i = 0; + char ch; + *mem_addr_ptr = *len_ptr = 0; + + while ((ch = from[i++]) != ',') { + *mem_addr_ptr = *mem_addr_ptr << 4; + *mem_addr_ptr |= fromhex (ch) & 0x0f; + } + + while ((ch = from[i++]) != ':') { + *len_ptr = *len_ptr << 4; + *len_ptr |= fromhex (ch) & 0x0f; + } + + if (remote_unescape_input ((const gdb_byte *) &from[i], packet_len - i, + to, *len_ptr) != *len_ptr) + return -1; + + return 0; +} + diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c new file mode 100644 index 00000000..2d3ce907 --- /dev/null +++ b/coregrind/m_gdbserver/server.c @@ -0,0 +1,971 @@ +/* Main code for remote server for GDB. + Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, + 2004, 2005, 2006, 2011 + Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "regdef.h" +#include "pub_core_options.h" +#include "pub_core_translate.h" +#include "pub_core_mallocfree.h" + +unsigned long cont_thread; +unsigned long general_thread; +unsigned long step_thread; +unsigned long thread_from_wait; +unsigned long old_thread_from_wait; + +/* for a gdbserver integrated in valgrind, resuming the process consists + in returning the control to valgrind. + Then at the next error or break or ..., valgrind calls gdbserver again. + A resume packet must then be built. + resume_packet_needed records the fact that the next call to gdbserver + must send a resume packet to gdb. */ +static Bool resume_packet_needed = False; + +VG_MINIMAL_JMP_BUF(toplevel); + +/* Decode a qXfer read request. Return 0 if everything looks OK, + or -1 otherwise. */ + +static +int decode_xfer_read (char *buf, char **annex, CORE_ADDR *ofs, unsigned int *len) +{ + /* Extract and NUL-terminate the annex. */ + *annex = buf; + while (*buf && *buf != ':') + buf++; + if (*buf == '\0') + return -1; + *buf++ = 0; + + /* After the read/write marker and annex, qXfer looks like a + traditional 'm' packet. */ + decode_m_packet (buf, ofs, len); + + return 0; +} + +/* Write the response to a successful qXfer read. Returns the + length of the (binary) data stored in BUF, corresponding + to as much of DATA/LEN as we could fit. IS_MORE controls + the first character of the response. */ +static +int write_qxfer_response (char *buf, unsigned char *data, int len, int is_more) +{ + int out_len; + + if (is_more) + buf[0] = 'm'; + else + buf[0] = 'l'; + + return remote_escape_output (data, len, (unsigned char *) buf + 1, &out_len, + PBUFSIZ - POVERHSIZ - 1) + 1; +} + +static Bool initial_valgrind_sink_saved = False; +/* True <=> valgrind log sink saved in initial_valgrind_sink */ +static OutputSink initial_valgrind_sink; + +static Bool command_output_to_log = False; +/* True <=> command output goes to log instead of gdb */ + +void reset_valgrind_sink(char *info) +{ + if (VG_(log_output_sink).fd != initial_valgrind_sink.fd + && initial_valgrind_sink_saved) { + VG_(log_output_sink).fd = initial_valgrind_sink.fd; + VG_(umsg) ("Reset valgrind output to log (%s)\n", + (info = NULL ? "" : info)); + } +} + +static +void kill_request (char *msg) +{ + VG_(umsg) ("%s", msg); + remote_close(); + VG_(exit) (0); +} + +/* handle_gdb_valgrind_command handles the provided mon string command. + If command is recognised, return 1 else return 0. + Note that in case of ambiguous command, 1 is returned. + + *sink_wanted_at_return is modified if one of the commands + 'vg.set *_output' is handled. +*/ +static +int handle_gdb_valgrind_command (char* mon, OutputSink* sink_wanted_at_return) +{ + UWord ret = 0; + char s[strlen(mon)+1]; /* copy for strtok_r */ + char* wcmd; + Char* ssaveptr; + char* endptr; + int kwdid; + int int_value; + + vg_assert (initial_valgrind_sink_saved); + + strcpy (s, mon); + wcmd = strtok_r (s, " ", &ssaveptr); + /* NB: if possible, avoid introducing a new command below which + starts with the same 4 first letters as an already existing + command. This ensures a shorter abbreviation for the user. */ + switch (VG_(keyword_id) ("help vg.set vg.info vg.wait vg.kill vg.translate", + wcmd, kwd_report_duplicated_matches)) { + case -2: + ret = 1; + break; + case -1: + break; + case 0: /* help */ + ret = 1; + wcmd = strtok_r (NULL, " ", &ssaveptr); + if (wcmd == NULL) { + int_value = 0; + } else { + switch (VG_(keyword_id) ("debug", wcmd, kwd_report_all)) { + case -2: int_value = 0; break; + case -1: int_value = 0; break; + case 0: int_value = 1; break; + default: tl_assert (0); + } + } + + VG_(gdb_printf) ( +"general valgrind monitor commands:\n" +" help [debug] : monitor command help. With debug: + debugging commands\n" +" vg.wait [<ms>] : sleep <ms> (default 0) then continue\n" +" vg.info all_errors : show all errors found so far\n" +" vg.info last_error : show last error found\n" +" vg.info n_errs_found : show the nr of errors found so far\n" +" vg.kill : kill the Valgrind process\n" +" vg.set gdb_output : set valgrind output to gdb\n" +" vg.set log_output : set valgrind output to log\n" +" vg.set mixed_output : set valgrind output to log, interactive output to gdb\n" +" vg.set vgdb-error <errornr> : debug me at error >= <errornr> \n"); + if (int_value) { VG_(gdb_printf) ( +"debugging valgrind internals monitor commands:\n" +" vg.info gdbserver_status : show gdbserver status\n" +" vg.info memory : show valgrind heap memory stats\n" +" vg.set debuglog <level> : set valgrind debug log level to <level>\n" +" vg.translate <addr> [<traceflags>] : debug translation of <addr> with <traceflags>\n" +" (default traceflags 0b00100000 : show after instrumentation)\n" +" An additional flag 0b100000000 allows to show gdbserver instrumentation\n"); + } + break; + case 1: /* vg.set */ + ret = 1; + wcmd = strtok_r (NULL, " ", &ssaveptr); + switch (kwdid = VG_(keyword_id) + ("vgdb-error debuglog gdb_output log_output mixed_output", + wcmd, kwd_report_all)) { + case -2: + case -1: + break; + case 0: /* vgdb-error */ + case 1: /* debuglog */ + wcmd = strtok_r (NULL, " ", &ssaveptr); + if (wcmd == NULL) { + int_value = 0; + endptr = "empty"; /* to report an error below */ + } else { + int_value = strtol (wcmd, &endptr, 10); + } + if (*endptr != '\0') { + VG_(gdb_printf) ("missing or malformed integer value\n"); + } else if (kwdid == 0) { + VG_(gdb_printf) ("vgdb-error value changed from %d to %d\n", + VG_(dyn_vgdb_error), int_value); + VG_(dyn_vgdb_error) = int_value; + } else if (kwdid == 1) { + VG_(gdb_printf) ("debuglog value changed from %d to %d\n", + VG_(debugLog_getLevel)(), int_value); + VG_(debugLog_startup) (int_value, "gdbsrv"); + } else { + vg_assert (0); + } + break; + case 2: /* gdb_output */ + (*sink_wanted_at_return).fd = -2; + command_output_to_log = False; + VG_(gdb_printf) ("valgrind output will go to gdb\n"); + break; + case 3: /* log_output */ + (*sink_wanted_at_return).fd = initial_valgrind_sink.fd; + command_output_to_log = True; + VG_(gdb_printf) ("valgrind output will go to log\n"); + break; + case 4: /* mixed output */ + (*sink_wanted_at_return).fd = initial_valgrind_sink.fd; + command_output_to_log = False; + VG_(gdb_printf) + ("valgrind output will go to log, interactive output will go to gdb\n"); + break; + default: + vg_assert (0); + } + break; + case 2: /* vg.info */ { + ret = 1; + wcmd = strtok_r (NULL, " ", &ssaveptr); + switch (kwdid = VG_(keyword_id) + ("all_errors n_errs_found last_error gdbserver_status memory", + wcmd, kwd_report_all)) { + case -2: + case -1: + break; + case 0: // all_errors + // A verbosity of minimum 2 is needed to show the errors. + VG_(show_all_errors)(/* verbosity */ 2, /* xml */ False); + break; + case 1: // n_errs_found + VG_(gdb_printf) ("n_errs_found %d (vgdb-error %d)\n", + VG_(get_n_errs_found) (), + VG_(dyn_vgdb_error)); + break; + case 2: // last_error + VG_(show_last_error)(); + break; + case 3: // gdbserver_status + VG_(gdbserver_status_output)(); + break; + case 4: /* memory */ + VG_(print_all_arena_stats) (); + if (VG_(clo_profile_heap)) + VG_(print_arena_cc_analysis) (); + ret = 1; + break; + default: + vg_assert(0); + } + break; + } + case 3: /* vg.wait */ + wcmd = strtok_r (NULL, " ", &ssaveptr); + if (wcmd != NULL) { + int_value = strtol (wcmd, &endptr, 10); + VG_(gdb_printf) ("gdbserver: continuing in %d ms ...\n", int_value); + VG_(poll)(NULL, 0, int_value); + } + VG_(gdb_printf) ("gdbserver: continuing after wait ...\n"); + ret = 1; + break; + case 4: /* vg.kill */ + kill_request ("monitor command request to kill this process\n"); + break; + case 5: { /* vg.translate */ + Addr address; + SizeT verbosity = 0x20; + + ret = 1; + + VG_(strtok_get_address_and_size) (&address, &verbosity, &ssaveptr); + if (address != (Addr) 0 || verbosity != 0) { + /* we need to force the output to log for the translation trace, + as low level VEX tracing cannot be redirected to gdb. */ + int saved_command_output_to_log = command_output_to_log; + int saved_fd = VG_(log_output_sink).fd; + Bool single_stepping_on_entry = valgrind_single_stepping(); + int vex_verbosity = verbosity & 0xff; + VG_(log_output_sink).fd = initial_valgrind_sink.fd; + if ((verbosity & 0x100) && !single_stepping_on_entry) { + valgrind_set_single_stepping(True); + // to force gdbserver instrumentation. + } + VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to debugging*/, + address, + /*debugging*/True, + (Int) vex_verbosity, + /*bbs_done*/0, + /*allow redir?*/True); + if ((verbosity & 0x100) && !single_stepping_on_entry) { + valgrind_set_single_stepping(False); + // reset single stepping. + } + command_output_to_log = saved_command_output_to_log; + VG_(log_output_sink).fd = saved_fd; + } + break; + } + + default: + vg_assert (0); + } + return ret; +} + +/* handle_gdb_monitor_command handles the provided mon string command, + which can be either a "standard" valgrind monitor command + or a tool specific monitor command. + If command recognised, return 1 else return 0. + Note that in case of ambiguous command, 1 is returned. +*/ +static +int handle_gdb_monitor_command (char* mon) +{ + UWord ret = 0; + UWord tool_ret = 0; + // initially, we assume that when returning, the desired sink is the + // one we have when entering. It can however be changed by the standard + // valgrind command handling. + OutputSink sink_wanted_at_return = VG_(log_output_sink); + + if (!initial_valgrind_sink_saved) { + /* first time we enter here, we save the valgrind default log sink */ + initial_valgrind_sink = sink_wanted_at_return; + initial_valgrind_sink_saved = True; + } + + if (!command_output_to_log) + VG_(log_output_sink).fd = -2; /* redirect to monitor_output */ + + ret = handle_gdb_valgrind_command (mon, &sink_wanted_at_return); + + /* Even if command was recognised by valgrind core, we call the + tool command handler : this is needed to handle help command + and/or to let the tool do some additional processing of a + valgrind standard command. Note however that if valgrind + recognised the command, we will always return success. */ + if (VG_(needs).client_requests) { + /* If the tool reports an error when handling a monitor command, + we need to avoid calling gdbserver during this command + handling. So, we temporarily set VG_(dyn_vgdb_error) to + a huge value to ensure m_errormgr.c does not call gdbserver. */ + Int save_dyn_vgdb_error = VG_(dyn_vgdb_error); + UWord arg[2]; + VG_(dyn_vgdb_error) = 999999999; + arg[0] = (UWord) VG_USERREQ__GDB_MONITOR_COMMAND; + arg[1] = (UWord) mon; + VG_TDICT_CALL(tool_handle_client_request, VG_(running_tid), arg, + &tool_ret); + VG_(dyn_vgdb_error) = save_dyn_vgdb_error; + } + + /* restore or set the desired output */ + VG_(log_output_sink).fd = sink_wanted_at_return.fd; + if (ret | tool_ret) + return 1; + else + return 0; +} + + +/* Handle all of the extended 'Q' packets. */ +static +void handle_set (char *arg_own_buf, int *new_packet_len_p) +{ + if (strcmp ("QStartNoAckMode", arg_own_buf) == 0) { + noack_mode = True; + write_ok (arg_own_buf); + return; + } + + if (strncmp ("QPassSignals:", arg_own_buf, 13) == 0) { + int i; + char *from, *to; + char *end = arg_own_buf + strlen(arg_own_buf); + CORE_ADDR sig; + for (i = 0; i < TARGET_SIGNAL_LAST; i++) + pass_signals[i] = 0; + + from = arg_own_buf + 13; + while (from < end) { + to = strchr(from, ';'); + if (to == NULL) to = end; + decode_address (&sig, from, to - from); + pass_signals[(int)sig] = 1; + dlog(1, "pass_signal %d\n", (int)sig); + from = to; + if (*from == ';') from++; + } + write_ok (arg_own_buf); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + arg_own_buf[0] = 0; +} + +/* Handle all of the extended 'q' packets. */ +static +void handle_query (char *arg_own_buf, int *new_packet_len_p) +{ + static struct inferior_list_entry *thread_ptr; + + /* qRcmd, monitor command handling. */ + if (strncmp ("qRcmd,", arg_own_buf, 6) == 0) { + char *p = arg_own_buf + 6; + int cmdlen = strlen(p)/2; + char cmd[cmdlen+1]; + + if (unhexify (cmd, p, cmdlen) != cmdlen) { + write_enn (arg_own_buf); + return; + } + cmd[cmdlen] = '\0'; + + if (handle_gdb_monitor_command (cmd)) { + /* In case the command is from a standalone vgdb, + connection will be closed soon => flush the output. */ + VG_(message_flush) (); + write_ok (arg_own_buf); + return; + } else { + /* cmd not recognised */ + VG_(gdb_printf) + ("command '%s' not recognised\n" + "In gdb, try 'monitor help'\n" + "In a shell, try 'vgdb help'\n", + cmd); + write_ok (arg_own_buf); + return; + } + } + + /* provide some valgrind specific info in return to qThreadExtraInfo. */ + if (strncmp ("qThreadExtraInfo,", arg_own_buf, 17) == 0) { + unsigned long gdb_id; + struct thread_info *ti; + ThreadState *tst; + char status[100]; + + gdb_id = strtoul (&arg_own_buf[17], NULL, 16); + ti = gdb_id_to_thread (gdb_id); + if (ti != NULL) { + tst = (ThreadState *) inferior_target_data (ti); + /* Additional info is the tid and the thread status. */ + VG_(snprintf) (status, sizeof(status), "tid %d %s", + tst->tid, + VG_(name_of_ThreadStatus)(tst->status)); + hexify (arg_own_buf, status, strlen(status)); + return; + } else { + write_enn (arg_own_buf); + return; + } + } + + if (strcmp ("qAttached", arg_own_buf) == 0) { + /* tell gdb to always detach, never kill the process */ + arg_own_buf[0] = '1'; + arg_own_buf[1] = 0; + return; + } + + if (strcmp ("qSymbol::", arg_own_buf) == 0) { + /* We have no symbol to read. */ + write_ok (arg_own_buf); + return; + } + + if (strcmp ("qfThreadInfo", arg_own_buf) == 0) { + thread_ptr = all_threads.head; + VG_(sprintf) (arg_own_buf, "m%x", + thread_to_gdb_id ((struct thread_info *)thread_ptr)); + thread_ptr = thread_ptr->next; + return; + } + + if (strcmp ("qsThreadInfo", arg_own_buf) == 0) { + if (thread_ptr != NULL) { + VG_(sprintf) (arg_own_buf, "m%x", + thread_to_gdb_id ((struct thread_info *)thread_ptr)); + thread_ptr = thread_ptr->next; + return; + } else { + VG_(sprintf) (arg_own_buf, "l"); + return; + } + } + + if ( ((*the_target->target_xml)() != NULL + || (*the_target->shadow_target_xml)() != NULL) + && strncmp ("qXfer:features:read:", arg_own_buf, 20) == 0) { + CORE_ADDR ofs; + unsigned int len, doc_len; + char *annex = NULL; + // First, the annex is extracted from the packet received. + // Then, it is replaced by the corresponding file name. + int fd; + + /* Grab the annex, offset, and length. */ + if (decode_xfer_read (arg_own_buf + 20, &annex, &ofs, &len) < 0) { + strcpy (arg_own_buf, "E00"); + return; + } + + if (strcmp (annex, "target.xml") == 0) { + annex = NULL; // to replace it by the corresponding filename. + + /* If VG_(clo_vgdb_shadow_registers), try to use + shadow_target_xml. Fallback to target_xml + if not defined. */ + if (VG_(clo_vgdb_shadow_registers)) { + annex = (*the_target->shadow_target_xml)(); + if (annex != NULL) + /* Ensure the shadow registers are initialized. */ + initialize_shadow_low(True); + } + if (annex == NULL) + annex = (*the_target->target_xml)(); + if (annex == NULL) { + strcpy (arg_own_buf, "E00"); + return; + } + } + + { + char doc[VG_(strlen)(VG_(libdir)) + 1 + VG_(strlen)(annex)]; + struct vg_stat stat_doc; + char toread[len]; + int len_read; + + VG_(sprintf)(doc, "%s/%s", VG_(libdir), annex); + fd = VG_(fd_open) (doc, VKI_O_RDONLY, 0); + if (fd == -1) { + strcpy (arg_own_buf, "E00"); + return; + } + if (VG_(fstat) (fd, &stat_doc) != 0) { + VG_(close) (fd); + strcpy (arg_own_buf, "E00"); + return; + } + doc_len = stat_doc.size; + + if (len > PBUFSIZ - POVERHSIZ) + len = PBUFSIZ - POVERHSIZ; + + if (ofs > doc_len) { + write_enn (arg_own_buf); + VG_(close) (fd); + return; + } + VG_(lseek) (fd, ofs, VKI_SEEK_SET); + len_read = VG_(read) (fd, toread, len); + *new_packet_len_p = write_qxfer_response (arg_own_buf, toread, + len_read, ofs + len_read < doc_len); + VG_(close) (fd); + return; + } + } + + /* Protocol features query. */ + if (strncmp ("qSupported", arg_own_buf, 10) == 0 + && (arg_own_buf[10] == ':' || arg_own_buf[10] == '\0')) { + VG_(sprintf) (arg_own_buf, "PacketSize=%x", PBUFSIZ - 1); + /* Note: max packet size including frame and checksum, but without + trailing null byte, which is not sent/received. */ + + strcat (arg_own_buf, ";QStartNoAckMode+"); + strcat (arg_own_buf, ";QPassSignals+"); + + if ((*the_target->target_xml)() != NULL + || (*the_target->shadow_target_xml)() != NULL) { + strcat (arg_own_buf, ";qXfer:features:read+"); + /* if a new gdb connects to us, we have to reset the register + set to the normal register sets to allow this new gdb to + decide to use or not the shadow registers. + + Note that the reset is only done for gdb that are sending + qSupported packets. If a user first connected with a recent + gdb using shadow registers and then with a very old gdb + that does not use qSupported packet, then the old gdb will + not properly connect. */ + initialize_shadow_low(False); + } + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + arg_own_buf[0] = 0; +} + +/* Handle all of the extended 'v' packets. */ +static +void handle_v_requests (char *arg_own_buf, char *status, int *signal) +{ + /* vcont packet code from gdb 6.6 removed */ + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + arg_own_buf[0] = 0; + return; +} + +static +void myresume (int step, int sig) +{ + struct thread_resume resume_info[2]; + int n = 0; + + if (step || sig || (cont_thread != 0 && cont_thread != -1)) { + resume_info[0].thread + = ((struct inferior_list_entry *) current_inferior)->id; + resume_info[0].step = step; + resume_info[0].sig = sig; + resume_info[0].leave_stopped = 0; + n++; + } + resume_info[n].thread = -1; + resume_info[n].step = 0; + resume_info[n].sig = 0; + resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1); + + resume_packet_needed = True; + (*the_target->resume) (resume_info); +} + +/* server_main global variables */ +static char *own_buf; +static unsigned char *mem_buf; + +void gdbserver_init (void) +{ + dlog(1, "gdbserver_init gdbserver embedded in valgrind: %s\n", version); + noack_mode = False; + initialize_low (); + own_buf = malloc (PBUFSIZ); + mem_buf = malloc (PBUFSIZ); +} + +void gdbserver_terminate (void) +{ + /* last call to gdbserver is cleanup call */ + if (VG_MINIMAL_SETJMP(toplevel)) { + dlog(0, "error caused VG_MINIMAL_LONGJMP to gdbserver_terminate\n"); + return; + } + remote_close(); +} + +void server_main (void) +{ + static char status; + static int signal; + + char ch; + int i = 0; + unsigned int len; + CORE_ADDR mem_addr; + + signal = mywait (&status); + if (VG_MINIMAL_SETJMP(toplevel)) { + dlog(0, "error caused VG_MINIMAL_LONGJMP to server_main\n"); + } + while (1) { + unsigned char sig; + int packet_len; + int new_packet_len = -1; + + if (resume_packet_needed) { + resume_packet_needed = False; + prepare_resume_reply (own_buf, status, signal); + putpkt (own_buf); + } + + packet_len = getpkt (own_buf); + if (packet_len <= 0) + break; + + i = 0; + ch = own_buf[i++]; + switch (ch) { + case 'Q': + handle_set (own_buf, &new_packet_len); + break; + case 'q': + handle_query (own_buf, &new_packet_len); + break; + case 'd': + /* set/unset debugging is done through valgrind debug level. */ + own_buf[0] = '\0'; + break; + case 'D': + reset_valgrind_sink("gdb detaching from process"); + + /* When detaching or kill the process, gdb expects to get + an packet OK back. Any other output will make gdb + believes detach did not work. */ + write_ok (own_buf); + putpkt (own_buf); + remote_finish (reset_after_error); + remote_open (VG_(clo_vgdb_prefix)); + myresume (0, 0); + resume_packet_needed = False; + return; + case '!': + /* We can not use the extended protocol with valgrind, + because we can not restart the running + program. So return unrecognized. */ + own_buf[0] = '\0'; + break; + case '?': + prepare_resume_reply (own_buf, status, signal); + break; + case 'H': + if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') { + unsigned long gdb_id, thread_id; + + gdb_id = strtoul (&own_buf[2], NULL, 16); + thread_id = gdb_id_to_thread_id (gdb_id); + if (thread_id == 0) { + write_enn (own_buf); + break; + } + + if (own_buf[1] == 'g') { + general_thread = thread_id; + set_desired_inferior (1); + } else if (own_buf[1] == 'c') { + cont_thread = thread_id; + } else if (own_buf[1] == 's') { + step_thread = thread_id; + } + + write_ok (own_buf); + } else { + /* Silently ignore it so that gdb can extend the protocol + without compatibility headaches. */ + own_buf[0] = '\0'; + } + break; + case 'g': + set_desired_inferior (1); + registers_to_string (own_buf); + break; + case 'G': + set_desired_inferior (1); + registers_from_string (&own_buf[1]); + write_ok (own_buf); + break; + case 'P': { + int regno; + char *regbytes; + Bool mod; + ThreadState *tst; + regno = strtol(&own_buf[1], NULL, 16); + regbytes = strchr(&own_buf[0], '=') + 1; + set_desired_inferior (1); + tst = (ThreadState *) inferior_target_data (current_inferior); + /* Only accept changing registers in "runnable state3. + In fact, it would be ok to change most of the registers + except a few "sensitive" registers such as the PC, SP, BP. + We assume we do not need to very specific here, and that we + can just refuse all of these. */ + if (tst->status == VgTs_Runnable || tst->status == VgTs_Yielding) { + supply_register_from_string (regno, regbytes, &mod); + write_ok (own_buf); + } else { + /* at least from gdb 6.6 onwards, an E. error + reply is shown to the user. So, we do an error + msg which both is accepted by gdb as an error msg + and is readable by the user. */ + VG_(sprintf) + (own_buf, +"E.\n" +"ERROR changing register %s regno %d\n" +"gdb commands changing registers (pc, sp, ...) (e.g. 'jump',\n" +"set pc, calling from gdb a function in the debugged process, ...)\n" +"can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state\n" +"Thread status is %s\n", + find_register_by_number (regno)->name, regno, + VG_(name_of_ThreadStatus)(tst->status)); + if (VG_(clo_verbosity) > 1) + VG_(umsg) ("%s\n", own_buf); + } + break; + } + case 'm': + decode_m_packet (&own_buf[1], &mem_addr, &len); + if (read_inferior_memory (mem_addr, mem_buf, len) == 0) + convert_int_to_ascii (mem_buf, own_buf, len); + else + write_enn (own_buf); + break; + case 'M': + decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); + if (write_inferior_memory (mem_addr, mem_buf, len) == 0) + write_ok (own_buf); + else + write_enn (own_buf); + break; + case 'X': + if (decode_X_packet (&own_buf[1], packet_len - 1, + &mem_addr, &len, mem_buf) < 0 + || write_inferior_memory (mem_addr, mem_buf, len) != 0) + write_enn (own_buf); + else + write_ok (own_buf); + break; + case 'C': + convert_ascii_to_int (own_buf + 1, &sig, 1); + if (target_signal_to_host_p (sig)) + signal = target_signal_to_host (sig); + else + signal = 0; + set_desired_inferior (0); + myresume (0, signal); + return; // return control to valgrind + case 'S': + convert_ascii_to_int (own_buf + 1, &sig, 1); + if (target_signal_to_host_p (sig)) + signal = target_signal_to_host (sig); + else + signal = 0; + set_desired_inferior (0); + myresume (1, signal); + return; // return control to valgrind + case 'c': + set_desired_inferior (0); + myresume (0, 0); + return; // return control to valgrind + case 's': + set_desired_inferior (0); + myresume (1, 0); + return; // return control to valgrind + case 'Z': { + char *lenptr; + char *dataptr; + CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); + int zlen = strtol (lenptr + 1, &dataptr, 16); + char type = own_buf[1]; + + if (the_target->insert_watchpoint == NULL + || (type < '0' || type > '4')) { + /* No watchpoint support or not a watchpoint command; + unrecognized either way. */ + own_buf[0] = '\0'; + } else { + int res; + + res = (*the_target->insert_watchpoint) (type, addr, zlen); + if (res == 0) + write_ok (own_buf); + else if (res == 1) + /* Unsupported. */ + own_buf[0] = '\0'; + else + write_enn (own_buf); + } + break; + } + case 'z': { + char *lenptr; + char *dataptr; + CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); + int zlen = strtol (lenptr + 1, &dataptr, 16); + char type = own_buf[1]; + + if (the_target->remove_watchpoint == NULL + || (type < '0' || type > '4')) { + /* No watchpoint support or not a watchpoint command; + unrecognized either way. */ + own_buf[0] = '\0'; + } else { + int res; + + res = (*the_target->remove_watchpoint) (type, addr, zlen); + if (res == 0) + write_ok (own_buf); + else if (res == 1) + /* Unsupported. */ + own_buf[0] = '\0'; + else + write_enn (own_buf); + } + break; + } + case 'k': + kill_request("Gdb request to kill this process\n"); + break; + case 'T': { + unsigned long gdb_id, thread_id; + + gdb_id = strtoul (&own_buf[1], NULL, 16); + thread_id = gdb_id_to_thread_id (gdb_id); + if (thread_id == 0) { + write_enn (own_buf); + break; + } + + if (mythread_alive (thread_id)) + write_ok (own_buf); + else + write_enn (own_buf); + break; + } + case 'R': + /* Restarting the inferior is only supported in the + extended protocol. + => It is a request we don't understand. Respond with an + empty packet so that gdb knows that we don't support this + request. */ + own_buf[0] = '\0'; + break; + case 'v': + /* Extended (long) request. */ + handle_v_requests (own_buf, &status, &signal); + break; + default: + /* It is a request we don't understand. Respond with an + empty packet so that gdb knows that we don't support this + request. */ + own_buf[0] = '\0'; + break; + } + + if (new_packet_len != -1) + putpkt_binary (own_buf, new_packet_len); + else + putpkt (own_buf); + + if (status == 'W') + VG_(umsg) ("\nChild exited with status %d\n", signal); + if (status == 'X') + VG_(umsg) ("\nChild terminated with signal = 0x%x (%s)\n", + target_signal_to_host (signal), + target_signal_to_name (signal)); + if (status == 'W' || status == 'X') { + VG_(umsg) ("Process exiting\n"); + VG_(exit) (0); + } + } + + /* We come here when getpkt fails => close the connection, + and re-open. Then return control to valgrind. + We return the control to valgrind as we assume that + the connection was closed due to vgdb having finished + to execute a command. */ + if (VG_(clo_verbosity) > 1) + VG_(umsg) ("Remote side has terminated connection. " + "GDBserver will reopen the connection.\n"); + remote_finish (reset_after_error); + remote_open (VG_(clo_vgdb_prefix)); + myresume (0, 0); + resume_packet_needed = False; + return; +} diff --git a/coregrind/m_gdbserver/server.h b/coregrind/m_gdbserver/server.h new file mode 100644 index 00000000..2b6ae0e0 --- /dev/null +++ b/coregrind/m_gdbserver/server.h @@ -0,0 +1,371 @@ +/* Common definitions for remote server for GDB. + Copyright (C) 1993, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, + 2006 + Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef SERVER_H +#define SERVER_H + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_debuglog.h" +#include "pub_core_errormgr.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcprint.h" +#include "pub_core_mallocfree.h" +#include "pub_core_syscall.h" +#include "pub_tool_libcproc.h" +#include "pub_core_tooliface.h" +#include "pub_tool_libcassert.h" +#include "pub_tool_libcbase.h" +#include "pub_tool_options.h" +#include "pub_core_gdbserver.h" +#include "pub_tool_libcsetjmp.h" +#include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_tool_vki.h" +#include "valgrind.h" + +/*------------- interface m_gdbserver <=> low level gdbserver */ + +/* Initializes gdbserver. After a call to gdbserver_init, vgdb + can contact the gdbserver embedded in valgrind. + The rest of the low level gdbserver interface can only + be called */ +extern void gdbserver_init (void); + +extern void server_main (void); + +/* To be called to indicate that gdbserver usage is finished. + Resources (e.g. FIFOs) will be destroyed. */ +extern void gdbserver_terminate (void); + + +/* Output string s to the gdb debugging this process or to vgdb. + Do not call this directly. Rather use VG_(monitor_print) + to output something to gdb, use normal valgrind messaging + (e.g. VG_(umsg)) to send output that can either go + to gdb or to log. */ +extern void monitor_output (char *s); + +/* returns 0 if there is no connection or no event on the connection + with gdb. + returns 1 if there are some data which has been received from gdb + and that must (still) be handled. + returns 2 if remote_desc_activity detected the connection has been + lost and should be reopened. + msg is used for debug logging.*/ +extern int remote_desc_activity(char *msg); + +/* output some status of gdbserver communication */ +extern void remote_utils_output_status(void); + +/* True if there is a connection with gdb. */ +extern Bool remote_connected(void); + +/* Finish the connection with gdb and reset_valgrind_sink. + Keeps the FIFOs and shared mem so as to allow connection + to be reopened. */ +extern void remote_finish(FinishReason reason); + +/* If Valgrind sink was changed by gdbserver: + Resets the valgrind sink to before the changes done by gdbserver, + and does VG_(umsg). If info != NULL, info added in VG_(usmg). */ +extern void reset_valgrind_sink(char* info); + + +/* True if gdbserver is single stepping the valgrind process */ +extern Bool valgrind_single_stepping(void); + +/* Set Valgrind in single stepping mode or not according to Bool. */ +extern void valgrind_set_single_stepping(Bool); + +/* gets the addr at which a (possible) break must be ignored once. + If there is no such break to be ignored once, 0 is returned. + This is needed for the following case: + The user sets a break at address AAA. + The break is encountered. Then the user does stepi + (i.e. step one instruction). + In such a case, the already encountered break must be ignored + to ensure the stepi will advance by one instruction: a "break" + is implemented in valgrind by some helper code just after the + instruction mark at which the break is set. This helper code + verifies if either there is a break at the current PC + or if we are in stepping mode. If we are in stepping mode, + the already encountered break must be ignored once to advance + to the next instruction. + ??? need to check if this is *really* needed. */ +extern Addr valgrind_get_ignore_break_once(void); + +/* When addr > 0, ensures the next stop reply packet informs + gdb about the encountered watchpoint. + Use addr 0x0 to reset. */ +extern void VG_(set_watchpoint_stop_address) (Addr addr); + +/* when invoked by vgdb using ptrace, contains the tid chosen + by vgdb (if vgdb gives a tid different of 0: a 0 tid by + vgdb means use the running_tid if there is one running + or tid 1 otherwise). */ +extern ThreadId vgdb_interrupted_tid; + +/*------------ end of interface to low level gdbserver */ + + +#define dlog(level, ...) \ + do { if (UNLIKELY(VG_(debugLog_getLevel)() >= level)) \ + VG_(debugLog) (level, "gdbsrv",__VA_ARGS__); } \ + while (0) + + +/* vki only defines VKI_POLLIN but even not on all OS. + Below is from linux bits/poll.h */ +#ifndef VKI_POLLIN +#define VKI_POLLIN 0x0001 +#endif +#define VKI_POLLPRI 0x0002 +#define VKI_POLLOUT 0x0004 +#define VKI_POLLERR 0x0008 +#define VKI_POLLHUP 0x0010 +#define VKI_POLLNVAL 0x0020 + +/* a bunch of macros to avoid libc usage in valgrind-ified gdbserver */ +#define strcmp(s1,s2) VG_(strcmp) ((Char *)(s1),(Char *)(s2)) +#define strncmp(s1,s2,nmax) VG_(strncmp) ((Char *)(s1),(Char *)(s2),nmax) +#define strcat(s1,s2) VG_(strcat) ((Char *)(s1),(Char *)(s2)) +#define strcpy(s1,s2) VG_(strcpy) ((Char *)(s1),(Char *)(s2)) +#define strncpy(s1,s2,nmax) VG_(strncpy) ((Char *)(s1),(Char *)(s2),nmax) +#define strlen(s) VG_(strlen) ((Char *)(s)) +#define strtok(p,s) (char *) VG_(strtok) ((Char *)(p),(Char *)(s)) +#define strtok_r(p,s,ss) (char *) VG_(strtok_r) ((Char *)(p),(Char *)(s),(Char **)(ss)) +#define strchr(s,c) (char *) VG_(strchr) ((Char *)(s),c) +/* strtol and strtoul supports base 16 or else assumes it is base 10 */ +#define strtol(s,r,b) ((b) == 16 ? \ + VG_(strtoll16) ((Char *)(s),(Char **)(r)) \ + : VG_(strtoll10) ((Char *)(s),(Char **)(r))) +#define strtoul(s,r,b) ((b) == 16 ? \ + VG_(strtoull16) ((Char *)(s),(Char **)(r)) \ + : VG_(strtoull10) ((Char *)(s),(Char **)(r))) + +#define malloc(sz) VG_(arena_malloc) (VG_AR_CORE, "gdbsrv", sz) +#define calloc(n,sz) VG_(arena_calloc) (VG_AR_CORE, "gdbsrv", n, sz) +#define realloc(p,size) VG_(arena_realloc) (VG_AR_CORE, "gdbsrv", p, size) +#define strdup(s) (char *) VG_(arena_strdup) (VG_AR_CORE, "gdbsrv", (Char *)(s)) +#define free(b) VG_(arena_free) (VG_AR_CORE, b) + +#ifndef ATTR_NORETURN +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)) +#define ATTR_NORETURN __attribute__ ((noreturn)) +#else +#define ATTR_NORETURN /* nothing */ +#endif +#endif + +#ifndef ATTR_FORMAT +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 4)) +#define ATTR_FORMAT(type, x, y) __attribute__ ((format(type, x, y))) +#else +#define ATTR_FORMAT(type, x, y) /* nothing */ +#endif +#endif + +/* A type used for binary buffers. */ +typedef unsigned char gdb_byte; + +typedef Addr CORE_ADDR; + +/* Generic information for tracking a list of ``inferiors'' - threads, + processes, etc. */ +struct inferior_list +{ + struct inferior_list_entry *head; + struct inferior_list_entry *tail; +}; +struct inferior_list_entry +{ + unsigned long id; + struct inferior_list_entry *next; +}; + +/* Opaque type for user-visible threads. */ +struct thread_info; + +#include "regcache.h" +#include "gdb/signals.h" + +/* signal handling with gdbserver: before delivering a signal, + call gdbserver_signal_encountered then give control to + gdbserver by calling call_gdbserver. + On return, call gdbserver_deliver_signal to effectively + deliver the signal or not. */ +extern void gdbserver_signal_encountered (Int sigNo); +/* between these two calls, call call_gdbserver */ +/* If gdbserver_deliver_signal True, then gdb did not ask + to ignore the signal, so signal can be delivered to the guest. */ +extern Bool gdbserver_deliver_signal (Int sigNo); + +/* To optimise signal handling, gdb can instruct gdbserver to + not stop on some signals. In the below, a 1 indicates the signal + has to be passed directly to the guest, without asking gdb. + A 0 indicates gdb has to be consulted to see if signal has + or has not to be passed. The gdb consultation is to + be done using the above two functions. */ +int pass_signals[TARGET_SIGNAL_LAST]; + + +#include "target.h" + +/* Target-specific functions */ + +void initialize_low (void); + +/* initialize or re-initialize the register set of the low target. + if shadow_mode, then (re-)define the normal and valgrind shadow registers + else (re-)define only the normal registers. */ +void initialize_shadow_low (Bool shadow_mode); + +/* From inferiors.c. */ + +extern struct inferior_list all_threads; +void add_inferior_to_list (struct inferior_list *list, + struct inferior_list_entry *new_inferior); +void for_each_inferior (struct inferior_list *list, + void (*action) (struct inferior_list_entry *)); +extern struct thread_info *current_inferior; +void remove_inferior (struct inferior_list *list, + struct inferior_list_entry *entry); +void remove_thread (struct thread_info *thread); +void add_thread (unsigned long thread_id, void *target_data, unsigned int); +unsigned int thread_id_to_gdb_id (unsigned long); +unsigned int thread_to_gdb_id (struct thread_info *); +unsigned long gdb_id_to_thread_id (unsigned int); +struct thread_info *gdb_id_to_thread (unsigned int); +void clear_inferiors (void); +struct inferior_list_entry *find_inferior (struct inferior_list *, + int (*func) (struct + inferior_list_entry *, + void *), + void *arg); +struct inferior_list_entry *find_inferior_id (struct inferior_list *list, + unsigned long id); +void *inferior_target_data (struct thread_info *); +void set_inferior_target_data (struct thread_info *, void *); +void *inferior_regcache_data (struct thread_info *); +void set_inferior_regcache_data (struct thread_info *, void *); +void change_inferior_id (struct inferior_list *list, + unsigned long new_id); + +/* Public variables in server.c */ + +extern unsigned long cont_thread; +extern unsigned long general_thread; +extern unsigned long step_thread; +extern unsigned long thread_from_wait; +extern unsigned long old_thread_from_wait; + +extern VG_MINIMAL_JMP_BUF(toplevel); + +/* From remote-utils.c */ + +extern Bool noack_mode; +int putpkt (char *buf); +int putpkt_binary (char *buf, int len); +int getpkt (char *buf); +void remote_open (char *name); +void remote_close (void); + +void sync_gdb_connection (void); +void write_ok (char *buf); +void write_enn (char *buf); +void convert_ascii_to_int (char *from, unsigned char *to, int n); +void convert_int_to_ascii (unsigned char *from, char *to, int n); +void prepare_resume_reply (char *buf, char status, unsigned char sig); + +void decode_address (CORE_ADDR *addrp, const char *start, int len); +void decode_m_packet (char *from, CORE_ADDR * mem_addr_ptr, + unsigned int *len_ptr); +void decode_M_packet (char *from, CORE_ADDR * mem_addr_ptr, + unsigned int *len_ptr, unsigned char *to); +int decode_X_packet (char *from, int packet_len, CORE_ADDR * mem_addr_ptr, + unsigned int *len_ptr, unsigned char *to); + +int unhexify (char *bin, const char *hex, int count); +int hexify (char *hex, const char *bin, int count); +int remote_escape_output (const gdb_byte *buffer, int len, + gdb_byte *out_buf, int *out_len, + int out_maxlen); + +/* Functions from ``signals.c''. */ +enum target_signal target_signal_from_host (int hostsig); +int target_signal_to_host_p (enum target_signal oursig); +int target_signal_to_host (enum target_signal oursig); +char *target_signal_to_name (enum target_signal); + +/* Functions from utils.c */ + +/* error is like VG_(umsg), then VG_MINIMAL_LONGJMP to gdbserver toplevel. */ +void error (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); +/* first output a description of the error inside sr, then like VG_(umsg). */ +void sr_perror (SysRes sr,char *string,...) ATTR_FORMAT (printf, 2, 3); +/* fatal is like VG_(umsg), then exit(1). */ +void fatal (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); +/* warning is like VG_(umsg). */ +void warning (const char *string,...) ATTR_FORMAT (printf, 1, 2); + +/* Functions from the register cache definition. */ + +void init_registers (void); + +/* Maximum number of bytes to read/write at once. The value here + is chosen to fill up a packet (the headers account for the 32). */ +#define MAXBUFBYTES(N) (((N)-32)/2) + +/* PBUFSIZ : Buffers size for transferring memory, registers, etc. + Must be big enough to hold all the registers, at least. + Must be at least big as 2*DATASIZ + 5: + 1 : packet begin ($ or %) + + 2*DATASIZ : encoded string + + 1 : packet end (#) + + 2 : packet checksum + + 1 : \0 + + Max value gdb likes is 16384. + + Note that what is sent/received to/from gdb does + not have a trailing null byte. We are adding 1 here to allow + null terminating the strings e.g. for printf. + + => packet Packet OVERHead SIZe is 5:*/ + +/* keep PBUFSIZ value in sync with vgdb.c */ +#define PBUFSIZ 16384 +#define POVERHSIZ 5 + +/* Max size of a string encoded in a packet. Hex Encoding can + multiply the size by 2 (trailing null byte not sent). */ +#define DATASIZ ((PBUFSIZ-POVERHSIZ)/2) + +/* Version information, from version.c. */ +extern const char version[]; + +#endif /* SERVER_H */ diff --git a/coregrind/m_gdbserver/signals.c b/coregrind/m_gdbserver/signals.c new file mode 100644 index 00000000..07b0c248 --- /dev/null +++ b/coregrind/m_gdbserver/signals.c @@ -0,0 +1,744 @@ +/* Target signal translation functions for GDB. + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2011 Free Software Foundation, Inc. + Contributed by Cygnus Support. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" + +#if defined(VGO_darwin) +// ???? darwin signal.h defines SIGPOLL conditionnally ???? +#ifndef SIGPOLL +#define SIGPOLL 7 +#endif +#endif + +enum target_signal target_signal_from_name (char *name); +enum target_signal target_signal_from_command (int num); + +/* This table must match in order and size the signals in enum target_signal + in gdb/signals.h. */ +/* *INDENT-OFF* */ +static struct { + char *name; + char *string; +} signals [] = + { + {"0", "Signal 0"}, + {"SIGHUP", "Hangup"}, + {"SIGINT", "Interrupt"}, + {"SIGQUIT", "Quit"}, + {"SIGILL", "Illegal instruction"}, + {"SIGTRAP", "Trace/breakpoint trap"}, + {"SIGABRT", "Aborted"}, + {"SIGEMT", "Emulation trap"}, + {"SIGFPE", "Arithmetic exception"}, + {"SIGKILL", "Killed"}, + {"SIGBUS", "Bus error"}, + {"SIGSEGV", "Segmentation fault"}, + {"SIGSYS", "Bad system call"}, + {"SIGPIPE", "Broken pipe"}, + {"SIGALRM", "Alarm clock"}, + {"SIGTERM", "Terminated"}, + {"SIGURG", "Urgent I/O condition"}, + {"SIGSTOP", "Stopped (signal)"}, + {"SIGTSTP", "Stopped (user)"}, + {"SIGCONT", "Continued"}, + {"SIGCHLD", "Child status changed"}, + {"SIGTTIN", "Stopped (tty input)"}, + {"SIGTTOU", "Stopped (tty output)"}, + {"SIGIO", "I/O possible"}, + {"SIGXCPU", "CPU time limit exceeded"}, + {"SIGXFSZ", "File size limit exceeded"}, + {"SIGVTALRM", "Virtual timer expired"}, + {"SIGPROF", "Profiling timer expired"}, + {"SIGWINCH", "Window size changed"}, + {"SIGLOST", "Resource lost"}, + {"SIGUSR1", "User defined signal 1"}, + {"SIGUSR2", "User defined signal 2"}, + {"SIGPWR", "Power fail/restart"}, + {"SIGPOLL", "Pollable event occurred"}, + {"SIGWIND", "SIGWIND"}, + {"SIGPHONE", "SIGPHONE"}, + {"SIGWAITING", "Process's LWPs are blocked"}, + {"SIGLWP", "Signal LWP"}, + {"SIGDANGER", "Swap space dangerously low"}, + {"SIGGRANT", "Monitor mode granted"}, + {"SIGRETRACT", "Need to relinquish monitor mode"}, + {"SIGMSG", "Monitor mode data available"}, + {"SIGSOUND", "Sound completed"}, + {"SIGSAK", "Secure attention"}, + {"SIGPRIO", "SIGPRIO"}, + {"SIG33", "Real-time event 33"}, + {"SIG34", "Real-time event 34"}, + {"SIG35", "Real-time event 35"}, + {"SIG36", "Real-time event 36"}, + {"SIG37", "Real-time event 37"}, + {"SIG38", "Real-time event 38"}, + {"SIG39", "Real-time event 39"}, + {"SIG40", "Real-time event 40"}, + {"SIG41", "Real-time event 41"}, + {"SIG42", "Real-time event 42"}, + {"SIG43", "Real-time event 43"}, + {"SIG44", "Real-time event 44"}, + {"SIG45", "Real-time event 45"}, + {"SIG46", "Real-time event 46"}, + {"SIG47", "Real-time event 47"}, + {"SIG48", "Real-time event 48"}, + {"SIG49", "Real-time event 49"}, + {"SIG50", "Real-time event 50"}, + {"SIG51", "Real-time event 51"}, + {"SIG52", "Real-time event 52"}, + {"SIG53", "Real-time event 53"}, + {"SIG54", "Real-time event 54"}, + {"SIG55", "Real-time event 55"}, + {"SIG56", "Real-time event 56"}, + {"SIG57", "Real-time event 57"}, + {"SIG58", "Real-time event 58"}, + {"SIG59", "Real-time event 59"}, + {"SIG60", "Real-time event 60"}, + {"SIG61", "Real-time event 61"}, + {"SIG62", "Real-time event 62"}, + {"SIG63", "Real-time event 63"}, + {"SIGCANCEL", "LWP internal signal"}, + {"SIG32", "Real-time event 32"}, + {"SIG64", "Real-time event 64"}, + {"SIG65", "Real-time event 65"}, + {"SIG66", "Real-time event 66"}, + {"SIG67", "Real-time event 67"}, + {"SIG68", "Real-time event 68"}, + {"SIG69", "Real-time event 69"}, + {"SIG70", "Real-time event 70"}, + {"SIG71", "Real-time event 71"}, + {"SIG72", "Real-time event 72"}, + {"SIG73", "Real-time event 73"}, + {"SIG74", "Real-time event 74"}, + {"SIG75", "Real-time event 75"}, + {"SIG76", "Real-time event 76"}, + {"SIG77", "Real-time event 77"}, + {"SIG78", "Real-time event 78"}, + {"SIG79", "Real-time event 79"}, + {"SIG80", "Real-time event 80"}, + {"SIG81", "Real-time event 81"}, + {"SIG82", "Real-time event 82"}, + {"SIG83", "Real-time event 83"}, + {"SIG84", "Real-time event 84"}, + {"SIG85", "Real-time event 85"}, + {"SIG86", "Real-time event 86"}, + {"SIG87", "Real-time event 87"}, + {"SIG88", "Real-time event 88"}, + {"SIG89", "Real-time event 89"}, + {"SIG90", "Real-time event 90"}, + {"SIG91", "Real-time event 91"}, + {"SIG92", "Real-time event 92"}, + {"SIG93", "Real-time event 93"}, + {"SIG94", "Real-time event 94"}, + {"SIG95", "Real-time event 95"}, + {"SIG96", "Real-time event 96"}, + {"SIG97", "Real-time event 97"}, + {"SIG98", "Real-time event 98"}, + {"SIG99", "Real-time event 99"}, + {"SIG100", "Real-time event 100"}, + {"SIG101", "Real-time event 101"}, + {"SIG102", "Real-time event 102"}, + {"SIG103", "Real-time event 103"}, + {"SIG104", "Real-time event 104"}, + {"SIG105", "Real-time event 105"}, + {"SIG106", "Real-time event 106"}, + {"SIG107", "Real-time event 107"}, + {"SIG108", "Real-time event 108"}, + {"SIG109", "Real-time event 109"}, + {"SIG110", "Real-time event 110"}, + {"SIG111", "Real-time event 111"}, + {"SIG112", "Real-time event 112"}, + {"SIG113", "Real-time event 113"}, + {"SIG114", "Real-time event 114"}, + {"SIG115", "Real-time event 115"}, + {"SIG116", "Real-time event 116"}, + {"SIG117", "Real-time event 117"}, + {"SIG118", "Real-time event 118"}, + {"SIG119", "Real-time event 119"}, + {"SIG120", "Real-time event 120"}, + {"SIG121", "Real-time event 121"}, + {"SIG122", "Real-time event 122"}, + {"SIG123", "Real-time event 123"}, + {"SIG124", "Real-time event 124"}, + {"SIG125", "Real-time event 125"}, + {"SIG126", "Real-time event 126"}, + {"SIG127", "Real-time event 127"}, + + {"SIGINFO", "Information request"}, + + {NULL, "Unknown signal"}, + {NULL, "Internal error: printing TARGET_SIGNAL_DEFAULT"}, + + /* Mach exceptions */ + {"EXC_BAD_ACCESS", "Could not access memory"}, + {"EXC_BAD_INSTRUCTION", "Illegal instruction/operand"}, + {"EXC_ARITHMETIC", "Arithmetic exception"}, + {"EXC_EMULATION", "Emulation instruction"}, + {"EXC_SOFTWARE", "Software generated exception"}, + {"EXC_BREAKPOINT", "Breakpoint"}, + + /* Last entry, used to check whether the table is the right size. */ + {NULL, "TARGET_SIGNAL_MAGIC"} + }; +/* *INDENT-ON* */ + + + +/* Return the name for a signal. */ +char *target_signal_to_name (enum target_signal sig) +{ + if ((sig >= TARGET_SIGNAL_FIRST) && (sig <= TARGET_SIGNAL_LAST) + && signals[sig].name != NULL) + return signals[sig].name; + else + /* I think the code which prints this will always print it along + with the string, so no need to be verbose (very old comment). */ + return "?"; +} + +/* Given a name, return its signal. */ +enum target_signal target_signal_from_name (char *name) +{ + enum target_signal sig; + + /* It's possible we also should allow "SIGCLD" as well as "SIGCHLD" + for TARGET_SIGNAL_SIGCHLD. SIGIOT, on the other hand, is more + questionable; seems like by now people should call it SIGABRT + instead. */ + + /* This ugly cast brought to you by the native VAX compiler. */ + for (sig = TARGET_SIGNAL_HUP; + sig < TARGET_SIGNAL_LAST; + sig = (enum target_signal) ((int) sig + 1)) + if (signals[sig].name != NULL + && strcmp (name, signals[sig].name) == 0) + return sig; + return TARGET_SIGNAL_UNKNOWN; +} + + +/* The following functions are to help certain targets deal + with the signal/waitstatus stuff. They could just as well be in + a file called native-utils.c or unixwaitstatus-utils.c or whatever. */ + +/* Convert host signal to our signals. */ +enum target_signal target_signal_from_host (int hostsig) +{ + /* A switch statement would make sense but would require special kludges + to deal with the cases where more than one signal has the same number. */ + + if (hostsig == 0) + return TARGET_SIGNAL_0; + +#if defined (VKI_SIGHUP) + if (hostsig == VKI_SIGHUP) + return TARGET_SIGNAL_HUP; +#endif +#if defined (VKI_SIGINT) + if (hostsig == VKI_SIGINT) + return TARGET_SIGNAL_INT; +#endif +#if defined (VKI_SIGQUIT) + if (hostsig == VKI_SIGQUIT) + return TARGET_SIGNAL_QUIT; +#endif +#if defined (VKI_SIGILL) + if (hostsig == VKI_SIGILL) + return TARGET_SIGNAL_ILL; +#endif +#if defined (VKI_SIGTRAP) + if (hostsig == VKI_SIGTRAP) + return TARGET_SIGNAL_TRAP; +#endif +#if defined (VKI_SIGABRT) + if (hostsig == VKI_SIGABRT) + return TARGET_SIGNAL_ABRT; +#endif +#if defined (VKI_SIGEMT) + if (hostsig == VKI_SIGEMT) + return TARGET_SIGNAL_EMT; +#endif +#if defined (VKI_SIGFPE) + if (hostsig == VKI_SIGFPE) + return TARGET_SIGNAL_FPE; +#endif +#if defined (VKI_SIGKILL) + if (hostsig == VKI_SIGKILL) + return TARGET_SIGNAL_KILL; +#endif +#if defined (VKI_SIGBUS) + if (hostsig == VKI_SIGBUS) + return TARGET_SIGNAL_BUS; +#endif +#if defined (VKI_SIGSEGV) + if (hostsig == VKI_SIGSEGV) + return TARGET_SIGNAL_SEGV; +#endif +#if defined (VKI_SIGSYS) + if (hostsig == VKI_SIGSYS) + return TARGET_SIGNAL_SYS; +#endif +#if defined (VKI_SIGPIPE) + if (hostsig == VKI_SIGPIPE) + return TARGET_SIGNAL_PIPE; +#endif +#if defined (VKI_SIGALRM) + if (hostsig == VKI_SIGALRM) + return TARGET_SIGNAL_ALRM; +#endif +#if defined (VKI_SIGTERM) + if (hostsig == VKI_SIGTERM) + return TARGET_SIGNAL_TERM; +#endif +#if defined (VKI_SIGUSR1) + if (hostsig == VKI_SIGUSR1) + return TARGET_SIGNAL_USR1; +#endif +#if defined (VKI_SIGUSR2) + if (hostsig == VKI_SIGUSR2) + return TARGET_SIGNAL_USR2; +#endif +#if defined (VKI_SIGCLD) + if (hostsig == VKI_SIGCLD) + return TARGET_SIGNAL_CHLD; +#endif +#if defined (VKI_SIGCHLD) + if (hostsig == VKI_SIGCHLD) + return TARGET_SIGNAL_CHLD; +#endif +#if defined (VKI_SIGPWR) + if (hostsig == VKI_SIGPWR) + return TARGET_SIGNAL_PWR; +#endif +#if defined (VKI_SIGWINCH) + if (hostsig == VKI_SIGWINCH) + return TARGET_SIGNAL_WINCH; +#endif +#if defined (VKI_SIGURG) + if (hostsig == VKI_SIGURG) + return TARGET_SIGNAL_URG; +#endif +#if defined (VKI_SIGIO) + if (hostsig == VKI_SIGIO) + return TARGET_SIGNAL_IO; +#endif +#if defined (VKI_SIGPOLL) + if (hostsig == VKI_SIGPOLL) + return TARGET_SIGNAL_POLL; +#endif +#if defined (VKI_SIGSTOP) + if (hostsig == VKI_SIGSTOP) + return TARGET_SIGNAL_STOP; +#endif +#if defined (VKI_SIGTSTP) + if (hostsig == VKI_SIGTSTP) + return TARGET_SIGNAL_TSTP; +#endif +#if defined (VKI_SIGCONT) + if (hostsig == VKI_SIGCONT) + return TARGET_SIGNAL_CONT; +#endif +#if defined (VKI_SIGTTIN) + if (hostsig == VKI_SIGTTIN) + return TARGET_SIGNAL_TTIN; +#endif +#if defined (VKI_SIGTTOU) + if (hostsig == VKI_SIGTTOU) + return TARGET_SIGNAL_TTOU; +#endif +#if defined (VKI_SIGVTALRM) + if (hostsig == VKI_SIGVTALRM) + return TARGET_SIGNAL_VTALRM; +#endif +#if defined (VKI_SIGPROF) + if (hostsig == VKI_SIGPROF) + return TARGET_SIGNAL_PROF; +#endif +#if defined (VKI_SIGXCPU) + if (hostsig == VKI_SIGXCPU) + return TARGET_SIGNAL_XCPU; +#endif +#if defined (VKI_SIGXFSZ) + if (hostsig == VKI_SIGXFSZ) + return TARGET_SIGNAL_XFSZ; +#endif +#if defined (VKI_SIGWIND) + if (hostsig == VKI_SIGWIND) + return TARGET_SIGNAL_WIND; +#endif +#if defined (VKI_SIGPHONE) + if (hostsig == VKI_SIGPHONE) + return TARGET_SIGNAL_PHONE; +#endif +#if defined (VKI_SIGLOST) + if (hostsig == VKI_SIGLOST) + return TARGET_SIGNAL_LOST; +#endif +#if defined (VKI_SIGWAITING) + if (hostsig == VKI_SIGWAITING) + return TARGET_SIGNAL_WAITING; +#endif +#if defined (VKI_SIGCANCEL) + if (hostsig == VKI_SIGCANCEL) + return TARGET_SIGNAL_CANCEL; +#endif +#if defined (VKI_SIGLWP) + if (hostsig == VKI_SIGLWP) + return TARGET_SIGNAL_LWP; +#endif +#if defined (VKI_SIGDANGER) + if (hostsig == VKI_SIGDANGER) + return TARGET_SIGNAL_DANGER; +#endif +#if defined (VKI_SIGGRANT) + if (hostsig == VKI_SIGGRANT) + return TARGET_SIGNAL_GRANT; +#endif +#if defined (VKI_SIGRETRACT) + if (hostsig == VKI_SIGRETRACT) + return TARGET_SIGNAL_RETRACT; +#endif +#if defined (VKI_SIGMSG) + if (hostsig == VKI_SIGMSG) + return TARGET_SIGNAL_MSG; +#endif +#if defined (VKI_SIGSOUND) + if (hostsig == VKI_SIGSOUND) + return TARGET_SIGNAL_SOUND; +#endif +#if defined (VKI_SIGSAK) + if (hostsig == VKI_SIGSAK) + return TARGET_SIGNAL_SAK; +#endif +#if defined (VKI_SIGPRIO) + if (hostsig == VKI_SIGPRIO) + return TARGET_SIGNAL_PRIO; +#endif + + /* Mach exceptions. Assumes that the values for EXC_ are positive! */ +#if defined (EXC_BAD_ACCESS) && defined (_NSIG) + if (hostsig == _NSIG + EXC_BAD_ACCESS) + return TARGET_EXC_BAD_ACCESS; +#endif +#if defined (EXC_BAD_INSTRUCTION) && defined (_NSIG) + if (hostsig == _NSIG + EXC_BAD_INSTRUCTION) + return TARGET_EXC_BAD_INSTRUCTION; +#endif +#if defined (EXC_ARITHMETIC) && defined (_NSIG) + if (hostsig == _NSIG + EXC_ARITHMETIC) + return TARGET_EXC_ARITHMETIC; +#endif +#if defined (EXC_EMULATION) && defined (_NSIG) + if (hostsig == _NSIG + EXC_EMULATION) + return TARGET_EXC_EMULATION; +#endif +#if defined (EXC_SOFTWARE) && defined (_NSIG) + if (hostsig == _NSIG + EXC_SOFTWARE) + return TARGET_EXC_SOFTWARE; +#endif +#if defined (EXC_BREAKPOINT) && defined (_NSIG) + if (hostsig == _NSIG + EXC_BREAKPOINT) + return TARGET_EXC_BREAKPOINT; +#endif + +#if defined (VKI_SIGINFO) + if (hostsig == VKI_SIGINFO) + return TARGET_SIGNAL_INFO; +#endif + + return TARGET_SIGNAL_UNKNOWN; +} + +/* Convert a OURSIG (an enum target_signal) to the form used by the + target operating system (refered to as the ``host'') or zero if the + equivalent host signal is not available. Set/clear OURSIG_OK + accordingly. */ + +static +int do_target_signal_to_host (enum target_signal oursig, + int *oursig_ok) +{ + *oursig_ok = 1; + switch (oursig) { + case TARGET_SIGNAL_0: + return 0; + +#if defined (VKI_SIGHUP) + case TARGET_SIGNAL_HUP: + return VKI_SIGHUP; +#endif +#if defined (VKI_SIGINT) + case TARGET_SIGNAL_INT: + return VKI_SIGINT; +#endif +#if defined (VKI_SIGQUIT) + case TARGET_SIGNAL_QUIT: + return VKI_SIGQUIT; +#endif +#if defined (VKI_SIGILL) + case TARGET_SIGNAL_ILL: + return VKI_SIGILL; +#endif +#if defined (VKI_SIGTRAP) + case TARGET_SIGNAL_TRAP: + return VKI_SIGTRAP; +#endif +#if defined (VKI_SIGABRT) + case TARGET_SIGNAL_ABRT: + return VKI_SIGABRT; +#endif +#if defined (VKI_SIGEMT) + case TARGET_SIGNAL_EMT: + return VKI_SIGEMT; +#endif +#if defined (VKI_SIGFPE) + case TARGET_SIGNAL_FPE: + return VKI_SIGFPE; +#endif +#if defined (VKI_SIGKILL) + case TARGET_SIGNAL_KILL: + return VKI_SIGKILL; +#endif +#if defined (VKI_SIGBUS) + case TARGET_SIGNAL_BUS: + return VKI_SIGBUS; +#endif +#if defined (VKI_SIGSEGV) + case TARGET_SIGNAL_SEGV: + return VKI_SIGSEGV; +#endif +#if defined (VKI_SIGSYS) + case TARGET_SIGNAL_SYS: + return VKI_SIGSYS; +#endif +#if defined (VKI_SIGPIPE) + case TARGET_SIGNAL_PIPE: + return VKI_SIGPIPE; +#endif +#if defined (VKI_SIGALRM) + case TARGET_SIGNAL_ALRM: + return VKI_SIGALRM; +#endif +#if defined (VKI_SIGTERM) + case TARGET_SIGNAL_TERM: + return VKI_SIGTERM; +#endif +#if defined (VKI_SIGUSR1) + case TARGET_SIGNAL_USR1: + return VKI_SIGUSR1; +#endif +#if defined (VKI_SIGUSR2) + case TARGET_SIGNAL_USR2: + return VKI_SIGUSR2; +#endif +#if defined (VKI_SIGCHLD) || defined (VKI_SIGCLD) + case TARGET_SIGNAL_CHLD: +#if defined (VKI_SIGCHLD) + return VKI_SIGCHLD; +#else + return VKI_SIGCLD; +#endif +#endif /* SIGCLD or SIGCHLD */ +#if defined (VKI_SIGPWR) + case TARGET_SIGNAL_PWR: + return VKI_SIGPWR; +#endif +#if defined (VKI_SIGWINCH) + case TARGET_SIGNAL_WINCH: + return VKI_SIGWINCH; +#endif +#if defined (VKI_SIGURG) + case TARGET_SIGNAL_URG: + return VKI_SIGURG; +#endif +#if defined (VKI_SIGIO) + case TARGET_SIGNAL_IO: + return VKI_SIGIO; +#endif +#if defined (VKI_SIGPOLL) + case TARGET_SIGNAL_POLL: + return VKI_SIGPOLL; +#endif +#if defined (VKI_SIGSTOP) + case TARGET_SIGNAL_STOP: + return VKI_SIGSTOP; +#endif +#if defined (VKI_SIGTSTP) + case TARGET_SIGNAL_TSTP: + return VKI_SIGTSTP; +#endif +#if defined (VKI_SIGCONT) + case TARGET_SIGNAL_CONT: + return VKI_SIGCONT; +#endif +#if defined (VKI_SIGTTIN) + case TARGET_SIGNAL_TTIN: + return VKI_SIGTTIN; +#endif +#if defined (VKI_SIGTTOU) + case TARGET_SIGNAL_TTOU: + return VKI_SIGTTOU; +#endif +#if defined (VKI_SIGVTALRM) + case TARGET_SIGNAL_VTALRM: + return VKI_SIGVTALRM; +#endif +#if defined (VKI_SIGPROF) + case TARGET_SIGNAL_PROF: + return VKI_SIGPROF; +#endif +#if defined (VKI_SIGXCPU) + case TARGET_SIGNAL_XCPU: + return VKI_SIGXCPU; +#endif +#if defined (VKI_SIGXFSZ) + case TARGET_SIGNAL_XFSZ: + return VKI_SIGXFSZ; +#endif +#if defined (VKI_SIGWIND) + case TARGET_SIGNAL_WIND: + return VKI_SIGWIND; +#endif +#if defined (VKI_SIGPHONE) + case TARGET_SIGNAL_PHONE: + return VKI_SIGPHONE; +#endif +#if defined (VKI_SIGLOST) + case TARGET_SIGNAL_LOST: + return VKI_SIGLOST; +#endif +#if defined (VKI_SIGWAITING) + case TARGET_SIGNAL_WAITING: + return VKI_SIGWAITING; +#endif +#if defined (VKI_SIGCANCEL) + case TARGET_SIGNAL_CANCEL: + return VKI_SIGCANCEL; +#endif +#if defined (VKI_SIGLWP) + case TARGET_SIGNAL_LWP: + return VKI_SIGLWP; +#endif +#if defined (VKI_SIGDANGER) + case TARGET_SIGNAL_DANGER: + return VKI_SIGDANGER; +#endif +#if defined (VKI_SIGGRANT) + case TARGET_SIGNAL_GRANT: + return VKI_SIGGRANT; +#endif +#if defined (VKI_SIGRETRACT) + case TARGET_SIGNAL_RETRACT: + return VKI_SIGRETRACT; +#endif +#if defined (VKI_SIGMSG) + case TARGET_SIGNAL_MSG: + return VKI_SIGMSG; +#endif +#if defined (VKI_SIGSOUND) + case TARGET_SIGNAL_SOUND: + return VKI_SIGSOUND; +#endif +#if defined (VKI_SIGSAK) + case TARGET_SIGNAL_SAK: + return VKI_SIGSAK; +#endif +#if defined (VKI_SIGPRIO) + case TARGET_SIGNAL_PRIO: + return VKI_SIGPRIO; +#endif + + /* Mach exceptions. Assumes that the values for EXC_ are positive! */ +#if defined (EXC_BAD_ACCESS) && defined (_NSIG) + case TARGET_EXC_BAD_ACCESS: + return _NSIG + EXC_BAD_ACCESS; +#endif +#if defined (EXC_BAD_INSTRUCTION) && defined (_NSIG) + case TARGET_EXC_BAD_INSTRUCTION: + return _NSIG + EXC_BAD_INSTRUCTION; +#endif +#if defined (EXC_ARITHMETIC) && defined (_NSIG) + case TARGET_EXC_ARITHMETIC: + return _NSIG + EXC_ARITHMETIC; +#endif +#if defined (EXC_EMULATION) && defined (_NSIG) + case TARGET_EXC_EMULATION: + return _NSIG + EXC_EMULATION; +#endif +#if defined (EXC_SOFTWARE) && defined (_NSIG) + case TARGET_EXC_SOFTWARE: + return _NSIG + EXC_SOFTWARE; +#endif +#if defined (EXC_BREAKPOINT) && defined (_NSIG) + case TARGET_EXC_BREAKPOINT: + return _NSIG + EXC_BREAKPOINT; +#endif + +#if defined (VKI_SIGINFO) + case TARGET_SIGNAL_INFO: + return VKI_SIGINFO; +#endif + + default: + *oursig_ok = 0; + return 0; + } +} + +int target_signal_to_host_p (enum target_signal oursig) +{ + int oursig_ok; + do_target_signal_to_host (oursig, &oursig_ok); + return oursig_ok; +} + +int target_signal_to_host (enum target_signal oursig) +{ + int oursig_ok; + int targ_signo = do_target_signal_to_host (oursig, &oursig_ok); + if (!oursig_ok) { + /* The user might be trying to do "signal SIGSAK" where this system + doesn't have SIGSAK. */ + warning ("Signal %s does not exist on this system.\n", + target_signal_to_name (oursig)); + return 0; + } else { + return targ_signo; + } +} + +/* In some circumstances we allow a command to specify a numeric + signal. The idea is to keep these circumstances limited so that + users (and scripts) develop portable habits. For comparison, + POSIX.2 `kill' requires that 1,2,3,6,9,14, and 15 work (and using a + numeric signal at all is obsolescent. We are slightly more + lenient and allow 1-15 which should match host signal numbers on + most systems. Use of symbolic signal names is strongly encouraged. */ + +enum target_signal target_signal_from_command (int num) +{ + if (num >= 1 && num <= 15) + return (enum target_signal) num; + error ("Only signals 1-15 are valid as numeric signals.\n\ +Use \"info signals\" for a list of symbolic signals.\n"); +} diff --git a/coregrind/m_gdbserver/target.c b/coregrind/m_gdbserver/target.c new file mode 100644 index 00000000..ae61c94c --- /dev/null +++ b/coregrind/m_gdbserver/target.c @@ -0,0 +1,121 @@ +/* Target operations for the remote server for GDB. + Copyright (C) 2002, 2004, 2005, 2011 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" + +struct target_ops *the_target; + +void set_desired_inferior (int use_general) +{ + struct thread_info *found; + + if (use_general == 1) { + found = (struct thread_info *) find_inferior_id (&all_threads, + general_thread); + } else { + found = NULL; + + /* If we are continuing any (all) thread(s), use step_thread + to decide which thread to step and/or send the specified + signal to. */ + if ((step_thread != 0 && step_thread != -1) + && (cont_thread == 0 || cont_thread == -1)) + found = (struct thread_info *) find_inferior_id (&all_threads, + step_thread); + + if (found == NULL) + found = (struct thread_info *) find_inferior_id (&all_threads, + cont_thread); + } + + if (found == NULL) + current_inferior = (struct thread_info *) all_threads.head; + else + current_inferior = found; + { + ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); + ThreadId tid = tst->tid; + dlog(1, "set_desired_inferior use_general %d found %p tid %d lwpid %d\n", + use_general, found, tid, tst->os_state.lwpid); + } +} + +int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int res; + res = (*the_target->read_memory) (memaddr, myaddr, len); + return res; +} + +int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, + int len) +{ + /* Lacking cleanups, there is some potential for a memory leak if the + write fails and we go through error(). Make sure that no more than + one buffer is ever pending by making BUFFER static. */ + static unsigned char *buffer = 0; + int res; + + if (buffer != NULL) + free (buffer); + + buffer = malloc (len); + VG_(memcpy) (buffer, myaddr, len); + res = (*the_target->write_memory) (memaddr, buffer, len); + free (buffer); + buffer = NULL; + + return res; +} + +void set_target_ops (struct target_ops *target) +{ + the_target = (struct target_ops *) malloc (sizeof (*the_target)); + VG_(memcpy) (the_target, target, sizeof (*the_target)); +} + +void* VG_(dmemcpy) ( void *d, const void *s, SizeT sz, Bool *mod ) +{ + if (VG_(memcmp) (d, s, sz)) { + *mod = True; + return VG_(memcpy) (d, s, sz); + } else { + *mod = False; + return d; + } +} + +void VG_(transfer) (void *valgrind, + void *gdbserver, + transfer_direction dir, + SizeT sz, + Bool *mod) +{ + if (dir == valgrind_to_gdbserver) + VG_(dmemcpy) (gdbserver, valgrind, sz, mod); + else if (dir == gdbserver_to_valgrind) + VG_(dmemcpy) (valgrind, gdbserver, sz, mod); + else + vg_assert (0); +} diff --git a/coregrind/m_gdbserver/target.h b/coregrind/m_gdbserver/target.h new file mode 100644 index 00000000..d657438a --- /dev/null +++ b/coregrind/m_gdbserver/target.h @@ -0,0 +1,176 @@ +/* Target operations for the remote server for GDB. + Copyright (C) 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef TARGET_H +#define TARGET_H + +/* This structure describes how to resume a particular thread (or + all threads) based on the client's request. If thread is -1, then + this entry applies to all threads. These are generally passed around + as an array, and terminated by a thread == -1 entry. */ + +struct thread_resume +{ + unsigned long thread; + + /* If non-zero, leave this thread stopped. */ + int leave_stopped; + + /* If non-zero, we want to single-step. */ + int step; + + /* If non-zero, send this signal when we resume. */ + int sig; +}; + +struct target_ops +{ + /* Return 1 iff the thread with process ID PID is alive. */ + + int (*thread_alive) (unsigned long pid); + + /* Resume the inferior process. */ + + void (*resume) (struct thread_resume *resume_info); + + /* Wait for the inferior process to change state. + + STATUS will be filled in with a response code to send to GDB. + + Returns the signal which caused the process to stop, in the + remote protocol numbering (e.g. TARGET_SIGNAL_STOP), or the + exit code as an integer if *STATUS is 'W'. */ + + unsigned char (*wait) (char *status); + + /* Fetch registers from the inferior process. + + If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */ + + void (*fetch_registers) (int regno); + + /* Store registers to the inferior process. + + If REGNO is -1, store all registers; otherwise, store at least REGNO. */ + + void (*store_registers) (int regno); + + /* Read memory from the inferior process. This should generally be + called through read_inferior_memory, which handles breakpoint shadowing. + + Read LEN bytes at MEMADDR into a buffer at MYADDR. + + Returns 0 on success and errno on failure. */ + + int (*read_memory) (CORE_ADDR memaddr, unsigned char *myaddr, int len); + + /* Write memory to the inferior process. This should generally be + called through write_inferior_memory, which handles breakpoint shadowing. + + Write LEN bytes from the buffer at MYADDR to MEMADDR. + + Returns 0 on success and errno on failure. */ + + int (*write_memory) (CORE_ADDR memaddr, const unsigned char *myaddr, + int len); + + /* Send a signal to the inferior process, however is appropriate. */ + void (*send_signal) (int); + + /* Returns the name of the xml target description file. + returns NULL if no xml target description available. */ + char* (*target_xml)(void); + + /* Same but describes also the shadow registers. */ + char* (*shadow_target_xml)(void); + + /* Insert and remove a hardware watchpoint. + Returns 0 on success, -1 on failure and 1 on unsupported. + The type is coded as follows: + 2 = write watchpoint + 3 = read watchpoint + 4 = access watchpoint + */ + + int (*insert_watchpoint) (char type, CORE_ADDR addr, int len); + int (*remove_watchpoint) (char type, CORE_ADDR addr, int len); + + /* Returns 1 if target was stopped due to a watchpoint hit, 0 otherwise. */ + + int (*stopped_by_watchpoint) (void); + + /* Returns the address associated with the watchpoint that hit, if any; + returns 0 otherwise. */ + + CORE_ADDR (*stopped_data_address) (void); + +}; + +extern struct target_ops *the_target; + +void set_target_ops (struct target_ops *); + +#define detach_inferior() \ + (*the_target->detach) () + +#define mythread_alive(pid) \ + (*the_target->thread_alive) (pid) + +#define fetch_inferior_registers(regno) \ + (*the_target->fetch_registers) (regno) + +#define store_inferior_registers(regno) \ + (*the_target->store_registers) (regno) + +#define mywait(statusp) \ + (*the_target->wait) (statusp) + +int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len); + +int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, + int len); + +void set_desired_inferior (int id); + +/* like memcpy but first check if content of destination and source + differs. If no difference, no copy is done, *mod set to False. + If different; copy is done, *mod set to True. */ +extern void* VG_(dmemcpy) ( void *d, const void *s, SizeT sz, Bool *mod ); + +typedef + enum { + valgrind_to_gdbserver, + gdbserver_to_valgrind} transfer_direction; + +// According to dir, calls VG_(dmemcpy) +// to copy data from/to valgrind to/from gdbserver. +// If the transferred data differs from what is currently stored, +// sets *mod to True otherwise set *mod to False. +extern void VG_(transfer) (void *valgrind, + void *gdbserver, + transfer_direction dir, + SizeT sz, + Bool *mod); + +#endif /* TARGET_H */ diff --git a/coregrind/m_gdbserver/utils.c b/coregrind/m_gdbserver/utils.c new file mode 100644 index 00000000..57db1751 --- /dev/null +++ b/coregrind/m_gdbserver/utils.c @@ -0,0 +1,88 @@ +/* General utility routines for the remote server for GDB. + Copyright (C) 1986, 1989, 1993, 1995, 1996, 1997, 1999, 2000, 2002, 2003, + 2011 + Free Software Foundation, Inc. + + This file is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +/* Generally useful subroutines used throughout the program. */ + +/* Print the system error message for sr. + Then print the rest of the args. */ +void sr_perror (SysRes sr,char *string,...) +{ + va_list args; + if (sr_isError (sr)) + VG_(umsg) ("error %ld %s\n", sr_Err(sr), VG_(strerror) (sr_Err(sr))); + else + VG_(umsg) ("sr_perror called with no error!!!\n"); + va_start (args, string); + VG_(vmessage) ( Vg_UserMsg, string, args ); + va_end (args); +} + +/* Print an error message and return to command level. + STRING is the error message, used as a fprintf string, + and ARG is passed as an argument to it. */ + +void error (const char *string,...) +{ + va_list args; + va_start (args, string); + VG_(vmessage) ( Vg_UserMsg, string, args ); + va_end(args); + VG_MINIMAL_LONGJMP(toplevel); +} + +/* Print an error message and exit reporting failure. + This is for a error that we cannot continue from. + STRING and ARG are passed to fprintf. */ + +/* VARARGS */ +void fatal (const char *string,...) +{ + va_list args; + va_start (args, string); + VG_(vmessage) ( Vg_UserMsg, string, args ); + va_end (args); + VG_(exit) (1); +} + +/* VARARGS */ +void warning (const char *string,...) +{ + va_list args; + va_start (args, string); + VG_(vmessage) ( Vg_UserMsg, string, args ); + va_end (args); +} + +#if 0 +/* print timestamp */ +static +void dbgts(void) +{ + struct vki_timeval dbgtv; + SysRes res; + res = VG_(do_syscall2)(__NR_gettimeofday, (UWord)&dbgtv, (UWord)NULL); + // gettimeofday(&dbgtv, NULL); + dlog(0, "%ld.%6ld ", dbgtv.tv_sec, dbgtv.tv_usec); +} +#endif diff --git a/coregrind/m_gdbserver/valgrind-low-amd64.c b/coregrind/m_gdbserver/valgrind-low-amd64.c new file mode 100644 index 00000000..0dc0382a --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low-amd64.c @@ -0,0 +1,306 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" + +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" + +#include "valgrind_low.h" + +#include "libvex_guest_amd64.h" +/* GDBTD: ??? have a cleaner way to get the f80 <> f64 conversion functions */ +/* below include needed for conversion f80 <> f64 */ +#include "../../VEX/priv/guest_generic_x87.h" + +/* below loosely inspired from file generated with gdb regdat.sh */ + +struct reg regs[] = { + { "rax", 0, 64 }, + { "rbx", 64, 64 }, + { "rcx", 128, 64 }, + { "rdx", 192, 64 }, + { "rsi", 256, 64 }, + { "rdi", 320, 64 }, + { "rbp", 384, 64 }, + { "rsp", 448, 64 }, + { "r8", 512, 64 }, + { "r9", 576, 64 }, + { "r10", 640, 64 }, + { "r11", 704, 64 }, + { "r12", 768, 64 }, + { "r13", 832, 64 }, + { "r14", 896, 64 }, + { "r15", 960, 64 }, + { "rip", 1024, 64 }, + { "eflags", 1088, 32 }, + { "cs", 1120, 32 }, + { "ss", 1152, 32 }, + { "ds", 1184, 32 }, + { "es", 1216, 32 }, + { "fs", 1248, 32 }, + { "gs", 1280, 32 }, + { "st0", 1312, 80 }, + { "st1", 1392, 80 }, + { "st2", 1472, 80 }, + { "st3", 1552, 80 }, + { "st4", 1632, 80 }, + { "st5", 1712, 80 }, + { "st6", 1792, 80 }, + { "st7", 1872, 80 }, + { "fctrl", 1952, 32 }, + { "fstat", 1984, 32 }, + { "ftag", 2016, 32 }, + { "fiseg", 2048, 32 }, + { "fioff", 2080, 32 }, + { "foseg", 2112, 32 }, + { "fooff", 2144, 32 }, + { "fop", 2176, 32 }, + { "xmm0", 2208, 128 }, + { "xmm1", 2336, 128 }, + { "xmm2", 2464, 128 }, + { "xmm3", 2592, 128 }, + { "xmm4", 2720, 128 }, + { "xmm5", 2848, 128 }, + { "xmm6", 2976, 128 }, + { "xmm7", 3104, 128 }, + { "xmm8", 3232, 128 }, + { "xmm9", 3360, 128 }, + { "xmm10", 3488, 128 }, + { "xmm11", 3616, 128 }, + { "xmm12", 3744, 128 }, + { "xmm13", 3872, 128 }, + { "xmm14", 4000, 128 }, + { "xmm15", 4128, 128 }, + { "mxcsr", 4256, 32 }, +#if defined(VGO_linux) + { "orig_rax", 4288, 64 } +#endif +}; +static const char *expedite_regs[] = { "rbp", "rsp", "rip", 0 }; +#define num_regs (sizeof (regs) / sizeof (regs[0])) + +static +CORE_ADDR get_pc (void) +{ + unsigned long pc; + + collect_register_by_name ("rip", &pc); + + dlog(1, "stop pc is %p\n", (void *) pc); + return pc; +} + +static +void set_pc (CORE_ADDR newpc) +{ + Bool mod; + supply_register_by_name ("rip", &newpc, &mod); + if (mod) + dlog(1, "set pc to %p\n", C2v (newpc)); + else + dlog(1, "set pc not changed %p\n", C2v (newpc)); +} + +/* store registers in the guest state (gdbserver_to_valgrind) + or fetch register from the guest state (valgrind_to_gdbserver). */ +static +void transfer_register (ThreadId tid, int abs_regno, void * buf, + transfer_direction dir, int size, Bool *mod) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int set = abs_regno / num_regs; + int regno = abs_regno % num_regs; + *mod = False; + + VexGuestAMD64State* amd64 = (VexGuestAMD64State*) get_arch (set, tst); + + switch (regno) { + // numbers here have to match the order of regs above. + // Attention: gdb order does not match valgrind order. + case 0: VG_(transfer) (&amd64->guest_RAX, buf, dir, size, mod); break; + case 1: VG_(transfer) (&amd64->guest_RBX, buf, dir, size, mod); break; + case 2: VG_(transfer) (&amd64->guest_RCX, buf, dir, size, mod); break; + case 3: VG_(transfer) (&amd64->guest_RDX, buf, dir, size, mod); break; + case 4: VG_(transfer) (&amd64->guest_RSI, buf, dir, size, mod); break; + case 5: VG_(transfer) (&amd64->guest_RDI, buf, dir, size, mod); break; + case 6: VG_(transfer) (&amd64->guest_RBP, buf, dir, size, mod); break; + case 7: VG_(transfer) (&amd64->guest_RSP, buf, dir, size, mod); break; + case 8: VG_(transfer) (&amd64->guest_R8, buf, dir, size, mod); break; + case 9: VG_(transfer) (&amd64->guest_R9, buf, dir, size, mod); break; + case 10: VG_(transfer) (&amd64->guest_R10, buf, dir, size, mod); break; + case 11: VG_(transfer) (&amd64->guest_R11, buf, dir, size, mod); break; + case 12: VG_(transfer) (&amd64->guest_R12, buf, dir, size, mod); break; + case 13: VG_(transfer) (&amd64->guest_R13, buf, dir, size, mod); break; + case 14: VG_(transfer) (&amd64->guest_R14, buf, dir, size, mod); break; + case 15: VG_(transfer) (&amd64->guest_R15, buf, dir, size, mod); break; + case 16: + VG_(transfer) (&amd64->guest_RIP, buf, dir, size, mod); + if (*mod && VG_(debugLog_getLevel)() > 2) { + char bufimage [2*sizeof(amd64->guest_IP_AT_SYSCALL) + 1]; + heximage (bufimage, + (char *) &amd64->guest_IP_AT_SYSCALL, + sizeof(amd64->guest_IP_AT_SYSCALL)); + dlog(3, "guest_IP_AT_SYSCALL %s\n", bufimage); + } + break; + case 17: + if (dir == valgrind_to_gdbserver) { + ULong rflags; + /* we can only retrieve the real flags (set 0) + retrieving shadow flags is not ok */ + if (set == 0) + rflags = LibVEX_GuestAMD64_get_rflags (amd64); + else + rflags = 0; + VG_(transfer) (&rflags, buf, dir, size, mod); + } else { + *mod = False; //GDBTD? how do we store rflags in libvex_guest_amd64.h ??? + } + break; + case 18: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_CS, buf, dir, size, mod); + case 19: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_SS, buf, dir, size, mod); + case 20: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_DS, buf, dir, size, mod); + case 21: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_ES, buf, dir, size, mod); + case 22: *mod = False; break; //GDBTD VG_(transfer) (&amd64->guest_FS, buf, dir, size, mod); + case 23: VG_(transfer) (&amd64->guest_GS_0x60, buf, dir, size, mod); break; + case 24: + case 25: + case 26: + case 27: /* register 24 to 31 are float registers 80 bits but 64 bits in valgrind */ + case 28: + case 29: + case 30: + case 31: + if (dir == valgrind_to_gdbserver) { + UChar fpreg80[10]; + convert_f64le_to_f80le ((UChar *)&amd64->guest_FPREG[regno-16], + fpreg80); + VG_(transfer) (&fpreg80, buf, dir, sizeof(fpreg80), mod); + } else { + ULong fpreg64; + convert_f80le_to_f64le (buf, (UChar *)&fpreg64); + VG_(transfer) (&amd64->guest_FPREG[regno-16], &fpreg64, + dir, sizeof(fpreg64), mod); + } + break; + case 32: + if (dir == valgrind_to_gdbserver) { + // vex only models the rounding bits (see libvex_guest_amd64.h) + UWord value = 0x037f; + value |= amd64->guest_FPROUND << 10; + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX equivalent fcrtl + } + break; + case 33: + if (dir == valgrind_to_gdbserver) { + UWord value = amd64->guest_FC3210; + value |= (amd64->guest_FTOP & 7) << 11; + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX equivalent fstat + } + break; + case 34: + if (dir == valgrind_to_gdbserver) { + // vex doesn't model these precisely + UWord value = + ((amd64->guest_FPTAG[0] ? 0 : 3) << 0) | + ((amd64->guest_FPTAG[1] ? 0 : 3) << 2) | + ((amd64->guest_FPTAG[2] ? 0 : 3) << 4) | + ((amd64->guest_FPTAG[3] ? 0 : 3) << 6) | + ((amd64->guest_FPTAG[4] ? 0 : 3) << 8) | + ((amd64->guest_FPTAG[5] ? 0 : 3) << 10) | + ((amd64->guest_FPTAG[6] ? 0 : 3) << 12) | + ((amd64->guest_FPTAG[7] ? 0 : 3) << 14); + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX equivalent ftag + } + break; + case 35: *mod = False; break; // GDBTD ??? equivalent of fiseg + case 36: *mod = False; break; // GDBTD ??? equivalent of fioff + case 37: *mod = False; break; // GDBTD ??? equivalent of foseg + case 38: *mod = False; break; // GDBTD ??? equivalent of fooff + case 39: *mod = False; break; // GDBTD ??? equivalent of fop + case 40: VG_(transfer) (&amd64->guest_XMM0, buf, dir, size, mod); break; + case 41: VG_(transfer) (&amd64->guest_XMM1, buf, dir, size, mod); break; + case 42: VG_(transfer) (&amd64->guest_XMM2, buf, dir, size, mod); break; + case 43: VG_(transfer) (&amd64->guest_XMM3, buf, dir, size, mod); break; + case 44: VG_(transfer) (&amd64->guest_XMM4, buf, dir, size, mod); break; + case 45: VG_(transfer) (&amd64->guest_XMM5, buf, dir, size, mod); break; + case 46: VG_(transfer) (&amd64->guest_XMM6, buf, dir, size, mod); break; + case 47: VG_(transfer) (&amd64->guest_XMM7, buf, dir, size, mod); break; + case 48: VG_(transfer) (&amd64->guest_XMM8, buf, dir, size, mod); break; + case 49: VG_(transfer) (&amd64->guest_XMM9, buf, dir, size, mod); break; + case 50: VG_(transfer) (&amd64->guest_XMM10, buf, dir, size, mod); break; + case 51: VG_(transfer) (&amd64->guest_XMM11, buf, dir, size, mod); break; + case 52: VG_(transfer) (&amd64->guest_XMM12, buf, dir, size, mod); break; + case 53: VG_(transfer) (&amd64->guest_XMM13, buf, dir, size, mod); break; + case 54: VG_(transfer) (&amd64->guest_XMM14, buf, dir, size, mod); break; + case 55: VG_(transfer) (&amd64->guest_XMM15, buf, dir, size, mod); break; + case 56: + if (dir == valgrind_to_gdbserver) { + // vex only models the rounding bits (see libvex_guest_x86.h) + UWord value = 0x1f80; + value |= amd64->guest_SSEROUND << 13; + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX equivalent mxcsr + } + break; + case 57: *mod = False; break; // GDBTD???? VEX equivalent { "orig_rax"}, + default: vg_assert(0); + } +} + +static struct valgrind_target_ops low_target = { + num_regs, + regs, + 7, //RSP + transfer_register, + get_pc, + set_pc, + "amd64", + NULL, // target_xml not needed. +#if defined(VGO_linux) + "amd64-linux-valgrind.xml" +#else + "amd64-coresse-valgrind.xml" +#endif +}; + +void amd64_init_architecture (struct valgrind_target_ops *target) +{ + *target = low_target; + set_register_cache (regs, num_regs); + gdbserver_expedite_regs = expedite_regs; +} diff --git a/coregrind/m_gdbserver/valgrind-low-arm.c b/coregrind/m_gdbserver/valgrind-low-arm.c new file mode 100644 index 00000000..d4cec3c5 --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low-arm.c @@ -0,0 +1,237 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" + +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" + +#include "valgrind_low.h" + +#include "libvex_guest_arm.h" + +struct reg regs[] = { + { "r0", 0, 32 }, + { "r1", 32, 32 }, + { "r2", 64, 32 }, + { "r3", 96, 32 }, + { "r4", 128, 32 }, + { "r5", 160, 32 }, + { "r6", 192, 32 }, + { "r7", 224, 32 }, + { "r8", 256, 32 }, + { "r9", 288, 32 }, + { "r10", 320, 32 }, + { "r11", 352, 32 }, + { "r12", 384, 32 }, + { "sp", 416, 32 }, + { "lr", 448, 32 }, + { "pc", 480, 32 }, + { "", 512, 0 }, // It seems these entries are needed + { "", 512, 0 }, // as previous versions of arm <-> gdb placed + { "", 512, 0 }, // some floating point registers here. So, cpsr + { "", 512, 0 }, // must be register 25. + { "", 512, 0 }, + { "", 512, 0 }, + { "", 512, 0 }, + { "", 512, 0 }, + { "", 512, 0 }, + { "cpsr", 512, 32 }, + { "d0", 544, 64 }, + { "d1", 608, 64 }, + { "d2", 672, 64 }, + { "d3", 736, 64 }, + { "d4", 800, 64 }, + { "d5", 864, 64 }, + { "d6", 928, 64 }, + { "d7", 992, 64 }, + { "d8", 1056, 64 }, + { "d9", 1120, 64 }, + { "d10", 1184, 64 }, + { "d11", 1248, 64 }, + { "d12", 1312, 64 }, + { "d13", 1376, 64 }, + { "d14", 1440, 64 }, + { "d15", 1504, 64 }, + { "d16", 1568, 64 }, + { "d17", 1632, 64 }, + { "d18", 1696, 64 }, + { "d19", 1760, 64 }, + { "d20", 1824, 64 }, + { "d21", 1888, 64 }, + { "d22", 1952, 64 }, + { "d23", 2016, 64 }, + { "d24", 2080, 64 }, + { "d25", 2144, 64 }, + { "d26", 2208, 64 }, + { "d27", 2272, 64 }, + { "d28", 2336, 64 }, + { "d29", 2400, 64 }, + { "d30", 2464, 64 }, + { "d31", 2528, 64 }, + { "fpscr", 2592, 32 } +}; +static const char *expedite_regs[] = { "r11", "sp", "pc", 0 }; +#define num_regs (sizeof (regs) / sizeof (regs[0])) + +static +CORE_ADDR get_pc (void) +{ + unsigned long pc; + + collect_register_by_name ("pc", &pc); + + dlog(1, "stop pc is %p\n", (void *) pc); + return pc; +} + +static +void set_pc (CORE_ADDR newpc) +{ + Bool mod; + supply_register_by_name ("pc", &newpc, &mod); + if (mod) + dlog(1, "set pc to %p\n", C2v (newpc)); + else + dlog(1, "set pc not changed %p\n", C2v (newpc)); +} + +/* store registers in the guest state (gdbserver_to_valgrind) + or fetch register from the guest state (valgrind_to_gdbserver). */ +static +void transfer_register (ThreadId tid, int abs_regno, void * buf, + transfer_direction dir, int size, Bool *mod) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int set = abs_regno / num_regs; + int regno = abs_regno % num_regs; + *mod = False; + + VexGuestARMState* arm = (VexGuestARMState*) get_arch (set, tst); + + switch (regno) { + // numbers here have to match the order of regs above + // Attention: gdb order does not match valgrind order. + case 0: VG_(transfer) (&arm->guest_R0, buf, dir, size, mod); break; + case 1: VG_(transfer) (&arm->guest_R1, buf, dir, size, mod); break; + case 2: VG_(transfer) (&arm->guest_R2, buf, dir, size, mod); break; + case 3: VG_(transfer) (&arm->guest_R3, buf, dir, size, mod); break; + case 4: VG_(transfer) (&arm->guest_R4, buf, dir, size, mod); break; + case 5: VG_(transfer) (&arm->guest_R5, buf, dir, size, mod); break; + case 6: VG_(transfer) (&arm->guest_R6, buf, dir, size, mod); break; + case 7: VG_(transfer) (&arm->guest_R7, buf, dir, size, mod); break; + case 8: VG_(transfer) (&arm->guest_R8, buf, dir, size, mod); break; + case 9: VG_(transfer) (&arm->guest_R9, buf, dir, size, mod); break; + case 10: VG_(transfer) (&arm->guest_R10, buf, dir, size, mod); break; + case 11: VG_(transfer) (&arm->guest_R11, buf, dir, size, mod); break; + case 12: VG_(transfer) (&arm->guest_R12, buf, dir, size, mod); break; + case 13: VG_(transfer) (&arm->guest_R13, buf, dir, size, mod); break; + case 14: VG_(transfer) (&arm->guest_R14, buf, dir, size, mod); break; + case 15: VG_(transfer) (&arm->guest_R15T, buf, dir, size, mod); break; + case 16: + case 17: + case 18: + case 19: + case 20: /* 9 "empty registers". See struct reg regs above. */ + case 21: + case 22: + case 23: + case 24: *mod = False; break; + case 25: { + UInt cpsr = LibVEX_GuestARM_get_cpsr (arm); + if (dir == valgrind_to_gdbserver) { + VG_(transfer) (&cpsr, buf, dir, size, mod); + } else { +# if 0 + UInt newcpsr; + VG_(transfer) (&newcpsr, buf, dir, size, mod); + *mod = newcpsr != cpsr; + // GDBTD ???? see FIXME in guest_arm_helpers.c + LibVEX_GuestARM_put_flags (newcpsr, arm); +# else + *mod = False; +# endif + } + break; + } + case 26: VG_(transfer) (&arm->guest_D0, buf, dir, size, mod); break; + case 27: VG_(transfer) (&arm->guest_D1, buf, dir, size, mod); break; + case 28: VG_(transfer) (&arm->guest_D2, buf, dir, size, mod); break; + case 29: VG_(transfer) (&arm->guest_D3, buf, dir, size, mod); break; + case 30: VG_(transfer) (&arm->guest_D4, buf, dir, size, mod); break; + case 31: VG_(transfer) (&arm->guest_D5, buf, dir, size, mod); break; + case 32: VG_(transfer) (&arm->guest_D6, buf, dir, size, mod); break; + case 33: VG_(transfer) (&arm->guest_D7, buf, dir, size, mod); break; + case 34: VG_(transfer) (&arm->guest_D8, buf, dir, size, mod); break; + case 35: VG_(transfer) (&arm->guest_D9, buf, dir, size, mod); break; + case 36: VG_(transfer) (&arm->guest_D10, buf, dir, size, mod); break; + case 37: VG_(transfer) (&arm->guest_D11, buf, dir, size, mod); break; + case 38: VG_(transfer) (&arm->guest_D12, buf, dir, size, mod); break; + case 39: VG_(transfer) (&arm->guest_D13, buf, dir, size, mod); break; + case 40: VG_(transfer) (&arm->guest_D14, buf, dir, size, mod); break; + case 41: VG_(transfer) (&arm->guest_D15, buf, dir, size, mod); break; + case 42: VG_(transfer) (&arm->guest_D16, buf, dir, size, mod); break; + case 43: VG_(transfer) (&arm->guest_D17, buf, dir, size, mod); break; + case 44: VG_(transfer) (&arm->guest_D18, buf, dir, size, mod); break; + case 45: VG_(transfer) (&arm->guest_D19, buf, dir, size, mod); break; + case 46: VG_(transfer) (&arm->guest_D20, buf, dir, size, mod); break; + case 47: VG_(transfer) (&arm->guest_D21, buf, dir, size, mod); break; + case 48: VG_(transfer) (&arm->guest_D22, buf, dir, size, mod); break; + case 49: VG_(transfer) (&arm->guest_D23, buf, dir, size, mod); break; + case 50: VG_(transfer) (&arm->guest_D24, buf, dir, size, mod); break; + case 51: VG_(transfer) (&arm->guest_D25, buf, dir, size, mod); break; + case 52: VG_(transfer) (&arm->guest_D26, buf, dir, size, mod); break; + case 53: VG_(transfer) (&arm->guest_D27, buf, dir, size, mod); break; + case 54: VG_(transfer) (&arm->guest_D28, buf, dir, size, mod); break; + case 55: VG_(transfer) (&arm->guest_D29, buf, dir, size, mod); break; + case 56: VG_(transfer) (&arm->guest_D30, buf, dir, size, mod); break; + case 57: VG_(transfer) (&arm->guest_D31, buf, dir, size, mod); break; + case 58: VG_(transfer) (&arm->guest_FPSCR, buf, dir, size, mod); break; + default: vg_assert(0); + } +} + +static struct valgrind_target_ops low_target = { + num_regs, + regs, + 13, //SP + transfer_register, + get_pc, + set_pc, + "arm", + "arm-with-vfpv3.xml", + "arm-with-vfpv3-valgrind.xml" +}; + +void arm_init_architecture (struct valgrind_target_ops *target) +{ + *target = low_target; + set_register_cache (regs, num_regs); + gdbserver_expedite_regs = expedite_regs; +} diff --git a/coregrind/m_gdbserver/valgrind-low-ppc32.c b/coregrind/m_gdbserver/valgrind-low-ppc32.c new file mode 100644 index 00000000..a7e282ed --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low-ppc32.c @@ -0,0 +1,343 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" + +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" + +#include "valgrind_low.h" + +#include "libvex_guest_ppc32.h" + +/* this is only the basic set of registers. + Need to look at what is the exact ppc32 model to support. +*/ +struct reg regs[] = { + { "r0", 0, 32 }, + { "r1", 32, 32 }, + { "r2", 64, 32 }, + { "r3", 96, 32 }, + { "r4", 128, 32 }, + { "r5", 160, 32 }, + { "r6", 192, 32 }, + { "r7", 224, 32 }, + { "r8", 256, 32 }, + { "r9", 288, 32 }, + { "r10", 320, 32 }, + { "r11", 352, 32 }, + { "r12", 384, 32 }, + { "r13", 416, 32 }, + { "r14", 448, 32 }, + { "r15", 480, 32 }, + { "r16", 512, 32 }, + { "r17", 544, 32 }, + { "r18", 576, 32 }, + { "r19", 608, 32 }, + { "r20", 640, 32 }, + { "r21", 672, 32 }, + { "r22", 704, 32 }, + { "r23", 736, 32 }, + { "r24", 768, 32 }, + { "r25", 800, 32 }, + { "r26", 832, 32 }, + { "r27", 864, 32 }, + { "r28", 896, 32 }, + { "r29", 928, 32 }, + { "r30", 960, 32 }, + { "r31", 992, 32 }, + { "f0", 1024, 64 }, + { "f1", 1088, 64 }, + { "f2", 1152, 64 }, + { "f3", 1216, 64 }, + { "f4", 1280, 64 }, + { "f5", 1344, 64 }, + { "f6", 1408, 64 }, + { "f7", 1472, 64 }, + { "f8", 1536, 64 }, + { "f9", 1600, 64 }, + { "f10", 1664, 64 }, + { "f11", 1728, 64 }, + { "f12", 1792, 64 }, + { "f13", 1856, 64 }, + { "f14", 1920, 64 }, + { "f15", 1984, 64 }, + { "f16", 2048, 64 }, + { "f17", 2112, 64 }, + { "f18", 2176, 64 }, + { "f19", 2240, 64 }, + { "f20", 2304, 64 }, + { "f21", 2368, 64 }, + { "f22", 2432, 64 }, + { "f23", 2496, 64 }, + { "f24", 2560, 64 }, + { "f25", 2624, 64 }, + { "f26", 2688, 64 }, + { "f27", 2752, 64 }, + { "f28", 2816, 64 }, + { "f29", 2880, 64 }, + { "f30", 2944, 64 }, + { "f31", 3008, 64 }, + { "pc", 3072, 32 }, + { "msr", 3104, 32 }, + { "cr", 3136, 32 }, + { "lr", 3168, 32 }, + { "ctr", 3200, 32 }, + { "xer", 3232, 32 }, + { "fpscr", 3264, 32 }, + { "orig_r3", 3296, 32 }, + { "trap", 3328, 32 }, + { "vr0", 3360, 128 }, + { "vr1", 3488, 128 }, + { "vr2", 3616, 128 }, + { "vr3", 3744, 128 }, + { "vr4", 3872, 128 }, + { "vr5", 4000, 128 }, + { "vr6", 4128, 128 }, + { "vr7", 4256, 128 }, + { "vr8", 4384, 128 }, + { "vr9", 4512, 128 }, + { "vr10", 4640, 128 }, + { "vr11", 4768, 128 }, + { "vr12", 4896, 128 }, + { "vr13", 5024, 128 }, + { "vr14", 5152, 128 }, + { "vr15", 5280, 128 }, + { "vr16", 5408, 128 }, + { "vr17", 5536, 128 }, + { "vr18", 5664, 128 }, + { "vr19", 5792, 128 }, + { "vr20", 5920, 128 }, + { "vr21", 6048, 128 }, + { "vr22", 6176, 128 }, + { "vr23", 6304, 128 }, + { "vr24", 6432, 128 }, + { "vr25", 6560, 128 }, + { "vr26", 6688, 128 }, + { "vr27", 6816, 128 }, + { "vr28", 6944, 128 }, + { "vr29", 7072, 128 }, + { "vr30", 7200, 128 }, + { "vr31", 7328, 128 }, + { "vscr", 7456, 32 }, + { "vrsave", 7488, 32 } +}; +static const char *expedite_regs[] = { "r1", "pc", 0 }; +#define num_regs (sizeof (regs) / sizeof (regs[0])) + +static +CORE_ADDR get_pc (void) +{ + unsigned long pc; + + collect_register_by_name ("pc", &pc); + + dlog(1, "stop pc is %p\n", (void *) pc); + return pc; +} + +static +void set_pc (CORE_ADDR newpc) +{ + Bool mod; + supply_register_by_name ("pc", &newpc, &mod); + if (mod) + dlog(1, "set pc to %p\n", C2v (newpc)); + else + dlog(1, "set pc not changed %p\n", C2v (newpc)); +} + +/* store registers in the guest state (gdbserver_to_valgrind) + or fetch register from the guest state (valgrind_to_gdbserver). */ +static +void transfer_register (ThreadId tid, int abs_regno, void * buf, + transfer_direction dir, int size, Bool *mod) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int set = abs_regno / num_regs; + int regno = abs_regno % num_regs; + *mod = False; + + VexGuestPPC32State* ppc32 = (VexGuestPPC32State*) get_arch (set, tst); + + switch (regno) { + // numbers here have to match the order of regs above + // Attention: gdb order does not match valgrind order. + case 0: VG_(transfer) (&ppc32->guest_GPR0, buf, dir, size, mod); break; + case 1: VG_(transfer) (&ppc32->guest_GPR1, buf, dir, size, mod); break; + case 2: VG_(transfer) (&ppc32->guest_GPR2, buf, dir, size, mod); break; + case 3: VG_(transfer) (&ppc32->guest_GPR3, buf, dir, size, mod); break; + case 4: VG_(transfer) (&ppc32->guest_GPR4, buf, dir, size, mod); break; + case 5: VG_(transfer) (&ppc32->guest_GPR5, buf, dir, size, mod); break; + case 6: VG_(transfer) (&ppc32->guest_GPR6, buf, dir, size, mod); break; + case 7: VG_(transfer) (&ppc32->guest_GPR7, buf, dir, size, mod); break; + case 8: VG_(transfer) (&ppc32->guest_GPR8, buf, dir, size, mod); break; + case 9: VG_(transfer) (&ppc32->guest_GPR9, buf, dir, size, mod); break; + case 10: VG_(transfer) (&ppc32->guest_GPR10, buf, dir, size, mod); break; + case 11: VG_(transfer) (&ppc32->guest_GPR11, buf, dir, size, mod); break; + case 12: VG_(transfer) (&ppc32->guest_GPR12, buf, dir, size, mod); break; + case 13: VG_(transfer) (&ppc32->guest_GPR13, buf, dir, size, mod); break; + case 14: VG_(transfer) (&ppc32->guest_GPR14, buf, dir, size, mod); break; + case 15: VG_(transfer) (&ppc32->guest_GPR15, buf, dir, size, mod); break; + case 16: VG_(transfer) (&ppc32->guest_GPR16, buf, dir, size, mod); break; + case 17: VG_(transfer) (&ppc32->guest_GPR17, buf, dir, size, mod); break; + case 18: VG_(transfer) (&ppc32->guest_GPR18, buf, dir, size, mod); break; + case 19: VG_(transfer) (&ppc32->guest_GPR19, buf, dir, size, mod); break; + case 20: VG_(transfer) (&ppc32->guest_GPR20, buf, dir, size, mod); break; + case 21: VG_(transfer) (&ppc32->guest_GPR21, buf, dir, size, mod); break; + case 22: VG_(transfer) (&ppc32->guest_GPR22, buf, dir, size, mod); break; + case 23: VG_(transfer) (&ppc32->guest_GPR23, buf, dir, size, mod); break; + case 24: VG_(transfer) (&ppc32->guest_GPR24, buf, dir, size, mod); break; + case 25: VG_(transfer) (&ppc32->guest_GPR25, buf, dir, size, mod); break; + case 26: VG_(transfer) (&ppc32->guest_GPR26, buf, dir, size, mod); break; + case 27: VG_(transfer) (&ppc32->guest_GPR27, buf, dir, size, mod); break; + case 28: VG_(transfer) (&ppc32->guest_GPR28, buf, dir, size, mod); break; + case 29: VG_(transfer) (&ppc32->guest_GPR29, buf, dir, size, mod); break; + case 30: VG_(transfer) (&ppc32->guest_GPR30, buf, dir, size, mod); break; + case 31: VG_(transfer) (&ppc32->guest_GPR31, buf, dir, size, mod); break; + case 32: VG_(transfer) (&ppc32->guest_VSR0, buf, dir, size, mod); break; + case 33: VG_(transfer) (&ppc32->guest_VSR1, buf, dir, size, mod); break; + case 34: VG_(transfer) (&ppc32->guest_VSR2, buf, dir, size, mod); break; + case 35: VG_(transfer) (&ppc32->guest_VSR3, buf, dir, size, mod); break; + case 36: VG_(transfer) (&ppc32->guest_VSR4, buf, dir, size, mod); break; + case 37: VG_(transfer) (&ppc32->guest_VSR5, buf, dir, size, mod); break; + case 38: VG_(transfer) (&ppc32->guest_VSR6, buf, dir, size, mod); break; + case 39: VG_(transfer) (&ppc32->guest_VSR7, buf, dir, size, mod); break; + case 40: VG_(transfer) (&ppc32->guest_VSR8, buf, dir, size, mod); break; + case 41: VG_(transfer) (&ppc32->guest_VSR9, buf, dir, size, mod); break; + case 42: VG_(transfer) (&ppc32->guest_VSR10, buf, dir, size, mod); break; + case 43: VG_(transfer) (&ppc32->guest_VSR11, buf, dir, size, mod); break; + case 44: VG_(transfer) (&ppc32->guest_VSR12, buf, dir, size, mod); break; + case 45: VG_(transfer) (&ppc32->guest_VSR13, buf, dir, size, mod); break; + case 46: VG_(transfer) (&ppc32->guest_VSR14, buf, dir, size, mod); break; + case 47: VG_(transfer) (&ppc32->guest_VSR15, buf, dir, size, mod); break; + case 48: VG_(transfer) (&ppc32->guest_VSR16, buf, dir, size, mod); break; + case 49: VG_(transfer) (&ppc32->guest_VSR17, buf, dir, size, mod); break; + case 50: VG_(transfer) (&ppc32->guest_VSR18, buf, dir, size, mod); break; + case 51: VG_(transfer) (&ppc32->guest_VSR19, buf, dir, size, mod); break; + case 52: VG_(transfer) (&ppc32->guest_VSR20, buf, dir, size, mod); break; + case 53: VG_(transfer) (&ppc32->guest_VSR21, buf, dir, size, mod); break; + case 54: VG_(transfer) (&ppc32->guest_VSR22, buf, dir, size, mod); break; + case 55: VG_(transfer) (&ppc32->guest_VSR23, buf, dir, size, mod); break; + case 56: VG_(transfer) (&ppc32->guest_VSR24, buf, dir, size, mod); break; + case 57: VG_(transfer) (&ppc32->guest_VSR25, buf, dir, size, mod); break; + case 58: VG_(transfer) (&ppc32->guest_VSR26, buf, dir, size, mod); break; + case 59: VG_(transfer) (&ppc32->guest_VSR27, buf, dir, size, mod); break; + case 60: VG_(transfer) (&ppc32->guest_VSR28, buf, dir, size, mod); break; + case 61: VG_(transfer) (&ppc32->guest_VSR29, buf, dir, size, mod); break; + case 62: VG_(transfer) (&ppc32->guest_VSR30, buf, dir, size, mod); break; + case 63: VG_(transfer) (&ppc32->guest_VSR31, buf, dir, size, mod); break; + case 64: VG_(transfer) (&ppc32->guest_CIA, buf, dir, size, mod); break; + case 65: *mod = False; break; // VEX does not model Machine State Register + case 66: { + UInt cr = LibVEX_GuestPPC32_get_CR (ppc32); + if (dir == valgrind_to_gdbserver) { + VG_(transfer) (&cr, buf, dir, size, mod); + } else { + UInt newcr; + VG_(transfer) (&newcr, buf, dir, size, mod); + *mod = newcr != cr; + LibVEX_GuestPPC32_put_CR (newcr, ppc32); + } + break; + } + case 67: VG_(transfer) (&ppc32->guest_LR, buf, dir, size, mod); break; + case 68: VG_(transfer) (&ppc32->guest_CTR, buf, dir, size, mod); break; + case 69: { + UInt xer = LibVEX_GuestPPC32_get_XER (ppc32); + if (dir == valgrind_to_gdbserver) { + VG_(transfer) (&xer, buf, dir, size, mod); + } else { + UInt newxer; + VG_(transfer) (&newxer, buf, dir, size, mod); + *mod = newxer != xer; + LibVEX_GuestPPC32_put_XER (newxer, ppc32); + } + break; + } + case 70: VG_(transfer) (&ppc32->guest_FPROUND, buf, dir, size, mod); break; + case 71: *mod = False; break; // GDBTD???? VEX { "orig_r3", 3296, 32 }, + case 72: *mod = False; break; // GDBTD???? VEX { "trap", 3328, 32 }, + case 73: VG_(transfer) (&ppc32->guest_VSR32, buf, dir, size, mod); break; + case 74: VG_(transfer) (&ppc32->guest_VSR33, buf, dir, size, mod); break; + case 75: VG_(transfer) (&ppc32->guest_VSR34, buf, dir, size, mod); break; + case 76: VG_(transfer) (&ppc32->guest_VSR35, buf, dir, size, mod); break; + case 77: VG_(transfer) (&ppc32->guest_VSR36, buf, dir, size, mod); break; + case 78: VG_(transfer) (&ppc32->guest_VSR37, buf, dir, size, mod); break; + case 79: VG_(transfer) (&ppc32->guest_VSR38, buf, dir, size, mod); break; + case 80: VG_(transfer) (&ppc32->guest_VSR39, buf, dir, size, mod); break; + case 81: VG_(transfer) (&ppc32->guest_VSR40, buf, dir, size, mod); break; + case 82: VG_(transfer) (&ppc32->guest_VSR41, buf, dir, size, mod); break; + case 83: VG_(transfer) (&ppc32->guest_VSR42, buf, dir, size, mod); break; + case 84: VG_(transfer) (&ppc32->guest_VSR43, buf, dir, size, mod); break; + case 85: VG_(transfer) (&ppc32->guest_VSR44, buf, dir, size, mod); break; + case 86: VG_(transfer) (&ppc32->guest_VSR45, buf, dir, size, mod); break; + case 87: VG_(transfer) (&ppc32->guest_VSR46, buf, dir, size, mod); break; + case 88: VG_(transfer) (&ppc32->guest_VSR47, buf, dir, size, mod); break; + case 89: VG_(transfer) (&ppc32->guest_VSR48, buf, dir, size, mod); break; + case 90: VG_(transfer) (&ppc32->guest_VSR49, buf, dir, size, mod); break; + case 91: VG_(transfer) (&ppc32->guest_VSR50, buf, dir, size, mod); break; + case 92: VG_(transfer) (&ppc32->guest_VSR51, buf, dir, size, mod); break; + case 93: VG_(transfer) (&ppc32->guest_VSR52, buf, dir, size, mod); break; + case 94: VG_(transfer) (&ppc32->guest_VSR53, buf, dir, size, mod); break; + case 95: VG_(transfer) (&ppc32->guest_VSR54, buf, dir, size, mod); break; + case 96: VG_(transfer) (&ppc32->guest_VSR55, buf, dir, size, mod); break; + case 97: VG_(transfer) (&ppc32->guest_VSR56, buf, dir, size, mod); break; + case 98: VG_(transfer) (&ppc32->guest_VSR57, buf, dir, size, mod); break; + case 99: VG_(transfer) (&ppc32->guest_VSR58, buf, dir, size, mod); break; + case 100: VG_(transfer) (&ppc32->guest_VSR59, buf, dir, size, mod); break; + case 101: VG_(transfer) (&ppc32->guest_VSR60, buf, dir, size, mod); break; + case 102: VG_(transfer) (&ppc32->guest_VSR61, buf, dir, size, mod); break; + case 103: VG_(transfer) (&ppc32->guest_VSR62, buf, dir, size, mod); break; + case 104: VG_(transfer) (&ppc32->guest_VSR63, buf, dir, size, mod); break; + case 105: VG_(transfer) (&ppc32->guest_VSCR, buf, dir, size, mod); break; + case 106: VG_(transfer) (&ppc32->guest_VRSAVE, buf, dir, size, mod); break; + default: vg_assert(0); + } +} + +static struct valgrind_target_ops low_target = { + num_regs, + regs, + 1, //r1 + transfer_register, + get_pc, + set_pc, + "ppc32", + "powerpc-altivec32l.xml", + "powerpc-altivec32l-valgrind.xml" +}; + +void ppc32_init_architecture (struct valgrind_target_ops *target) +{ + *target = low_target; + set_register_cache (regs, num_regs); + gdbserver_expedite_regs = expedite_regs; +} + diff --git a/coregrind/m_gdbserver/valgrind-low-ppc64.c b/coregrind/m_gdbserver/valgrind-low-ppc64.c new file mode 100644 index 00000000..f2fdbecc --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low-ppc64.c @@ -0,0 +1,339 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" + +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" + +#include "valgrind_low.h" + +#include "libvex_guest_ppc64.h" + +struct reg regs[] = { + { "r0", 0, 64 }, + { "r1", 64, 64 }, + { "r2", 128, 64 }, + { "r3", 192, 64 }, + { "r4", 256, 64 }, + { "r5", 320, 64 }, + { "r6", 384, 64 }, + { "r7", 448, 64 }, + { "r8", 512, 64 }, + { "r9", 576, 64 }, + { "r10", 640, 64 }, + { "r11", 704, 64 }, + { "r12", 768, 64 }, + { "r13", 832, 64 }, + { "r14", 896, 64 }, + { "r15", 960, 64 }, + { "r16", 1024, 64 }, + { "r17", 1088, 64 }, + { "r18", 1152, 64 }, + { "r19", 1216, 64 }, + { "r20", 1280, 64 }, + { "r21", 1344, 64 }, + { "r22", 1408, 64 }, + { "r23", 1472, 64 }, + { "r24", 1536, 64 }, + { "r25", 1600, 64 }, + { "r26", 1664, 64 }, + { "r27", 1728, 64 }, + { "r28", 1792, 64 }, + { "r29", 1856, 64 }, + { "r30", 1920, 64 }, + { "r31", 1984, 64 }, + { "f0", 2048, 64 }, + { "f1", 2112, 64 }, + { "f2", 2176, 64 }, + { "f3", 2240, 64 }, + { "f4", 2304, 64 }, + { "f5", 2368, 64 }, + { "f6", 2432, 64 }, + { "f7", 2496, 64 }, + { "f8", 2560, 64 }, + { "f9", 2624, 64 }, + { "f10", 2688, 64 }, + { "f11", 2752, 64 }, + { "f12", 2816, 64 }, + { "f13", 2880, 64 }, + { "f14", 2944, 64 }, + { "f15", 3008, 64 }, + { "f16", 3072, 64 }, + { "f17", 3136, 64 }, + { "f18", 3200, 64 }, + { "f19", 3264, 64 }, + { "f20", 3328, 64 }, + { "f21", 3392, 64 }, + { "f22", 3456, 64 }, + { "f23", 3520, 64 }, + { "f24", 3584, 64 }, + { "f25", 3648, 64 }, + { "f26", 3712, 64 }, + { "f27", 3776, 64 }, + { "f28", 3840, 64 }, + { "f29", 3904, 64 }, + { "f30", 3968, 64 }, + { "f31", 4032, 64 }, + { "pc", 4096, 64 }, + { "msr", 4160, 64 }, + { "cr", 4224, 32 }, + { "lr", 4256, 64 }, + { "ctr", 4320, 64 }, + { "xer", 4384, 32 }, + { "fpscr", 4416, 32 }, + { "orig_r3", 4448, 64 }, + { "trap", 4512, 64 }, + { "vr0", 4576, 128 }, + { "vr1", 4704, 128 }, + { "vr2", 4832, 128 }, + { "vr3", 4960, 128 }, + { "vr4", 5088, 128 }, + { "vr5", 5216, 128 }, + { "vr6", 5344, 128 }, + { "vr7", 5472, 128 }, + { "vr8", 5600, 128 }, + { "vr9", 5728, 128 }, + { "vr10", 5856, 128 }, + { "vr11", 5984, 128 }, + { "vr12", 6112, 128 }, + { "vr13", 6240, 128 }, + { "vr14", 6368, 128 }, + { "vr15", 6496, 128 }, + { "vr16", 6624, 128 }, + { "vr17", 6752, 128 }, + { "vr18", 6880, 128 }, + { "vr19", 7008, 128 }, + { "vr20", 7136, 128 }, + { "vr21", 7264, 128 }, + { "vr22", 7392, 128 }, + { "vr23", 7520, 128 }, + { "vr24", 7648, 128 }, + { "vr25", 7776, 128 }, + { "vr26", 7904, 128 }, + { "vr27", 8032, 128 }, + { "vr28", 8160, 128 }, + { "vr29", 8288, 128 }, + { "vr30", 8416, 128 }, + { "vr31", 8544, 128 }, + { "vscr", 8672, 32 }, + { "vrsave", 8704, 32 }, +}; +static const char *expedite_regs[] = { "r1", "pc", 0 }; +#define num_regs (sizeof (regs) / sizeof (regs[0])) + +static +CORE_ADDR get_pc (void) +{ + unsigned long pc; + + collect_register_by_name ("pc", &pc); + + dlog(1, "stop pc is %p\n", (void *) pc); + return pc; +} + +static +void set_pc (CORE_ADDR newpc) +{ + Bool mod; + supply_register_by_name ("pc", &newpc, &mod); + if (mod) + dlog(1, "set pc to %p\n", C2v (newpc)); + else + dlog(1, "set pc not changed %p\n", C2v (newpc)); +} + +/* store registers in the guest state (gdbserver_to_valgrind) + or fetch register from the guest state (valgrind_to_gdbserver). */ +static +void transfer_register (ThreadId tid, int abs_regno, void * buf, + transfer_direction dir, int size, Bool *mod) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int set = abs_regno / num_regs; + int regno = abs_regno % num_regs; + *mod = False; + + VexGuestPPC64State* ppc64 = (VexGuestPPC64State*) get_arch (set, tst); + + switch (regno) { + // numbers here have to match the order of regs above + // Attention: gdb order does not match valgrind order. + case 0: VG_(transfer) (&ppc64->guest_GPR0, buf, dir, size, mod); break; + case 1: VG_(transfer) (&ppc64->guest_GPR1, buf, dir, size, mod); break; + case 2: VG_(transfer) (&ppc64->guest_GPR2, buf, dir, size, mod); break; + case 3: VG_(transfer) (&ppc64->guest_GPR3, buf, dir, size, mod); break; + case 4: VG_(transfer) (&ppc64->guest_GPR4, buf, dir, size, mod); break; + case 5: VG_(transfer) (&ppc64->guest_GPR5, buf, dir, size, mod); break; + case 6: VG_(transfer) (&ppc64->guest_GPR6, buf, dir, size, mod); break; + case 7: VG_(transfer) (&ppc64->guest_GPR7, buf, dir, size, mod); break; + case 8: VG_(transfer) (&ppc64->guest_GPR8, buf, dir, size, mod); break; + case 9: VG_(transfer) (&ppc64->guest_GPR9, buf, dir, size, mod); break; + case 10: VG_(transfer) (&ppc64->guest_GPR10, buf, dir, size, mod); break; + case 11: VG_(transfer) (&ppc64->guest_GPR11, buf, dir, size, mod); break; + case 12: VG_(transfer) (&ppc64->guest_GPR12, buf, dir, size, mod); break; + case 13: VG_(transfer) (&ppc64->guest_GPR13, buf, dir, size, mod); break; + case 14: VG_(transfer) (&ppc64->guest_GPR14, buf, dir, size, mod); break; + case 15: VG_(transfer) (&ppc64->guest_GPR15, buf, dir, size, mod); break; + case 16: VG_(transfer) (&ppc64->guest_GPR16, buf, dir, size, mod); break; + case 17: VG_(transfer) (&ppc64->guest_GPR17, buf, dir, size, mod); break; + case 18: VG_(transfer) (&ppc64->guest_GPR18, buf, dir, size, mod); break; + case 19: VG_(transfer) (&ppc64->guest_GPR19, buf, dir, size, mod); break; + case 20: VG_(transfer) (&ppc64->guest_GPR20, buf, dir, size, mod); break; + case 21: VG_(transfer) (&ppc64->guest_GPR21, buf, dir, size, mod); break; + case 22: VG_(transfer) (&ppc64->guest_GPR22, buf, dir, size, mod); break; + case 23: VG_(transfer) (&ppc64->guest_GPR23, buf, dir, size, mod); break; + case 24: VG_(transfer) (&ppc64->guest_GPR24, buf, dir, size, mod); break; + case 25: VG_(transfer) (&ppc64->guest_GPR25, buf, dir, size, mod); break; + case 26: VG_(transfer) (&ppc64->guest_GPR26, buf, dir, size, mod); break; + case 27: VG_(transfer) (&ppc64->guest_GPR27, buf, dir, size, mod); break; + case 28: VG_(transfer) (&ppc64->guest_GPR28, buf, dir, size, mod); break; + case 29: VG_(transfer) (&ppc64->guest_GPR29, buf, dir, size, mod); break; + case 30: VG_(transfer) (&ppc64->guest_GPR30, buf, dir, size, mod); break; + case 31: VG_(transfer) (&ppc64->guest_GPR31, buf, dir, size, mod); break; + case 32: VG_(transfer) (&ppc64->guest_VSR0, buf, dir, size, mod); break; + case 33: VG_(transfer) (&ppc64->guest_VSR1, buf, dir, size, mod); break; + case 34: VG_(transfer) (&ppc64->guest_VSR2, buf, dir, size, mod); break; + case 35: VG_(transfer) (&ppc64->guest_VSR3, buf, dir, size, mod); break; + case 36: VG_(transfer) (&ppc64->guest_VSR4, buf, dir, size, mod); break; + case 37: VG_(transfer) (&ppc64->guest_VSR5, buf, dir, size, mod); break; + case 38: VG_(transfer) (&ppc64->guest_VSR6, buf, dir, size, mod); break; + case 39: VG_(transfer) (&ppc64->guest_VSR7, buf, dir, size, mod); break; + case 40: VG_(transfer) (&ppc64->guest_VSR8, buf, dir, size, mod); break; + case 41: VG_(transfer) (&ppc64->guest_VSR9, buf, dir, size, mod); break; + case 42: VG_(transfer) (&ppc64->guest_VSR10, buf, dir, size, mod); break; + case 43: VG_(transfer) (&ppc64->guest_VSR11, buf, dir, size, mod); break; + case 44: VG_(transfer) (&ppc64->guest_VSR12, buf, dir, size, mod); break; + case 45: VG_(transfer) (&ppc64->guest_VSR13, buf, dir, size, mod); break; + case 46: VG_(transfer) (&ppc64->guest_VSR14, buf, dir, size, mod); break; + case 47: VG_(transfer) (&ppc64->guest_VSR15, buf, dir, size, mod); break; + case 48: VG_(transfer) (&ppc64->guest_VSR16, buf, dir, size, mod); break; + case 49: VG_(transfer) (&ppc64->guest_VSR17, buf, dir, size, mod); break; + case 50: VG_(transfer) (&ppc64->guest_VSR18, buf, dir, size, mod); break; + case 51: VG_(transfer) (&ppc64->guest_VSR19, buf, dir, size, mod); break; + case 52: VG_(transfer) (&ppc64->guest_VSR20, buf, dir, size, mod); break; + case 53: VG_(transfer) (&ppc64->guest_VSR21, buf, dir, size, mod); break; + case 54: VG_(transfer) (&ppc64->guest_VSR22, buf, dir, size, mod); break; + case 55: VG_(transfer) (&ppc64->guest_VSR23, buf, dir, size, mod); break; + case 56: VG_(transfer) (&ppc64->guest_VSR24, buf, dir, size, mod); break; + case 57: VG_(transfer) (&ppc64->guest_VSR25, buf, dir, size, mod); break; + case 58: VG_(transfer) (&ppc64->guest_VSR26, buf, dir, size, mod); break; + case 59: VG_(transfer) (&ppc64->guest_VSR27, buf, dir, size, mod); break; + case 60: VG_(transfer) (&ppc64->guest_VSR28, buf, dir, size, mod); break; + case 61: VG_(transfer) (&ppc64->guest_VSR29, buf, dir, size, mod); break; + case 62: VG_(transfer) (&ppc64->guest_VSR30, buf, dir, size, mod); break; + case 63: VG_(transfer) (&ppc64->guest_VSR31, buf, dir, size, mod); break; + case 64: VG_(transfer) (&ppc64->guest_CIA, buf, dir, size, mod); break; + case 65: *mod = False; break; // VEX does not model Machine State Register + case 66: { + UInt cr = LibVEX_GuestPPC64_get_CR (ppc64); + if (dir == valgrind_to_gdbserver) { + VG_(transfer) (&cr, buf, dir, size, mod); + } else { + UInt newcr; + VG_(transfer) (&newcr, buf, dir, size, mod); + *mod = newcr != cr; + LibVEX_GuestPPC64_put_CR (newcr, ppc64); + } + break; + } + case 67: VG_(transfer) (&ppc64->guest_LR, buf, dir, size, mod); break; + case 68: VG_(transfer) (&ppc64->guest_CTR, buf, dir, size, mod); break; + case 69: { + UInt xer = LibVEX_GuestPPC64_get_XER (ppc64); + if (dir == valgrind_to_gdbserver) { + VG_(transfer) (&xer, buf, dir, size, mod); + } else { + UInt newxer; + VG_(transfer) (&newxer, buf, dir, size, mod); + *mod = newxer != xer; + LibVEX_GuestPPC64_put_XER (newxer, ppc64); + } + break; + } + case 70: VG_(transfer) (&ppc64->guest_FPROUND, buf, dir, size, mod); break; + case 71: *mod = False; break; // GDBTD???? VEX { "orig_r3", 4448, 64 }, + case 72: *mod = False; break; // GDBTD???? VEX { "trap", 4512, 64 }, + case 73: VG_(transfer) (&ppc64->guest_VSR32, buf, dir, size, mod); break; + case 74: VG_(transfer) (&ppc64->guest_VSR33, buf, dir, size, mod); break; + case 75: VG_(transfer) (&ppc64->guest_VSR34, buf, dir, size, mod); break; + case 76: VG_(transfer) (&ppc64->guest_VSR35, buf, dir, size, mod); break; + case 77: VG_(transfer) (&ppc64->guest_VSR36, buf, dir, size, mod); break; + case 78: VG_(transfer) (&ppc64->guest_VSR37, buf, dir, size, mod); break; + case 79: VG_(transfer) (&ppc64->guest_VSR38, buf, dir, size, mod); break; + case 80: VG_(transfer) (&ppc64->guest_VSR39, buf, dir, size, mod); break; + case 81: VG_(transfer) (&ppc64->guest_VSR40, buf, dir, size, mod); break; + case 82: VG_(transfer) (&ppc64->guest_VSR41, buf, dir, size, mod); break; + case 83: VG_(transfer) (&ppc64->guest_VSR42, buf, dir, size, mod); break; + case 84: VG_(transfer) (&ppc64->guest_VSR43, buf, dir, size, mod); break; + case 85: VG_(transfer) (&ppc64->guest_VSR44, buf, dir, size, mod); break; + case 86: VG_(transfer) (&ppc64->guest_VSR45, buf, dir, size, mod); break; + case 87: VG_(transfer) (&ppc64->guest_VSR46, buf, dir, size, mod); break; + case 88: VG_(transfer) (&ppc64->guest_VSR47, buf, dir, size, mod); break; + case 89: VG_(transfer) (&ppc64->guest_VSR48, buf, dir, size, mod); break; + case 90: VG_(transfer) (&ppc64->guest_VSR49, buf, dir, size, mod); break; + case 91: VG_(transfer) (&ppc64->guest_VSR50, buf, dir, size, mod); break; + case 92: VG_(transfer) (&ppc64->guest_VSR51, buf, dir, size, mod); break; + case 93: VG_(transfer) (&ppc64->guest_VSR52, buf, dir, size, mod); break; + case 94: VG_(transfer) (&ppc64->guest_VSR53, buf, dir, size, mod); break; + case 95: VG_(transfer) (&ppc64->guest_VSR54, buf, dir, size, mod); break; + case 96: VG_(transfer) (&ppc64->guest_VSR55, buf, dir, size, mod); break; + case 97: VG_(transfer) (&ppc64->guest_VSR56, buf, dir, size, mod); break; + case 98: VG_(transfer) (&ppc64->guest_VSR57, buf, dir, size, mod); break; + case 99: VG_(transfer) (&ppc64->guest_VSR58, buf, dir, size, mod); break; + case 100: VG_(transfer) (&ppc64->guest_VSR59, buf, dir, size, mod); break; + case 101: VG_(transfer) (&ppc64->guest_VSR60, buf, dir, size, mod); break; + case 102: VG_(transfer) (&ppc64->guest_VSR61, buf, dir, size, mod); break; + case 103: VG_(transfer) (&ppc64->guest_VSR62, buf, dir, size, mod); break; + case 104: VG_(transfer) (&ppc64->guest_VSR63, buf, dir, size, mod); break; + case 105: VG_(transfer) (&ppc64->guest_VSCR, buf, dir, size, mod); break; + case 106: VG_(transfer) (&ppc64->guest_VRSAVE, buf, dir, size, mod); break; + default: vg_assert(0); + } +} + +static struct valgrind_target_ops low_target = { + num_regs, + regs, + 1, //r1 + transfer_register, + get_pc, + set_pc, + "ppc64", + "powerpc-altivec64l.xml", + "powerpc-altivec64l-valgrind.xml" +}; + +void ppc64_init_architecture (struct valgrind_target_ops *target) +{ + *target = low_target; + set_register_cache (regs, num_regs); + gdbserver_expedite_regs = expedite_regs; +} diff --git a/coregrind/m_gdbserver/valgrind-low-s390x.c b/coregrind/m_gdbserver/valgrind-low-s390x.c new file mode 100644 index 00000000..3e6b53c1 --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low-s390x.c @@ -0,0 +1,205 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" + +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" + +#include "valgrind_low.h" + +#include "libvex_guest_s390x.h" + +struct reg regs[] = { + { "pswm", 0, 64 }, + { "pswa", 64, 64 }, + { "r0", 128, 64 }, + { "r1", 192, 64 }, + { "r2", 256, 64 }, + { "r3", 320, 64 }, + { "r4", 384, 64 }, + { "r5", 448, 64 }, + { "r6", 512, 64 }, + { "r7", 576, 64 }, + { "r8", 640, 64 }, + { "r9", 704, 64 }, + { "r10", 768, 64 }, + { "r11", 832, 64 }, + { "r12", 896, 64 }, + { "r13", 960, 64 }, + { "r14", 1024, 64 }, + { "r15", 1088, 64 }, + { "acr0", 1152, 32 }, + { "acr1", 1184, 32 }, + { "acr2", 1216, 32 }, + { "acr3", 1248, 32 }, + { "acr4", 1280, 32 }, + { "acr5", 1312, 32 }, + { "acr6", 1344, 32 }, + { "acr7", 1376, 32 }, + { "acr8", 1408, 32 }, + { "acr9", 1440, 32 }, + { "acr10", 1472, 32 }, + { "acr11", 1504, 32 }, + { "acr12", 1536, 32 }, + { "acr13", 1568, 32 }, + { "acr14", 1600, 32 }, + { "acr15", 1632, 32 }, + { "fpc", 1664, 32 }, + { "f0", 1696, 64 }, + { "f1", 1760, 64 }, + { "f2", 1824, 64 }, + { "f3", 1888, 64 }, + { "f4", 1952, 64 }, + { "f5", 2016, 64 }, + { "f6", 2080, 64 }, + { "f7", 2144, 64 }, + { "f8", 2208, 64 }, + { "f9", 2272, 64 }, + { "f10", 2336, 64 }, + { "f11", 2400, 64 }, + { "f12", 2464, 64 }, + { "f13", 2528, 64 }, + { "f14", 2592, 64 }, + { "f15", 2656, 64 }, +}; +static const char *expedite_regs[] = { "r14", "r15", "pswa", 0 }; +#define num_regs (sizeof (regs) / sizeof (regs[0])) + +static +CORE_ADDR get_pc (void) +{ + unsigned long pc; + + collect_register_by_name ("pswa", &pc); + + dlog(1, "stop pc is %p\n", (void *) pc); + return pc; +} + +static +void set_pc (CORE_ADDR newpc) +{ + Bool mod; + supply_register_by_name ("pswa", &newpc, &mod); + if (mod) + dlog(1, "set pc to %p\n", C2v (newpc)); + else + dlog(1, "set pc not changed %p\n", C2v (newpc)); +} + +/* store registers in the guest state (gdbserver_to_valgrind) + or fetch register from the guest state (valgrind_to_gdbserver). */ +static +void transfer_register (ThreadId tid, int abs_regno, void * buf, + transfer_direction dir, int size, Bool *mod) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int set = abs_regno / num_regs; + int regno = abs_regno % num_regs; + *mod = False; + + VexGuestS390XState* s390x = (VexGuestS390XState*) get_arch (set, tst); + + switch (regno) { + // numbers here have to match the order of regs above + // Attention: gdb order does not match valgrind order. + case 0: *mod = False; //GDBTD??? { "pswm", 0, 64 }, + case 1: VG_(transfer) (&s390x->guest_IA, buf, dir, size, mod); break; + case 2: VG_(transfer) (&s390x->guest_r0, buf, dir, size, mod); break; + case 3: VG_(transfer) (&s390x->guest_r1, buf, dir, size, mod); break; + case 4: VG_(transfer) (&s390x->guest_r2, buf, dir, size, mod); break; + case 5: VG_(transfer) (&s390x->guest_r3, buf, dir, size, mod); break; + case 6: VG_(transfer) (&s390x->guest_r4, buf, dir, size, mod); break; + case 7: VG_(transfer) (&s390x->guest_r5, buf, dir, size, mod); break; + case 8: VG_(transfer) (&s390x->guest_r6, buf, dir, size, mod); break; + case 9: VG_(transfer) (&s390x->guest_r7, buf, dir, size, mod); break; + case 10: VG_(transfer) (&s390x->guest_r8, buf, dir, size, mod); break; + case 11: VG_(transfer) (&s390x->guest_r9, buf, dir, size, mod); break; + case 12: VG_(transfer) (&s390x->guest_r10, buf, dir, size, mod); break; + case 13: VG_(transfer) (&s390x->guest_r11, buf, dir, size, mod); break; + case 14: VG_(transfer) (&s390x->guest_r12, buf, dir, size, mod); break; + case 15: VG_(transfer) (&s390x->guest_r13, buf, dir, size, mod); break; + case 16: VG_(transfer) (&s390x->guest_r14, buf, dir, size, mod); break; + case 17: VG_(transfer) (&s390x->guest_r15, buf, dir, size, mod); break; + case 18: VG_(transfer) (&s390x->guest_a0, buf, dir, size, mod); break; + case 19: VG_(transfer) (&s390x->guest_a1, buf, dir, size, mod); break; + case 20: VG_(transfer) (&s390x->guest_a2, buf, dir, size, mod); break; + case 21: VG_(transfer) (&s390x->guest_a3, buf, dir, size, mod); break; + case 22: VG_(transfer) (&s390x->guest_a4, buf, dir, size, mod); break; + case 23: VG_(transfer) (&s390x->guest_a5, buf, dir, size, mod); break; + case 24: VG_(transfer) (&s390x->guest_a6, buf, dir, size, mod); break; + case 25: VG_(transfer) (&s390x->guest_a7, buf, dir, size, mod); break; + case 26: VG_(transfer) (&s390x->guest_a8, buf, dir, size, mod); break; + case 27: VG_(transfer) (&s390x->guest_a9, buf, dir, size, mod); break; + case 28: VG_(transfer) (&s390x->guest_a10, buf, dir, size, mod); break; + case 29: VG_(transfer) (&s390x->guest_a11, buf, dir, size, mod); break; + case 30: VG_(transfer) (&s390x->guest_a12, buf, dir, size, mod); break; + case 31: VG_(transfer) (&s390x->guest_a13, buf, dir, size, mod); break; + case 32: VG_(transfer) (&s390x->guest_a14, buf, dir, size, mod); break; + case 33: VG_(transfer) (&s390x->guest_a15, buf, dir, size, mod); break; + case 34: VG_(transfer) (&s390x->guest_fpc, buf, dir, size, mod); break; + case 35: VG_(transfer) (&s390x->guest_f0, buf, dir, size, mod); break; + case 36: VG_(transfer) (&s390x->guest_f1, buf, dir, size, mod); break; + case 37: VG_(transfer) (&s390x->guest_f2, buf, dir, size, mod); break; + case 38: VG_(transfer) (&s390x->guest_f3, buf, dir, size, mod); break; + case 39: VG_(transfer) (&s390x->guest_f4, buf, dir, size, mod); break; + case 40: VG_(transfer) (&s390x->guest_f5, buf, dir, size, mod); break; + case 41: VG_(transfer) (&s390x->guest_f6, buf, dir, size, mod); break; + case 42: VG_(transfer) (&s390x->guest_f7, buf, dir, size, mod); break; + case 43: VG_(transfer) (&s390x->guest_f8, buf, dir, size, mod); break; + case 44: VG_(transfer) (&s390x->guest_f9, buf, dir, size, mod); break; + case 45: VG_(transfer) (&s390x->guest_f10, buf, dir, size, mod); break; + case 46: VG_(transfer) (&s390x->guest_f11, buf, dir, size, mod); break; + case 47: VG_(transfer) (&s390x->guest_f12, buf, dir, size, mod); break; + case 48: VG_(transfer) (&s390x->guest_f13, buf, dir, size, mod); break; + case 49: VG_(transfer) (&s390x->guest_f14, buf, dir, size, mod); break; + case 50: VG_(transfer) (&s390x->guest_f15, buf, dir, size, mod); break; + default: vg_assert(0); + } +} + +static struct valgrind_target_ops low_target = { + num_regs, + regs, + 17, //sp = r15, which is register offset 17 in regs + transfer_register, + get_pc, + set_pc, + "s390x", + NULL, // target_xml not needed. + NULL // no xml shadow target description (yet?) +}; + +void s390x_init_architecture (struct valgrind_target_ops *target) +{ + *target = low_target; + set_register_cache (regs, num_regs); + gdbserver_expedite_regs = expedite_regs; +} diff --git a/coregrind/m_gdbserver/valgrind-low-x86.c b/coregrind/m_gdbserver/valgrind-low-x86.c new file mode 100644 index 00000000..89ef3297 --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low-x86.c @@ -0,0 +1,276 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" + +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" + +#include "valgrind_low.h" + +#include "libvex_guest_x86.h" +/* GDBTD: ??? have a cleaner way to get the f80 <> f64 conversion functions */ +/* below include needed for conversion f80 <> f64 */ +#include "../../VEX/priv/guest_generic_x87.h" + + +/* below loosely inspired from file generated with gdb regdat.sh */ + +static struct reg regs[] = { + { "eax", 0, 32 }, + { "ecx", 32, 32 }, + { "edx", 64, 32 }, + { "ebx", 96, 32 }, + { "esp", 128, 32 }, + { "ebp", 160, 32 }, + { "esi", 192, 32 }, + { "edi", 224, 32 }, + { "eip", 256, 32 }, + { "eflags", 288, 32 }, + { "cs", 320, 32 }, + { "ss", 352, 32 }, + { "ds", 384, 32 }, + { "es", 416, 32 }, + { "fs", 448, 32 }, + { "gs", 480, 32 }, + { "st0", 512, 80 }, + { "st1", 592, 80 }, + { "st2", 672, 80 }, + { "st3", 752, 80 }, + { "st4", 832, 80 }, + { "st5", 912, 80 }, + { "st6", 992, 80 }, + { "st7", 1072, 80 }, + { "fctrl", 1152, 32 }, + { "fstat", 1184, 32 }, + { "ftag", 1216, 32 }, + { "fiseg", 1248, 32 }, + { "fioff", 1280, 32 }, + { "foseg", 1312, 32 }, + { "fooff", 1344, 32 }, + { "fop", 1376, 32 }, + { "xmm0", 1408, 128 }, + { "xmm1", 1536, 128 }, + { "xmm2", 1664, 128 }, + { "xmm3", 1792, 128 }, + { "xmm4", 1920, 128 }, + { "xmm5", 2048, 128 }, + { "xmm6", 2176, 128 }, + { "xmm7", 2304, 128 }, + { "mxcsr", 2432, 32 }, +#if defined(VGO_linux) + { "orig_eax", 2464, 32 } +#endif +}; +static const char *expedite_regs[] = { "ebp", "esp", "eip", 0 }; +#define num_regs (sizeof (regs) / sizeof (regs[0])) + +static +CORE_ADDR get_pc (void) +{ + unsigned long pc; + + collect_register_by_name ("eip", &pc); + + dlog(1, "stop pc is %p\n", (void *) pc); + return pc; +} + +static +void set_pc (CORE_ADDR newpc) +{ + Bool mod; + supply_register_by_name ("eip", &newpc, &mod); + if (mod) + dlog(1, "set pc to %p\n", C2v (newpc)); + else + dlog(1, "set pc not changed %p\n", C2v (newpc)); +} + +/* store registers in the guest state (gdbserver_to_valgrind) + or fetch register from the guest state (valgrind_to_gdbserver). */ +static +void transfer_register (ThreadId tid, int abs_regno, void * buf, + transfer_direction dir, int size, Bool *mod) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + int set = abs_regno / num_regs; + int regno = abs_regno % num_regs; + *mod = False; + + VexGuestX86State* x86 = (VexGuestX86State*) get_arch (set, tst); + + switch (regno) { + // numbers here have to match the order of regs above + // Attention: gdb order does not match valgrind order. + case 0: VG_(transfer) (&x86->guest_EAX, buf, dir, size, mod); break; + case 1: VG_(transfer) (&x86->guest_ECX, buf, dir, size, mod); break; + case 2: VG_(transfer) (&x86->guest_EDX, buf, dir, size, mod); break; + case 3: VG_(transfer) (&x86->guest_EBX, buf, dir, size, mod); break; + case 4: VG_(transfer) (&x86->guest_ESP, buf, dir, size, mod); break; + case 5: VG_(transfer) (&x86->guest_EBP, buf, dir, size, mod); break; + case 6: VG_(transfer) (&x86->guest_ESI, buf, dir, size, mod); break; + case 7: VG_(transfer) (&x86->guest_EDI, buf, dir, size, mod); break; + case 8: + VG_(transfer) (&x86->guest_EIP, buf, dir, size, mod); + if (*mod && VG_(debugLog_getLevel)() > 2) { + char bufimage [2*sizeof(x86->guest_IP_AT_SYSCALL) + 1]; + heximage (bufimage, + (char *) &x86->guest_IP_AT_SYSCALL, + sizeof(x86->guest_IP_AT_SYSCALL)); + dlog(3, "guest_IP_AT_SYSCALL %s\n", bufimage); + } + break; + case 9: + if (dir == valgrind_to_gdbserver) { + UInt eflags; + /* we can only retrieve the real flags (set 0) + retrieving shadow flags is not ok */ + if (set == 0) + eflags = LibVEX_GuestX86_get_eflags (x86); + else + eflags = 0; + VG_(transfer) (&eflags, buf, dir, size, mod); break; + } else { + *mod = False; //GDBTD? how do we store eflags in libvex_guest_x86.h ??? + } + break; + case 10: VG_(transfer) (&x86->guest_CS, buf, dir, size, mod); break; + case 11: VG_(transfer) (&x86->guest_SS, buf, dir, size, mod); break; + case 12: VG_(transfer) (&x86->guest_DS, buf, dir, size, mod); break; + case 13: VG_(transfer) (&x86->guest_ES, buf, dir, size, mod); break; + case 14: VG_(transfer) (&x86->guest_FS, buf, dir, size, mod); break; + case 15: VG_(transfer) (&x86->guest_GS, buf, dir, size, mod); break; + case 16: + case 17: + case 18: + case 19: /* register 16 to 23 are float registers 80 bits but 64 bits in valgrind */ + case 20: + case 21: + case 22: + case 23: { + if (dir == valgrind_to_gdbserver) { + UChar fpreg80[10]; + convert_f64le_to_f80le ((UChar *)&x86->guest_FPREG[regno-16], + fpreg80); + VG_(transfer) (&fpreg80, buf, dir, sizeof(fpreg80), mod); + } else { + ULong fpreg64; + convert_f80le_to_f64le (buf, (UChar *)&fpreg64); + VG_(transfer) (&x86->guest_FPREG[regno-16], &fpreg64, + dir, sizeof(fpreg64), mod); + } + break; + } + case 24: + if (dir == valgrind_to_gdbserver) { + // vex only models the rounding bits (see libvex_guest_x86.h) + UWord value = 0x037f; + value |= x86->guest_FPROUND << 10; + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX { "fctrl", 1152, 32 }, + } + break; + case 25: + if (dir == valgrind_to_gdbserver) { + UWord value = x86->guest_FC3210; + value |= (x86->guest_FTOP & 7) << 11; + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX { "fstat", 1184, 32 }, + } + break; + case 26: + if (dir == valgrind_to_gdbserver) { + // vex doesn't model these precisely + UWord value = + ((x86->guest_FPTAG[0] ? 0 : 3) << 0) | + ((x86->guest_FPTAG[1] ? 0 : 3) << 2) | + ((x86->guest_FPTAG[2] ? 0 : 3) << 4) | + ((x86->guest_FPTAG[3] ? 0 : 3) << 6) | + ((x86->guest_FPTAG[4] ? 0 : 3) << 8) | + ((x86->guest_FPTAG[5] ? 0 : 3) << 10) | + ((x86->guest_FPTAG[6] ? 0 : 3) << 12) | + ((x86->guest_FPTAG[7] ? 0 : 3) << 14); + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX { "ftag", 1216, 32 }, + } + break; + case 27: *mod = False; break; // GDBTD???? VEX { "fiseg", 1248, 32 }, + case 28: *mod = False; break; // GDBTD???? VEX { "fioff", 1280, 32 }, + case 29: *mod = False; break; // GDBTD???? VEX { "foseg", 1312, 32 }, + case 30: *mod = False; break; // GDBTD???? VEX { "fooff", 1344, 32 }, + case 31: *mod = False; break; // GDBTD???? VEX { "fop", 1376, 32 }, + case 32: VG_(transfer) (&x86->guest_XMM0, buf, dir, size, mod); break; + case 33: VG_(transfer) (&x86->guest_XMM1, buf, dir, size, mod); break; + case 34: VG_(transfer) (&x86->guest_XMM2, buf, dir, size, mod); break; + case 35: VG_(transfer) (&x86->guest_XMM3, buf, dir, size, mod); break; + case 36: VG_(transfer) (&x86->guest_XMM4, buf, dir, size, mod); break; + case 37: VG_(transfer) (&x86->guest_XMM5, buf, dir, size, mod); break; + case 38: VG_(transfer) (&x86->guest_XMM6, buf, dir, size, mod); break; + case 39: VG_(transfer) (&x86->guest_XMM7, buf, dir, size, mod); break; + case 40: + if (dir == valgrind_to_gdbserver) { + // vex only models the rounding bits (see libvex_guest_x86.h) + UWord value = 0x1f80; + value |= x86->guest_SSEROUND << 13; + VG_(transfer)(&value, buf, dir, size, mod); + } else { + *mod = False; // GDBTD???? VEX { "mxcsr", 2432, 32 }, + } + break; + case 41: *mod = False; break; // GDBTD???? VEX { "orig_eax", 2464, 32 }, + default: vg_assert(0); + } +} + +static struct valgrind_target_ops low_target = { + num_regs, + regs, + 4, //ESP + transfer_register, + get_pc, + set_pc, + "i386", + NULL, // target_xml not needed. +#if defined(VGO_linux) + "i386-linux-valgrind.xml" +#else + "i386-coresse-valgrind.xml" +#endif +}; + +void x86_init_architecture (struct valgrind_target_ops *target) +{ + *target = low_target; + set_register_cache (regs, num_regs); + gdbserver_expedite_regs = expedite_regs; +} diff --git a/coregrind/m_gdbserver/valgrind-low.c b/coregrind/m_gdbserver/valgrind-low.c new file mode 100644 index 00000000..fc376faf --- /dev/null +++ b/coregrind/m_gdbserver/valgrind-low.c @@ -0,0 +1,639 @@ +/* Low level interface to valgrind, for the remote server for GDB integrated + in valgrind. + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file is part of VALGRIND. + It has been inspired from a file from gdbserver in gdb 6.6. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "target.h" +#include "regdef.h" +#include "regcache.h" +#include "valgrind_low.h" +#include "gdb/signals.h" +#include "pub_core_aspacemgr.h" +#include "pub_tool_machine.h" +#include "pub_core_threadstate.h" +#include "pub_core_transtab.h" +#include "pub_core_gdbserver.h" +#include "pub_tool_debuginfo.h" + +/* the_low_target defines the architecture specific aspects depending + on the cpu */ +static struct valgrind_target_ops the_low_target; + +/* builds an image of bin according to byte order of the architecture + Useful for register and int image */ +char* heximage (char *buf, char *bin, int count) +{ +#if defined(VGA_x86) || defined(VGA_amd64) + char rev[count]; + /* note: no need for trailing \0, length is known with count */ + int i; + for (i = 0; i < count; i++) + rev[i] = bin[count - i - 1]; + hexify (buf, rev, count); +#else + hexify (buf, bin, count); +#endif + return buf; +} + +void* C2v(CORE_ADDR addr) +{ + return (void*) addr; +} + +static +char *image_ptid(unsigned long ptid) +{ + static char result[100]; + VG_(sprintf) (result, "id %ld", ptid); + return result; +} +#define get_thread(inf) ((struct thread_info *)(inf)) +static +void remove_thread_if_not_in_vg_threads (struct inferior_list_entry *inf) +{ + struct thread_info *thread = get_thread (inf); + if (!VG_(lwpid_to_vgtid)(thread_to_gdb_id(thread))) { + dlog(1, "removing gdb ptid %s\n", + image_ptid(thread_to_gdb_id(thread))); + remove_thread (thread); + } +} + +/* synchronize threads known by valgrind and threads known by gdbserver */ +static +void valgrind_update_threads (int pid) +{ + ThreadId tid; + ThreadState *ts; + unsigned long ptid; + struct thread_info *ti; + + /* call remove_thread for all gdb threads not in valgrind threads */ + for_each_inferior (&all_threads, remove_thread_if_not_in_vg_threads); + + /* call add_thread for all valgrind threads not known in gdb all_threads */ + for (tid = 1; tid < VG_N_THREADS; tid++) { + +#define LOCAL_THREAD_TRACE " ti* %p vgtid %d status %s as gdb ptid %s lwpid %d\n", \ + ti, tid, VG_(name_of_ThreadStatus) (ts->status), \ + image_ptid (ptid), ts->os_state.lwpid + + if (VG_(is_valid_tid) (tid)) { + ts = VG_(get_ThreadState) (tid); + ptid = ts->os_state.lwpid; + ti = gdb_id_to_thread (ptid); + if (!ti) { + /* we do not report the threads which are not yet fully + initialized otherwise this creates duplicated threads + in gdb: once with pid xxx lwpid 0, then after that + with pid xxx lwpid yyy. */ + if (ts->status != VgTs_Init) { + dlog(1, "adding_thread" LOCAL_THREAD_TRACE); + add_thread (ptid, ts, ptid); + } + } else { + dlog(2, "(known thread)" LOCAL_THREAD_TRACE); + } + } +#undef LOCAL_THREAD_TRACE + } +} + +/* Return nonzero if the given thread is still alive. */ +static +int valgrind_thread_alive (unsigned long tid) +{ + struct thread_info *ti = gdb_id_to_thread(tid); + ThreadState *tst; + + if (ti != NULL) { + tst = (ThreadState *) inferior_target_data (ti); + return tst->status != VgTs_Zombie; + } + else { + return 0; + } +} + +/* allocate and build a register structure containing the shadow registers. + reg_defs is the normal registers, n is their numbers */ +static +struct reg* build_shadow_arch (struct reg *reg_defs, int n) { + int i, r; + static char *postfix[3] = { "", "s1", "s2" }; + struct reg *new_regs = malloc(3 * n * sizeof(reg_defs[0])); + int reg_set_len = reg_defs[n-1].offset + reg_defs[n-1].size; + + for (i = 0; i < 3; i++) { + for (r = 0; r < n; r++) { + new_regs[i*n + r].name = malloc(strlen(reg_defs[r].name) + + strlen (postfix[i]) + 1); + strcpy (new_regs[i*n + r].name, reg_defs[r].name); + strcat (new_regs[i*n + r].name, postfix[i]); + new_regs[i*n + r].offset = i*reg_set_len + reg_defs[r].offset; + new_regs[i*n + r].size = reg_defs[r].size; + dlog(1, + "%10s Nr %d offset(bit) %d offset(byte) %d size(bit) %d\n", + new_regs[i*n + r].name, i*n + r, new_regs[i*n + r].offset, + (new_regs[i*n + r].offset) / 8, new_regs[i*n + r].size); + } + } + + return new_regs; +} + +/* Fetch one register from valgrind VEX guest state. */ +static +void fetch_register (int regno) +{ + int size; + ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); + ThreadId tid = tst->tid; + + if (regno >= the_low_target.num_regs) { + dlog(0, "error fetch_register regno %d max %d\n", + regno, the_low_target.num_regs); + return; + } + size = register_size (regno); + { + Bool mod; + char buf [size]; + VG_(memset) (buf, 0, size); // registers not fetched will be seen as 0. + (*the_low_target.transfer_register) (tid, regno, buf, + valgrind_to_gdbserver, size, &mod); + // Note: the *mod received from transfer_register is not interesting. + // We are interested to see if the register data in the register cache is modified. + supply_register (regno, buf, &mod); + if (mod && VG_(debugLog_getLevel)() > 1) { + char bufimage [2*size + 1]; + heximage (bufimage, buf, size); + dlog(2, "fetched register %d size %d name %s value %s tid %d status %s\n", + regno, size, the_low_target.reg_defs[regno].name, bufimage, + tid, VG_(name_of_ThreadStatus) (tst->status)); + } + } +} + +/* Fetch all registers, or just one, from the child process. */ +static +void usr_fetch_inferior_registers (int regno) +{ + if (regno == -1 || regno == 0) + for (regno = 0; regno < the_low_target.num_regs; regno++) + fetch_register (regno); + else + fetch_register (regno); +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ +static +void usr_store_inferior_registers (int regno) +{ + int size; + ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); + ThreadId tid = tst->tid; + + if (regno >= 0) { + + if (regno >= the_low_target.num_regs) { + dlog(0, "error store_register regno %d max %d\n", + regno, the_low_target.num_regs); + return; + } + + size = register_size (regno); + { + Bool mod; + Addr old_SP, new_SP; + char buf[size]; + + if (regno == the_low_target.stack_pointer_regno) { + /* When the stack pointer register is changed such that + the stack is extended, we better inform the tool of the + stack increase. This is needed in particular to avoid + spurious Memcheck errors during Inferior calls. So, we + save in old_SP the SP before the change. A change of + stack pointer is also assumed to have initialised this + new stack space. For the typical example of an inferior + call, gdb writes arguments on the stack, and then + changes the stack pointer. As the stack increase tool + function might mark it as undefined, we have to call it + at the good moment. */ + VG_(memset) ((void *) &old_SP, 0, size); + (*the_low_target.transfer_register) (tid, regno, (void *) &old_SP, + valgrind_to_gdbserver, size, &mod); + } + + VG_(memset) (buf, 0, size); + collect_register (regno, buf); + (*the_low_target.transfer_register) (tid, regno, buf, + gdbserver_to_valgrind, size, &mod); + if (mod && VG_(debugLog_getLevel)() > 1) { + char bufimage [2*size + 1]; + heximage (bufimage, buf, size); + dlog(2, + "stored register %d size %d name %s value %s " + "tid %d status %s\n", + regno, size, the_low_target.reg_defs[regno].name, bufimage, + tid, VG_(name_of_ThreadStatus) (tst->status)); + } + if (regno == the_low_target.stack_pointer_regno) { + VG_(memcpy) (&new_SP, buf, size); + if (old_SP > new_SP) { + Word delta = (Word)new_SP - (Word)old_SP; + dlog(1, + " stack increase by stack pointer changed from %p to %p " + "delta %ld\n", + (void*) old_SP, (void *) new_SP, + delta); + VG_TRACK( new_mem_stack_w_ECU, new_SP, -delta, 0 ); + VG_TRACK( new_mem_stack, new_SP, -delta ); + if (VG_(tdict).track_post_mem_write) { + VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid, + new_SP, -delta); + } + } + } + } + } + else { + for (regno = 0; regno < the_low_target.num_regs; regno++) + usr_store_inferior_registers (regno); + } +} + +static +void valgrind_fetch_registers (int regno) +{ + usr_fetch_inferior_registers (regno); +} + +static +void valgrind_store_registers (int regno) +{ + usr_store_inferior_registers (regno); +} + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +static +int valgrind_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + const void *sourceaddr = C2v (memaddr); + dlog(2, "reading memory %p size %d\n", sourceaddr, len); + if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr) sourceaddr, + len, VKI_PROT_READ)) { + dlog(1, "error reading memory %p size %d\n", sourceaddr, len); + return -1; + } + VG_(memcpy) (myaddr, sourceaddr, len); + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +static +int valgrind_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) +{ + void *targetaddr = C2v (memaddr); + dlog(2, "writing memory %p size %d\n", targetaddr, len); + if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr)targetaddr, + len, VKI_PROT_WRITE)) { + dlog(1, "error writing memory %p size %d\n", targetaddr, len); + return -1; + } + if (len > 0) { + VG_(memcpy) (targetaddr, myaddr, len); + if (VG_(tdict).track_post_mem_write) { + /* Inform the tool of the post memwrite. Note that we do the + minimum necessary to avoid complains from e.g. + memcheck. The idea is that the debugger is as least + intrusive as possible. So, we do not inform of the pre + mem write (and in any case, this would cause problems with + memcheck that does not like our CorePart in + pre_mem_write. */ + ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); + ThreadId tid = tst->tid; + VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid, (Addr) targetaddr, len ); + } + } + return 0; +} + +/* insert or remove a breakpoint */ +static +int valgrind_point (Bool insert, char type, CORE_ADDR addr, int len) +{ + PointKind kind; + switch (type) { + case '0': /* implemented by inserting checks at each instruction in sb */ + kind = software_breakpoint; + break; + case '1': /* hw breakpoint, same implementation as sw breakpoint */ + kind = hardware_breakpoint; + break; + case '2': + kind = write_watchpoint; + break; + case '3': + kind = read_watchpoint; + break; + case '4': + kind = access_watchpoint; + break; + default: + vg_assert (0); + } + + /* Attention: gdbserver convention differs: 0 means ok; 1 means not ok */ + if (VG_(gdbserver_point) (kind, insert, addr, len)) + return 0; + else + return 1; /* error or unsupported */ +} + +static +void valgrind_send_signal (int sig) +{ + dlog(1, "valgrind_send_signal %d called ????\n", sig); +} + +static +char* valgrind_target_xml (void) +{ + return (char *) the_low_target.target_xml; +} + +static +char* valgrind_shadow_target_xml (void) +{ + return (char *) the_low_target.shadow_target_xml; +} + +static +int valgrind_insert_point (char type, CORE_ADDR addr, int len) +{ + return valgrind_point (/* insert */ True, type, addr, len); +} + +static +int valgrind_remove_point (char type, CORE_ADDR addr, int len) +{ + return valgrind_point (/* insert*/ False, type, addr, len); +} + +static CORE_ADDR stopped_data_address = 0; +void VG_(set_watchpoint_stop_address) (Addr addr) +{ + stopped_data_address = addr; +} + +static +int valgrind_stopped_by_watchpoint (void) +{ + return stopped_data_address != 0; +} + +static +CORE_ADDR valgrind_stopped_data_address (void) +{ + return stopped_data_address; +} + +/* pc at which we last stopped */ +static CORE_ADDR stop_pc; + +/* pc at which we resume. + If stop_pc != resume_pc, it means + gdb/gdbserver has changed the pc so as to have either + a "continue by jumping at that address" + or a "continue at that address to call some code from gdb". +*/ +static CORE_ADDR resume_pc; + +static int signal_to_report; + +void gdbserver_signal_encountered (Int sigNo) +{ + signal_to_report = sigNo; +} + +static int signal_to_deliver; +Bool gdbserver_deliver_signal (Int sigNo) +{ + return sigNo == signal_to_deliver; +} + +static +char* sym (Addr addr) +{ + static char buf[200]; + VG_(describe_IP) (addr, buf, 200); + return buf; +} + +ThreadId vgdb_interrupted_tid = 0; +/* called to wait for the process to stop */ +static +unsigned char valgrind_wait (char *ourstatus) +{ + int pid; + unsigned long wptid; + ThreadState *tst; + enum target_signal sig; + + pid = VG_(getpid) (); + dlog(1, "enter valgrind_wait pid %d\n", pid); + + regcache_invalidate(); + valgrind_update_threads(pid); + + /* in valgrind, we consider that a wait always succeeds with STOPPED 'T' + and with a signal TRAP (i.e. a breakpoint), unless there is + a signal to report. */ + *ourstatus = 'T'; + if (signal_to_report == 0) + sig = TARGET_SIGNAL_TRAP; + else + sig = target_signal_from_host(signal_to_report); + + if (vgdb_interrupted_tid != 0) + tst = VG_(get_ThreadState) (vgdb_interrupted_tid); + else + tst = VG_(get_ThreadState) (VG_(running_tid)); + wptid = tst->os_state.lwpid; + /* we can only change the current_inferior when the wptid references + an existing thread. Otherwise, we are still in the init phase. + (hack similar to main thread hack in valgrind_update_threads) */ + if (tst->os_state.lwpid) + current_inferior = gdb_id_to_thread (wptid); + stop_pc = (*the_low_target.get_pc) (); + + dlog(1, + "exit valgrind_wait returns ptid %s stop_pc %s signal %d\n", + image_ptid (wptid), sym (stop_pc), sig); + return sig; +} + +/* 0 => not single stepping. + 1 => single stepping asked by gdb + 2 => single stepping asked by valgrind (watchpoint) */ +static int stepping = 0; + +/* called when the process is to be resumed */ +static +void valgrind_resume (struct thread_resume *resume_info) +{ + dlog(1, + "resume_info thread %ld leave_stopped %d step %d sig %d stepping %d\n", + resume_info->thread, + resume_info->leave_stopped, + resume_info->step, + resume_info->sig, + stepping); + if (valgrind_stopped_by_watchpoint()) { + dlog(1, "clearing watchpoint stopped_data_address %p\n", + C2v(stopped_data_address)); + VG_(set_watchpoint_stop_address) ((Addr) 0); + } + signal_to_deliver = resume_info->sig; + + stepping = resume_info->step; + resume_pc = (*the_low_target.get_pc) (); + if (resume_pc != stop_pc) { + dlog(1, + "stop_pc %p changed to be resume_pc %s\n", + C2v(stop_pc), sym(resume_pc)); + } + regcache_invalidate(); +} + +Addr valgrind_get_ignore_break_once(void) +{ + if (valgrind_single_stepping()) + return resume_pc; + else + return 0; +} + + +void valgrind_set_single_stepping(Bool set) +{ + if (set) + stepping = 2; + else + stepping = 0; +} + +Bool valgrind_single_stepping(void) +{ + if (stepping) + return True; + else + return False; +} + +static struct target_ops valgrind_target_ops = { + valgrind_thread_alive, + valgrind_resume, + valgrind_wait, + valgrind_fetch_registers, + valgrind_store_registers, + valgrind_read_memory, + valgrind_write_memory, + valgrind_send_signal, + valgrind_target_xml, + valgrind_shadow_target_xml, + valgrind_insert_point, + valgrind_remove_point, + valgrind_stopped_by_watchpoint, + valgrind_stopped_data_address, +}; + + +/* returns a pointer to the architecture state corresponding to + the provided register set: 0 => normal guest registers, + 1 => shadow1 + 2 => shadow2 +*/ +VexGuestArchState* get_arch (int set, ThreadState* tst) +{ + switch (set) { + case 0: return &tst->arch.vex; + case 1: return &tst->arch.vex_shadow1; + case 2: return &tst->arch.vex_shadow2; + default: vg_assert(0); + } +} + +static int non_shadow_num_regs = 0; +static struct reg *non_shadow_reg_defs = NULL; +void initialize_shadow_low(Bool shadow_mode) +{ + if (non_shadow_reg_defs == NULL) { + non_shadow_reg_defs = the_low_target.reg_defs; + non_shadow_num_regs = the_low_target.num_regs; + } + + regcache_invalidate(); + if (the_low_target.reg_defs != non_shadow_reg_defs) { + free (the_low_target.reg_defs); + } + if (shadow_mode) { + the_low_target.num_regs = 3 * non_shadow_num_regs; + the_low_target.reg_defs = build_shadow_arch (non_shadow_reg_defs, non_shadow_num_regs); + } else { + the_low_target.num_regs = non_shadow_num_regs; + the_low_target.reg_defs = non_shadow_reg_defs; + } + set_register_cache (the_low_target.reg_defs, the_low_target.num_regs); +} + +void initialize_low(void) +{ + set_target_ops (&valgrind_target_ops); + +#if defined(VGA_x86) + x86_init_architecture(&the_low_target); +#elif defined(VGA_amd64) + amd64_init_architecture(&the_low_target); +#elif defined(VGA_arm) + arm_init_architecture(&the_low_target); +#elif defined(VGA_ppc32) + ppc32_init_architecture(&the_low_target); +#elif defined(VGA_ppc64) + ppc64_init_architecture(&the_low_target); +#elif defined(VGA_s390x) + s390x_init_architecture(&the_low_target); +#else + architecture missing in valgrind-low.c +#endif + +} diff --git a/coregrind/m_gdbserver/valgrind_low.h b/coregrind/m_gdbserver/valgrind_low.h new file mode 100644 index 00000000..6817110e --- /dev/null +++ b/coregrind/m_gdbserver/valgrind_low.h @@ -0,0 +1,91 @@ +/* Definitions of interface to the "low" (arch specific) functions + needed for interfacing the Valgrind gdbserver with the Valgrind + guest. + + Copyright (C) 2011 + Free Software Foundation, Inc. + + This file has been inspired from a file that is part of GDB. + It has been modified to integrate it in valgrind + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef VALGRIND_LOW_H +#define VALGRIND_LOW_H + +/* defines the characteristics of the "low" valgrind target architecture. + In other words, struct valgrind_target_ops defines the functions and + data which are specific to the architecture (x86 or amd64 or + ppc32 or ...). */ +struct valgrind_target_ops +{ + int num_regs; + struct reg *reg_defs; + + int stack_pointer_regno; + /* register number of the stack pointer register */ + + /* transfer the register regno from/to valgrind (guest state) + to/from buf + according to transfer_direction. + *mod set to True if destination content is modified by the transfer + otherwise it is set to False. */ + void (*transfer_register) (ThreadId tid, int regno, void * buf, + transfer_direction dir, int size, Bool *mod); + + + CORE_ADDR (*get_pc) (void); + void (*set_pc) (CORE_ADDR newpc); + + /* What string to report to GDB when it asks for the architecture, + or NULL not to answer. */ + const char *arch_string; + + /* Description of the set of registers. + For some architectures (e.g. arm), it is mandatory + to give a description of the registers, otherwise + gdb does not understand the reply to the 'g' packet + (which is used to get the registers). */ + const char *target_xml; + + /* Same as target_xml, but describes also the two shadow + registers set. + This is mandatory to use the option --vgdb-shadow-registers=yes. */ + const char *shadow_target_xml; +}; + + +/* convert from CORE_ADDR to void* */ +extern void* C2v(CORE_ADDR addr); + +/* builds an image of bin according to byte order of the architecture + Useful for register and int image */ +extern char* heximage (char *buf, char *bin, int count); + +/* returns a pointer to the architecture state corresponding to + the provided register set: 0 => normal guest registers, + 1 => shadow1 + 2 => shadow2 */ +VexGuestArchState* get_arch (int set, ThreadState* tst); + +extern void x86_init_architecture (struct valgrind_target_ops *target); +extern void amd64_init_architecture (struct valgrind_target_ops *target); +extern void arm_init_architecture (struct valgrind_target_ops *target); +extern void ppc32_init_architecture (struct valgrind_target_ops *target); +extern void ppc64_init_architecture (struct valgrind_target_ops *target); +extern void s390x_init_architecture (struct valgrind_target_ops *target); + +#endif diff --git a/coregrind/m_gdbserver/version.c b/coregrind/m_gdbserver/version.c new file mode 100644 index 00000000..7b2af402 --- /dev/null +++ b/coregrind/m_gdbserver/version.c @@ -0,0 +1,2 @@ +#include "server.h" +const char version[] = "gdbserver protocol box extracted from gdb 6.6"; diff --git a/coregrind/m_libcbase.c b/coregrind/m_libcbase.c index 98ff96b3..6d6a2740 100644 --- a/coregrind/m_libcbase.c +++ b/coregrind/m_libcbase.c @@ -89,6 +89,31 @@ Long VG_(strtoll10) ( Char* str, Char** endptr ) return n; } +ULong VG_(strtoull10) ( Char* str, Char** endptr ) +{ + Bool converted = False; + ULong n = 0; + Long digit = 0; + Char* str0 = str; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '+'. + if (*str == '+') { str++; } + + while (is_dec_digit(*str, &digit)) { + converted = True; // Ok, we've actually converted a digit. + n = 10*n + digit; + str++; + } + + if (!converted) str = str0; // If nothing converted, endptr points to + // the start of the string. + if (endptr) *endptr = str; // Record first failing character. + return n; +} + Long VG_(strtoll16) ( Char* str, Char** endptr ) { Bool neg = False, converted = False; @@ -122,6 +147,39 @@ Long VG_(strtoll16) ( Char* str, Char** endptr ) return n; } +ULong VG_(strtoull16) ( Char* str, Char** endptr ) +{ + Bool converted = False; + ULong n = 0; + Long digit = 0; + Char* str0 = str; + + // Skip leading whitespace. + while (VG_(isspace)(*str)) str++; + + // Allow a leading '+'. + if (*str == '+') { str++; } + + // Allow leading "0x", but only if there's a hex digit + // following it. + if (*str == '0' + && (*(str+1) == 'x' || *(str+1) == 'X') + && is_hex_digit( *(str+2), &digit )) { + str += 2; + } + + while (is_hex_digit(*str, &digit)) { + converted = True; // Ok, we've actually converted a digit. + n = 16*n + digit; + str++; + } + + if (!converted) str = str0; // If nothing converted, endptr points to + // the start of the string. + if (endptr) *endptr = str; // Record first failing character. + return n; +} + double VG_(strtod) ( Char* str, Char** endptr ) { Bool neg = False; @@ -356,6 +414,88 @@ Char* VG_(strrchr) ( const Char* s, Char c ) return NULL; } +/* (code copied from glib then updated to valgrind types) */ +static Char *olds; +Char * +VG_(strtok) (Char *s, const Char *delim) +{ + return VG_(strtok_r) (s, delim, &olds); +} + +Char * +VG_(strtok_r) (Char* s, const Char* delim, Char** saveptr) +{ + Char *token; + + if (s == NULL) + s = *saveptr; + + /* Scan leading delimiters. */ + s += VG_(strspn (s, delim)); + if (*s == '\0') + { + *saveptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + s = VG_(strpbrk (token, delim)); + if (s == NULL) + /* This token finishes the string. */ + *saveptr = token + VG_(strlen) (token); + else + { + /* Terminate the token and make OLDS point past it. */ + *s = '\0'; + *saveptr = s + 1; + } + return token; +} + +static Bool isHex ( UChar c ) +{ + return ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')); +} + +static UInt fromHex ( UChar c ) +{ + if (c >= '0' && c <= '9') + return (UInt)c - (UInt)'0'; + if (c >= 'a' && c <= 'f') + return 10 + (UInt)c - (UInt)'a'; + if (c >= 'A' && c <= 'F') + return 10 + (UInt)c - (UInt)'A'; + /*NOTREACHED*/ + // ??? need to vg_assert(0); + return 0; +} + +Bool VG_(parse_Addr) ( UChar** ppc, Addr* result ) +{ + Int used, limit = 2 * sizeof(Addr); + if (**ppc != '0') + return False; + (*ppc)++; + if (**ppc != 'x') + return False; + (*ppc)++; + *result = 0; + used = 0; + while (isHex(**ppc)) { + // ??? need to vg_assert(d < fromHex(**ppc)); + *result = ((*result) << 4) | fromHex(**ppc); + (*ppc)++; + used++; + if (used > limit) return False; + } + if (used == 0) + return False; + return True; +} + SizeT VG_(strspn) ( const Char* s, const Char* accpt ) { const Char *p, *a; diff --git a/coregrind/m_libcfile.c b/coregrind/m_libcfile.c index 8c1dbc99..2b9e4454 100644 --- a/coregrind/m_libcfile.c +++ b/coregrind/m_libcfile.c @@ -108,6 +108,17 @@ Bool VG_(resolve_filename) ( Int fd, HChar* buf, Int n_buf ) # endif } +SysRes VG_(mknod) ( const Char* pathname, Int mode, UWord dev ) +{ +# if defined(VGO_linux) || defined(VGO_aix5) || defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_mknod, + (UWord)pathname, mode, dev); +# else +# error Unknown OS +# endif + return res; +} + SysRes VG_(open) ( const Char* pathname, Int flags, Int mode ) { # if defined(VGO_linux) || defined(VGO_aix5) @@ -122,6 +133,16 @@ SysRes VG_(open) ( const Char* pathname, Int flags, Int mode ) return res; } +Int VG_(fd_open) (const Char* pathname, Int flags, Int mode) +{ + SysRes sr; + sr = VG_(open) (pathname, flags, mode); + if (sr_isError (sr)) + return -1; + else + return sr_Res (sr); +} + void VG_(close) ( Int fd ) { /* Hmm. Return value is not checked. That's uncool. */ @@ -444,6 +465,14 @@ Bool VG_(get_startup_wd) ( Char* buf, SizeT size ) return True; } +Int VG_(poll) (struct vki_pollfd *fds, Int nfds, Int timeout) +{ + SysRes res; + res = VG_(do_syscall3)(__NR_poll, (UWord)fds, nfds, timeout); + return sr_isError(res) ? -1 : sr_Res(res); +} + + Int VG_(readlink) (const Char* path, Char* buf, UInt bufsiz) { SysRes res; diff --git a/coregrind/m_libcprint.c b/coregrind/m_libcprint.c index dee9e566..fb2544d5 100644 --- a/coregrind/m_libcprint.c +++ b/coregrind/m_libcprint.c @@ -31,6 +31,7 @@ #include "pub_core_basics.h" #include "pub_core_vki.h" #include "pub_core_debuglog.h" +#include "pub_core_gdbserver.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcfile.h" // VG_(write)(), VG_(write_socket)() @@ -47,7 +48,10 @@ /* The destination sinks for normal and XML output. These have their initial values here; they are set to final values by m_main.main_process_cmd_line_options(). See comment at the top of - that function for the associated logic. */ + that function for the associated logic. + After startup, the gdbserver monitor command might temporarily + set the fd of log_output_sink to -2 to indicate that output is + to be given to gdb rather than output to the startup fd */ OutputSink VG_(log_output_sink) = { 2, False }; /* 2 = stderr */ OutputSink VG_(xml_output_sink) = { -1, False }; /* disabled */ @@ -70,6 +74,8 @@ void send_bytes_to_logging_sink ( OutputSink* sink, Char* msg, Int nbytes ) any more output. */ if (sink->fd >= 0) VG_(write)( sink->fd, msg, nbytes ); + else if (sink->fd == -2) + VG_(gdb_printf)("%s", msg); } } @@ -107,7 +113,7 @@ static UInt vprintf_to_buf ( printf_buf_t* b, const HChar *format, va_list vargs ) { UInt ret = 0; - if (b->sink->fd >= 0) { + if (b->sink->fd >= 0 || b->sink->fd == -2) { ret = VG_(debugLog_vprintf) ( add_to__printf_buf, b, format, vargs ); } diff --git a/coregrind/m_libcproc.c b/coregrind/m_libcproc.c index dbedac2e..96a233d7 100644 --- a/coregrind/m_libcproc.c +++ b/coregrind/m_libcproc.c @@ -418,6 +418,21 @@ Int VG_(setrlimit) (Int resource, const struct vki_rlimit *rlim) return sr_isError(res) ? -1 : sr_Res(res); } +/* Support for prctl. */ +Int VG_(prctl) (Int option, + ULong arg2, ULong arg3, ULong arg4, ULong arg5) +{ + SysRes res = VG_(mk_SysRes_Error)(VKI_ENOSYS); +# if defined(VGO_linux) + /* res = prctl( option, arg2, arg3, arg4, arg5 ); */ + res = VG_(do_syscall5)(__NR_prctl, (UWord) option, + (UWord) arg2, (UWord) arg3, (UWord) arg4, + (UWord) arg5); +# endif + + return sr_isError(res) ? -1 : sr_Res(res); +} + /* --------------------------------------------------------------------- pids, etc ------------------------------------------------------------------ */ diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 7f80f654..b27b5adb 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -41,6 +41,7 @@ #include "pub_core_debuglog.h" #include "pub_core_errormgr.h" #include "pub_core_execontext.h" +#include "pub_core_gdbserver.h" #include "pub_core_initimg.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" @@ -129,6 +130,9 @@ static void usage_NORETURN ( Bool debug_help ) " but check the argv[] entries for children, rather\n" " than the exe name, to make a follow/no-follow decision\n" " --child-silent-after-fork=no|yes omit child output between fork & exec? [no]\n" +" --vgdb=no|yes|full activate gdbserver? [yes]\n" +" full is slower but provides precise watchpoint/step\n" +" --vgdb-error=<number> invoke gdbserver after <number> errors [%d] \n" " --track-fds=no|yes track open file descriptors? [no]\n" " --time-stamp=no|yes add timestamps to log messages? [no]\n" " --log-fd=<number> log messages to file descriptor [2=stderr]\n" @@ -173,6 +177,9 @@ static void usage_NORETURN ( Bool debug_help ) " and use it to print better error messages in\n" " tools that make use of it (Memcheck, Helgrind,\n" " DRD) [no]\n" +" --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [%d] \n" +" --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no]\n" +" --vgdb-prefix=<prefix> prefix for vgdb FIFOs [%s]\n" " --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]\n" " --sim-hints=hint1,hint2,... known hints:\n" " lax-ioctls, enable-outer [none]\n" @@ -252,8 +259,10 @@ static void usage_NORETURN ( Bool debug_help ) VG_(log_output_sink).fd = 1; VG_(log_output_sink).is_socket = False; - /* 'usage1' expects one char* argument and one SizeT argument. */ - VG_(printf)(usage1, gdb_path, VG_MIN_MALLOC_SZB); + /* 'usage1' expects two int, two char* argument, and one SizeT argument. */ + VG_(printf)(usage1, + VG_(clo_vgdb_error), gdb_path, VG_MIN_MALLOC_SZB, + VG_(clo_vgdb_poll), VG_(clo_vgdb_prefix)); if (VG_(details).name) { VG_(printf)(" user options for %s:\n", VG_(details).name); if (VG_(needs).command_line_options) @@ -457,6 +466,14 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd, else if VG_BOOL_CLO(arg, "--stats", VG_(clo_stats)) {} else if VG_BOOL_CLO(arg, "--xml", VG_(clo_xml)) {} + else if VG_XACT_CLO(arg, "--vgdb=no", VG_(clo_vgdb), Vg_VgdbNo) {} + else if VG_XACT_CLO(arg, "--vgdb=yes", VG_(clo_vgdb), Vg_VgdbYes) {} + else if VG_XACT_CLO(arg, "--vgdb=full", VG_(clo_vgdb), Vg_VgdbFull) {} + else if VG_INT_CLO (arg, "--vgdb-poll", VG_(clo_vgdb_poll)) {} + else if VG_INT_CLO (arg, "--vgdb-error", VG_(clo_vgdb_error)) {} + else if VG_STR_CLO (arg, "--vgdb-prefix", VG_(clo_vgdb_prefix)) {} + else if VG_BOOL_CLO(arg, "--vgdb-shadow-registers", + VG_(clo_vgdb_shadow_registers)) {} else if VG_BOOL_CLO(arg, "--db-attach", VG_(clo_db_attach)) {} else if VG_BOOL_CLO(arg, "--demangle", VG_(clo_demangle)) {} else if VG_BOOL_CLO(arg, "--error-limit", VG_(clo_error_limit)) {} @@ -671,6 +688,8 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd, if (VG_(clo_verbosity) < 0) VG_(clo_verbosity) = 0; + VG_(dyn_vgdb_error) = VG_(clo_vgdb_error); + if (VG_(clo_gen_suppressions) > 0 && !VG_(needs).core_errors && !VG_(needs).tool_errors) { VG_(fmsg_bad_option)("--gen-suppressions=yes", @@ -2426,7 +2445,7 @@ void shutdown_actions_NORETURN( ThreadId tid, /* In XML mode, this merely prints the used suppressions. */ if (VG_(needs).core_errors || VG_(needs).tool_errors) - VG_(show_all_errors)(); + VG_(show_all_errors)(VG_(clo_verbosity), VG_(clo_xml)); if (VG_(clo_xml)) { VG_(printf_xml)("\n"); @@ -2461,6 +2480,10 @@ void shutdown_actions_NORETURN( ThreadId tid, /* Flush any output cached by previous calls to VG_(message). */ VG_(message_flush)(); + /* terminate gdbserver if ever it was started. We terminate it here so that it get + the output above if output was redirected to gdb */ + VG_(gdbserver) (0); + /* Ok, finally exit in the os-specific way, according to the scheduler's return code. In short, if the (last) thread exited by calling sys_exit, do likewise; if the (last) thread stopped due to a fatal diff --git a/coregrind/m_options.c b/coregrind/m_options.c index 888e2c78..59ec1fee 100644 --- a/coregrind/m_options.c +++ b/coregrind/m_options.c @@ -46,6 +46,11 @@ VexControl VG_(clo_vex_control); Bool VG_(clo_error_limit) = True; Int VG_(clo_error_exitcode) = 0; +VgVgdb VG_(clo_vgdb) = Vg_VgdbYes; +Int VG_(clo_vgdb_poll) = 5000; +Int VG_(clo_vgdb_error) = 999999999; +Char* VG_(clo_vgdb_prefix) = VG_CLO_VGDB_PREFIX_DEFAULT; +Bool VG_(clo_vgdb_shadow_registers) = False; Bool VG_(clo_db_attach) = False; Char* VG_(clo_db_command) = GDB_PATH " -nw %f %p"; Int VG_(clo_gen_suppressions) = 0; diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 0a3d82f9..6d069472 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -67,6 +67,7 @@ #include "pub_core_clreq.h" // for VG_USERREQ__* #include "pub_core_dispatch.h" #include "pub_core_errormgr.h" // For VG_(get_n_errs_found)() +#include "pub_core_gdbserver.h" // for VG_(gdbserver) and VG_(gdbserver_activity) #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcprint.h" @@ -113,6 +114,13 @@ UInt VG_(dispatch_ctr); /* 64-bit counter for the number of basic blocks done. */ static ULong bbs_done = 0; +/* Counter to see if vgdb activity is to be verified. + When nr of bbs done reaches vgdb_next_poll, scheduler will + poll for gdbserver activity. VG_(force_vgdb_poll) and + VG_(disable_vgdb_poll) allows the valgrind core (e.g. m_gdbserver) + to control when the next poll will be done. */ +static ULong vgdb_next_poll; + /* Forwards */ static void do_client_request ( ThreadId tid ); static void scheduler_sanity ( ThreadId tid ); @@ -684,6 +692,20 @@ static void do_pre_run_checks ( ThreadState* tst ) # endif } +// NO_VGDB_POLL value ensures vgdb is not polled, while +// VGDB_POLL_ASAP ensures that the next scheduler call +// will cause a poll. +#define NO_VGDB_POLL 0xffffffffffffffffULL +#define VGDB_POLL_ASAP 0x0ULL + +void VG_(disable_vgdb_poll) (void ) +{ + vgdb_next_poll = NO_VGDB_POLL; +} +void VG_(force_vgdb_poll) ( void ) +{ + vgdb_next_poll = VGDB_POLL_ASAP; +} /* Run the thread tid for a while, and return a VG_TRC_* value indicating why VG_(run_innerloop) stopped. */ @@ -769,6 +791,16 @@ static UInt run_thread_for_a_while ( ThreadId tid ) // Tell the tool this thread has stopped running client code VG_TRACK( stop_client_code, tid, bbs_done ); + if (bbs_done >= vgdb_next_poll) { + if (VG_(clo_vgdb_poll)) + vgdb_next_poll = bbs_done + (ULong)VG_(clo_vgdb_poll); + else + /* value was changed due to gdbserver invocation via ptrace */ + vgdb_next_poll = NO_VGDB_POLL; + if (VG_(gdbserver_activity) (tid)) + VG_(gdbserver) (tid); + } + return trc; } @@ -854,6 +886,11 @@ static UInt run_noredir_translation ( Addr hcode, ThreadId tid ) return retval; } +ULong VG_(bbs_done) (void) +{ + return bbs_done; +} + /* --------------------------------------------------------------------- The scheduler proper. @@ -959,10 +996,69 @@ VgSchedReturnCode VG_(scheduler) ( ThreadId tid ) { UInt trc; ThreadState *tst = VG_(get_ThreadState)(tid); + static Bool vgdb_startup_action_done = False; if (VG_(clo_trace_sched)) print_sched_event(tid, "entering VG_(scheduler)"); + /* Do vgdb initialization (but once). Only the first (main) task + starting up will do the below. + Initialize gdbserver earlier than at the first + thread VG_(scheduler) is causing problems: + * at the end of VG_(scheduler_init_phase2) : + The main thread is in VgTs_Init state, but in a not yet + consistent state => the thread cannot be reported to gdb + (e.g. causes an assert in LibVEX_GuestX86_get_eflags when giving + back the guest registers to gdb). + * at end of valgrind_main, just + before VG_(main_thread_wrapper_NORETURN)(1) : + The main thread is still in VgTs_Init state but in a + more advanced state. However, the thread state is not yet + completely initialized : a.o., the os_state is not yet fully + set => the thread is then not properly reported to gdb, + which is then confused (causing e.g. a duplicate thread be + shown, without thread id). + * it would be possible to initialize gdbserver "lower" in the + call stack (e.g. in VG_(main_thread_wrapper_NORETURN)) but + these are platform dependent and the place at which + the thread state is completely initialized is not + specific anymore to the main thread (so a similar "do it only + once" would be needed). + + => a "once only" initialization here is the best compromise. */ + if (!vgdb_startup_action_done) { + vg_assert(tid == 1); // it must be the main thread. + vgdb_startup_action_done = True; + if (VG_(clo_vgdb) != Vg_VgdbNo) { + /* If we have to poll, ensures we do an initial poll at first + scheduler call. Otherwise, ensure no poll (unless interrupted + by ptrace). */ + if (VG_(clo_vgdb_poll)) + VG_(force_vgdb_poll) (); + else + VG_(disable_vgdb_poll) (); + + vg_assert (VG_(dyn_vgdb_error) == VG_(clo_vgdb_error)); + /* As we are initializing, VG_(dyn_vgdb_error) can't have been + changed yet. */ + + if (VG_(dyn_vgdb_error) == 0) { + /* The below call allows gdb to attach at startup + before the first guest instruction is executed. */ + VG_(umsg)("(action at startup) vgdb me ... \n"); + VG_(gdbserver)(1); + } else { + /* User has activated gdbserver => initialize now the FIFOs + to let vgdb/gdb contact us either via the scheduler poll + mechanism or via vgdb ptrace-ing valgrind. */ + if (VG_(gdbserver_activity) (1)) + VG_(gdbserver) (1); + } + } else { + VG_(disable_vgdb_poll) (); + } + } + /* set the proper running signal mask */ block_signals(); diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c index 105c02a3..8f4bbda4 100644 --- a/coregrind/m_signals.c +++ b/coregrind/m_signals.c @@ -191,6 +191,11 @@ VG_(sigtimedwait_zero). This is trivial on Linux, since it's just a syscall. But on Darwin and AIX, we have to cobble together the functionality in a tedious, longwinded and probably error-prone way. + + Finally, if a gdb is debugging the process under valgrind, + the signal can be ignored if gdb tells this. So, before resuming the + scheduler/delivering the signal, a call to VG_(gdbserver_report_signal) + is done. If this returns True, the signal is delivered. */ #include "pub_core_basics.h" @@ -204,6 +209,7 @@ #include "pub_core_aspacemgr.h" #include "pub_core_debugger.h" // For VG_(start_debugger) #include "pub_core_errormgr.h" +#include "pub_core_gdbserver.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcprint.h" @@ -656,11 +662,14 @@ typedef static SKSS skss; -static Bool is_sig_ign(Int sigNo) +/* returns True if signal is to be ignored. + To check this, possibly call gdbserver with tid. */ +static Bool is_sig_ign(Int sigNo, ThreadId tid) { vg_assert(sigNo >= 1 && sigNo <= _VKI_NSIG); - return scss.scss_per_sig[sigNo].scss_handler == VKI_SIG_IGN; + return scss.scss_per_sig[sigNo].scss_handler == VKI_SIG_IGN + || !VG_(gdbserver_report_signal) (sigNo, tid); } /* --------------------------------------------------------------------- @@ -1795,6 +1804,9 @@ static void synth_fault_common(ThreadId tid, Addr addr, Int si_code) info.si_code = si_code; info.VKI_SIGINFO_si_addr = (void*)addr; + /* even if gdbserver indicates to ignore the signal, we will deliver it */ + VG_(gdbserver_report_signal) (VKI_SIGSEGV, tid); + /* If they're trying to block the signal, force it to be delivered */ if (VG_(sigismember)(&VG_(threads)[tid].sig_mask, VKI_SIGSEGV)) VG_(set_default_handler)(VKI_SIGSEGV); @@ -1833,8 +1845,12 @@ void VG_(synth_sigill)(ThreadId tid, Addr addr) info.si_code = VKI_ILL_ILLOPC; /* jrs: no idea what this should be */ info.VKI_SIGINFO_si_addr = (void*)addr; - resume_scheduler(tid); - deliver_signal(tid, &info, NULL); + if (VG_(gdbserver_report_signal) (VKI_SIGILL, tid)) { + resume_scheduler(tid); + deliver_signal(tid, &info, NULL); + } + else + resume_scheduler(tid); } // Synthesise a SIGBUS. @@ -1854,8 +1870,12 @@ void VG_(synth_sigbus)(ThreadId tid) in .si_addr. Oh well. */ /* info.VKI_SIGINFO_si_addr = (void*)addr; */ - resume_scheduler(tid); - deliver_signal(tid, &info, NULL); + if (VG_(gdbserver_report_signal) (VKI_SIGBUS, tid)) { + resume_scheduler(tid); + deliver_signal(tid, &info, NULL); + } + else + resume_scheduler(tid); } // Synthesise a SIGTRAP. @@ -1890,8 +1910,12 @@ void VG_(synth_sigtrap)(ThreadId tid) # endif /* fixs390: do we need to do anything here for s390 ? */ - resume_scheduler(tid); - deliver_signal(tid, &info, &uc); + if (VG_(gdbserver_report_signal) (VKI_SIGTRAP, tid)) { + resume_scheduler(tid); + deliver_signal(tid, &info, &uc); + } + else + resume_scheduler(tid); } /* Make a signal pending for a thread, for later delivery. @@ -2070,7 +2094,7 @@ void async_signalhandler ( Int sigNo, /* (2) */ /* Set up the thread's state to deliver a signal */ - if (!is_sig_ign(info->si_signo)) + if (!is_sig_ign(info->si_signo, tid)) deliver_signal(tid, info, uc); /* It's crucial that (1) and (2) happen in the order (1) then (2) @@ -2342,10 +2366,15 @@ void sync_signalhandler_from_kernel ( ThreadId tid, } if (VG_(in_generated_code)) { - /* Can't continue; must longjmp back to the scheduler and thus - enter the sighandler immediately. */ - deliver_signal(tid, info, uc); - resume_scheduler(tid); + if (VG_(gdbserver_report_signal) (sigNo, tid) + || VG_(sigismember)(&tst->sig_mask, sigNo)) { + /* Can't continue; must longjmp back to the scheduler and thus + enter the sighandler immediately. */ + deliver_signal(tid, info, uc); + resume_scheduler(tid); + } + else + resume_scheduler(tid); } /* If resume_scheduler returns or its our fault, it means we @@ -2545,7 +2574,7 @@ void VG_(poll_signals)(ThreadId tid) /* OK, something to do; deliver it */ if (VG_(clo_trace_signals)) VG_(dmsg)("Polling found signal %d for tid %d\n", sip->si_signo, tid); - if (!is_sig_ign(sip->si_signo)) + if (!is_sig_ign(sip->si_signo, tid)) deliver_signal(tid, sip, NULL); else if (VG_(clo_trace_signals)) VG_(dmsg)(" signal %d ignored\n", sip->si_signo); diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c index 5fa2dd1b..17d1b699 100644 --- a/coregrind/m_translate.c +++ b/coregrind/m_translate.c @@ -59,6 +59,7 @@ #include "pub_core_execontext.h" // VG_(make_depth_1_ExeContext_from_Addr) +#include "pub_core_gdbserver.h" // VG_(tool_instrument_then_gdbserver_if_needed) /*------------------------------------------------------------*/ /*--- Stats ---*/ @@ -210,6 +211,30 @@ static IRExpr* mk_ecu_Expr ( Addr64 guest_IP ) return mkIRExpr_HWord( (HWord)ecu ); } +/* When gdbserver is activated, the translation of a block must + first be done by the tool function, then followed by a pass + which (if needed) instruments the code for gdbserver. +*/ +static +IRSB* tool_instrument_then_gdbserver_if_needed ( VgCallbackClosure* closureV, + IRSB* sb_in, + VexGuestLayout* layout, + VexGuestExtents* vge, + IRType gWordTy, + IRType hWordTy ) +{ + return VG_(instrument_for_gdbserver_if_needed) + (VG_(tdict).tool_instrument (closureV, + sb_in, + layout, + vge, + gWordTy, + hWordTy), + layout, + vge, + gWordTy, + hWordTy); +} /* For tools that want to know about SP changes, this pass adds in the appropriate hooks. We have to do it after the tool's @@ -1350,11 +1375,19 @@ Bool VG_(translate) ( ThreadId tid, if (seg != NULL) { /* There's some kind of segment at the requested place, but we aren't allowed to execute code here. */ - VG_(synth_fault_perms)(tid, addr); + if (debugging_translation) + VG_(printf)("translations not allowed here (segment not executable)" + "(0x%llx)\n", addr); + else + VG_(synth_fault_perms)(tid, addr); } else { /* There is no segment at all; we are attempting to execute in the middle of nowhere. */ - VG_(synth_fault_mapping)(tid, addr); + if (debugging_translation) + VG_(printf)("translations not allowed here (no segment)" + "(0x%llx)\n", addr); + else + VG_(synth_fault_mapping)(tid, addr); } return False; } @@ -1460,7 +1493,9 @@ Bool VG_(translate) ( ThreadId tid, IRSB*(*f)(VgCallbackClosure*, IRSB*,VexGuestLayout*,VexGuestExtents*, IRType,IRType) - = VG_(tdict).tool_instrument; + = VG_(clo_vgdb) != Vg_VgdbNo + ? tool_instrument_then_gdbserver_if_needed + : VG_(tdict).tool_instrument; IRSB*(*g)(void*, IRSB*,VexGuestLayout*,VexGuestExtents*, IRType,IRType) diff --git a/coregrind/pub_core_aspacemgr.h b/coregrind/pub_core_aspacemgr.h index 484303e8..c93b64a2 100644 --- a/coregrind/pub_core_aspacemgr.h +++ b/coregrind/pub_core_aspacemgr.h @@ -283,12 +283,18 @@ extern SysRes VG_(am_mmap_anon_float_valgrind)( SizeT cszB ); extern SysRes VG_(am_sbrk_anon_float_valgrind)( SizeT cszB ); -/* Map a file at an unconstrained address for V, and update the +/* Map privately a file at an unconstrained address for V, and update the segment array accordingly. This is used by V for transiently mapping in object files to read their debug info. */ extern SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, Int fd, Off64T offset ); +/* Map shared a file at an unconstrained address for V, and update the + segment array accordingly. This is used by V for communicating + with vgdb. */ +extern SysRes VG_(am_shared_mmap_file_float_valgrind) + ( SizeT length, UInt prot, Int fd, Off64T offset ); + /* Unmap the given address range and update the segment array accordingly. This fails if the range isn't valid for the client. If *need_discard is True after a successful return, the caller diff --git a/coregrind/pub_core_errormgr.h b/coregrind/pub_core_errormgr.h index 2b72b038..c3462e03 100644 --- a/coregrind/pub_core_errormgr.h +++ b/coregrind/pub_core_errormgr.h @@ -51,7 +51,14 @@ typedef extern void VG_(load_suppressions) ( void ); -extern void VG_(show_all_errors) ( void ); +// if verbosity == 0, print nothing. +// else if xml print suppressions used (in xml format) +// else if verbosity == 1 print Error summary +// else print all errors and suppressions used. +extern void VG_(show_all_errors) ( Int verbosity, Bool xml ); + +/* Print (in readable format) the last error that occured. */ +extern void VG_(show_last_error) ( void ); extern void VG_(show_error_counts_as_XML) ( void ); diff --git a/coregrind/pub_core_gdbserver.h b/coregrind/pub_core_gdbserver.h new file mode 100644 index 00000000..27ed10e5 --- /dev/null +++ b/coregrind/pub_core_gdbserver.h @@ -0,0 +1,186 @@ + +/*--------------------------------------------------------------------*/ +/*--- Handle remote gdb protocol. pub_core_gdbserver.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2011 Philippe Waroquiers + + 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. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PUB_CORE_GDBSERVER_H +#define __PUB_CORE_GDBSERVER_H + +#include "pub_tool_gdbserver.h" + +// True if there is some activity from vgdb +// If it returns True, then extern void VG_(gdbserver) can be called +// to handle this incoming vgdb request. +extern Bool VG_(gdbserver_activity) (ThreadId tid); + + +/* Called by low level to insert or remove a break or watch point. + Break or watch point implementation is done using help from the tool. + break point support implies some (small) specific instrumentation + taken in charge for all tools by m_translate.c. + + Write/read/access watchpoint can only be provided by tools which are + tracking addressability and/or accessibility of memory + (so typically memcheck can provide it). Note that memcheck addressability + bits do not differentiate between read and write accessibility. + However, when accessing unaddressable byte, memcheck can differentiate + reads from write, thereby providing read/write or access watchpoints. + + Note that gdbserver assumes that software breakpoint is supported + (as this will be done by re-instrumenting the code). + Note that len is ignored for sofware breakpoints. hardware_breakpoint + are not supported. + + Returns True if the point has properly been inserted or removed + Returns False otherwise. */ +Bool VG_(gdbserver_point) (PointKind kind, Bool insert, + Addr addr, int len); + +/* Entry point invoked by vgdb when it uses ptrace to cause a gdbserver + invocation. A magic value is passed by vgdb in check as a verification + that the call has been properly pushed by vgdb. */ +extern void VG_(invoke_gdbserver) ( int check ); + +// To be called before delivering a signal. +// Returns True if gdb user asks to pass the signal to the client. +// Note that if the below returns True, the signal might +// still be ignored if this is the action desired by the +// guest program. +extern Bool VG_(gdbserver_report_signal) (Int signo, ThreadId tid); + +/* software_breakpoint, single step and jump support ------------------------*/ +/* VG_(instrument_for_gdbserver_if_needed) allows to do "standard and easy" + instrumentation for gdbserver. + VG_(instrument_for_gdbserver_if_needed) does the following: + * checks if gdbserver instrumentation is needed for vge. + * if no gdbserver instrumentation needed, + returns sb_in + * otherwise + It will instrument sb_in to allow gdbserver to properly + handle breakpoints and single_stepping in sb_in. + All the target jumps of sb_in will also be invalidated + if these are not yet instrumented for gdbserver. + This allows to have single_step working, using a lazily + translation of the blocks which are being single stepped + in. + + The typical usage of this function is to call it on the block + instrumented by the tool instrument function i.e. : + return VG_(instrument_for_gdbserver_if_needed) (sb_out, + layout, + vge, + gWordTy, + hWordTy); + where sb_out is the block instrumented by the tool. + + If the block contains a call to a dirty helper that indirectly + calls gdbserver, then this dirty helper can (indirectly) change + the IP. This implies to jump to this IP after the call to + gdbserver. */ +extern IRSB* VG_(instrument_for_gdbserver_if_needed) + (IRSB* sb_in, /* block to be instrumented */ + VexGuestLayout* layout, + VexGuestExtents* vge, + IRType gWordTy, IRType hWordTy); + +/* reason for which gdbserver connection must be finished */ +typedef + enum { + orderly_finish, + reset_after_error, + reset_after_fork} FinishReason; + +/* output various gdbserver statistics and status. */ +extern void VG_(gdbserver_status_output)(void); + +/* Shared structure between vgdb and the process running + under valgrind. + We define two variants: a 32 bit and a 64 bit. + The valgrind process will use the appropriate size, + according to the architecture. + vgdb will use what the valgrind process is using. */ +/* The below takes care that sizes will be 32 or 64 bits, + whatever the architecture. A.o., vgdb.c cannot use directly + the types from pub_core_threadstate.h as we want vgdb.c to + be independent of the arch it is debugging in case of bi-arch + Valgrind (e.g. x86 and amd64). So, the valgrind process must + give all the needed info/offset to vgdb in the below structure. */ + +typedef + struct { + // PID of the vgdb that last connected to the Valgrind gdbserver. + // It will be set by vgdb after connecting. + int vgdb_pid; + + // nr of bytes vgdb has written to valgrind + volatile int written_by_vgdb; + // nr of bytes seen by valgrind + volatile int seen_by_valgrind; + + // address at which gdbserver can be invoked + Addr32 invoke_gdbserver; + + // address of VG_(threads) and various sizes + // and offset needed by vgdb. + Addr32 threads; + int sizeof_ThreadState; + int offset_status; + int offset_lwpid; + } VgdbShared32; + +/* Same as VgdbShared32 but for 64 bits arch. */ +typedef + struct { + int vgdb_pid; + + volatile int written_by_vgdb; + volatile int seen_by_valgrind; + + Addr64 invoke_gdbserver; + + Addr64 threads; + int sizeof_ThreadState; + int offset_status; + int offset_lwpid; + } VgdbShared64; + +// The below typedef makes the life of valgrind easier. +// vgdb must however work explicitely with the specific 32 or 64 bits version. + +#if VEX_HOST_WORDSIZE == 8 +typedef VgdbShared64 VgdbShared; +#elif VEX_HOST_WORDSIZE == 4 +typedef VgdbShared32 VgdbShared; +#else +# error "unexpected wordsize" +#endif + + +#endif // __PUB_CORE_GDBSERVER_H +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h index ecfbd9bd..bb7cb6c4 100644 --- a/coregrind/pub_core_options.h +++ b/coregrind/pub_core_options.h @@ -54,6 +54,27 @@ extern Bool VG_(clo_error_limit); default: 0 (no, return the application's exit code in the normal way. */ extern Int VG_(clo_error_exitcode); + +typedef + enum { + Vg_VgdbNo, // Do not activate gdbserver. + Vg_VgdbYes, // Activate gdbserver (default). + Vg_VgdbFull, // ACtivate gdbserver in full mode, allowing + // a precise handling of watchpoints and single stepping + // at any moment. + } + VgVgdb; +/* if != Vg_VgdbNo, allows valgrind to serve vgdb/gdb. */ +extern VgVgdb VG_(clo_vgdb); +/* if > 0, checks every VG_(clo_vgdb_poll) BBS if vgdb wants to be served. */ +extern Int VG_(clo_vgdb_poll); +/* prefix for the named pipes (FIFOs) used by vgdb/gdb to communicate with valgrind */ +extern Char* VG_(clo_vgdb_prefix); +/* if True, gdbserver in valgrind will expose a target description containing + shadow registers */ +extern Bool VG_(clo_vgdb_shadow_registers); +#define VG_CLO_VGDB_PREFIX_DEFAULT "/tmp/vgdb-pipe" + /* Enquire about whether to attach to a debugger at errors? default: NO */ extern Bool VG_(clo_db_attach); /* The debugger command? default: whatever gdb ./configure found */ diff --git a/coregrind/pub_core_scheduler.h b/coregrind/pub_core_scheduler.h index 73773d79..0458aaa6 100644 --- a/coregrind/pub_core_scheduler.h +++ b/coregrind/pub_core_scheduler.h @@ -95,6 +95,14 @@ extern void VG_(scheduler_init_phase2) ( ThreadId main_tid, Addr clstack_end, SizeT clstack_size ); +// Allows to disable the polling done to detect vgdb input +// or to force a poll at next scheduler call. +extern void VG_(disable_vgdb_poll) (void ); +extern void VG_(force_vgdb_poll) ( void ); + +/* nr of bbs done since startup. */ +extern ULong VG_(bbs_done) (void); + /* Stats ... */ extern void VG_(print_scheduler_stats) ( void ); diff --git a/coregrind/vgdb.c b/coregrind/vgdb.c new file mode 100644 index 00000000..fffc7cc8 --- /dev/null +++ b/coregrind/vgdb.c @@ -0,0 +1,2141 @@ +/*--------------------------------------------------------------------*/ +/*--- Relay between gdb and gdbserver embedded in valgrind vgdb.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2011 Philippe Waroquiers + + 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. + + The GNU General Public License is contained in the file COPYING. +*/ +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_libcsetjmp.h" +#include "pub_core_threadstate.h" +#include "pub_core_gdbserver.h" + +#include <unistd.h> +#include <string.h> +#include <poll.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> +#include <signal.h> +#include <sys/mman.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include "assert.h" +#include <sys/user.h> + +# if defined(VGO_linux) +#include <sys/prctl.h> +# endif + +/* vgdb has two usages: + 1. relay application between gdb and the gdbserver embedded in valgrind. + 2. standalone to send monitor commands to a running valgrind-ified process + + It is made of a main program which reads arguments. If no + arguments are given or only --pid and --vgdb-prefix, then usage 1 is + assumed. + + As relay application, vgdb reads bytes from gdb on stdin and + writes these bytes to valgrind. Bytes read from valgrind are + written to gdb on stdout. Read/Write from/to valgrind is done + using FIFOs. There is one thread reading from stdin, writing to + valgrind on a FIFO. There is one thread reading from valgrind on a + FIFO, writing to gdb on stdout + + As a standalone utility, vgdb builds command packets to write to valgrind, + sends it and reads the reply. The same two threads are used to write/read. + Once all the commands are sent and their replies received, vgdb will exit. + +*/ + +/* define PTRACEINVOKER to compile the ptrace related code + which ensures a valgrind process blocked in a system call + can be "waken up". PTRACEINVOKER implies some architecture + specific code and/or some OS specific code. */ +#if defined(VGA_arm) || defined(VGA_x86) || defined(VGA_amd64) \ + || defined(VGA_ppc32) || defined(VGA_ppc64) || defined(VGA_s390x) +#define PTRACEINVOKER +#else +I_die_here : (PTRACEINVOKER) architecture missing in vgdb.c +#endif + +/* Some darwin specific stuff is needed as ptrace is not + fully supported on MacOS. Till we find someone courageous + having access to Darwin, there is no PTRACEINVOKER. */ +#if defined(VGO_darwin) +#undef PTRACEINVOKER +#endif + +static int debuglevel; +static struct timeval dbgtv; +/* if level <= debuglevel, print timestamp, then print provided by debug info */ +#define DEBUG(level, ...) (level <= debuglevel ? \ + gettimeofday(&dbgtv, NULL), \ + fprintf(stderr, "%ld.%6.6ld ", \ + (long int)dbgtv.tv_sec, \ + (long int)dbgtv.tv_usec), \ + fprintf(stderr, __VA_ARGS__),fflush(stderr) \ + : 0) + +/* same as DEBUG but does not print time stamp info */ +#define PDEBUG(level, ...) (level <= debuglevel ? \ + fprintf(stderr, __VA_ARGS__),fflush(stderr) \ + : 0) + +/* if errno != 0, + report the errno and fprintf the ... varargs on stderr. */ +#define ERROR(errno, ...) ((errno == 0 ? 0 : perror("syscall failed")), \ + fprintf(stderr, __VA_ARGS__), \ + fflush(stderr)) +/* same as ERROR, but also exits with status 1 */ +#define XERROR(errno, ...) ((errno == 0 ? 0 : perror("syscall failed")), \ + fprintf(stderr, __VA_ARGS__), \ + fflush(stderr), \ + exit(1)) + +static char *vgdb_prefix = "/tmp/vgdb-pipe"; + +/* Will be set to True when any condition indicating we have to shutdown + is encountered. */ +static Bool shutting_down = False; + +static VgdbShared32 *shared32; +static VgdbShared64 *shared64; +#define VS_written_by_vgdb (shared32 != NULL ? \ + shared32->written_by_vgdb \ + : shared64->written_by_vgdb) +#define VS_seen_by_valgrind (shared32 != NULL ? \ + shared32->seen_by_valgrind \ + : shared64->seen_by_valgrind) + +#define VS_vgdb_pid (shared32 != NULL ? shared32->vgdb_pid : shared64->vgdb_pid) + +/* Calls malloc (size). Exits if memory can't be allocated. */ +static +void *vmalloc(size_t size) +{ + void * mem = malloc(size); + if (mem == NULL) + XERROR (errno, "can't allocate memory\n"); + return mem; +} + +/* Calls realloc (size). Exits if memory can't be allocated. */ +static +void *vrealloc(void *ptr,size_t size) +{ + void * mem = realloc(ptr, size); + if (mem == NULL) + XERROR (errno, "can't reallocate memory\n"); + return mem; +} + +/* add nrw to the written_by_vgdb field of shared32 or shared64 */ +static +void add_written(int nrw) +{ + if (shared32 != NULL) + shared32->written_by_vgdb += nrw; + else if (shared64 != NULL) + shared64->written_by_vgdb += nrw; + else + assert(0); +} + +static int shared_mem_fd = -1; +static +void map_vgdbshared (char* shared_mem) +{ + struct stat fdstat; + void **s; + shared_mem_fd = open(shared_mem, O_RDWR); + /* shared_mem_fd will not be closed till vgdb exits. */ + + if (shared_mem_fd == -1) + XERROR (errno, "error opening %s shared memory file\n", shared_mem); + + if (fstat(shared_mem_fd, &fdstat) != 0) + XERROR (errno, "fstat"); + + if (fdstat.st_size == sizeof(VgdbShared64)) + s = (void*) &shared64; + else if (fdstat.st_size == sizeof(VgdbShared32)) + s = (void*) &shared32; + else +#if VEX_HOST_WORDSIZE == 8 + XERROR (0, + "error size shared memory file %s.\n" + "expecting size %d (64bits) or %d (32bits) got %ld.\n", + shared_mem, + (int) sizeof(VgdbShared64), (int) sizeof(VgdbShared32), + (long int)fdstat.st_size); +#elif VEX_HOST_WORDSIZE == 4 + XERROR (0, + "error size shared memory file %s.\n" + "expecting size %d (32bits) got %ld.\n", + shared_mem, + (int) sizeof(VgdbShared32), + fdstat.st_size); +#else +# error "unexpected wordsize" +#endif + +#if VEX_HOST_WORDSIZE == 4 + if (shared64 != NULL) + XERROR (0, "cannot use 32 bits vgdb with a 64bits valgrind process\n"); + /* But we can use a 64 bits vgdb with a 32 bits valgrind */ +#endif + + *s = (void*) mmap (NULL, fdstat.st_size, + PROT_READ|PROT_WRITE, MAP_SHARED, + shared_mem_fd, 0); + + if (*s == (void *) -1) + XERROR (errno, "error mmap shared memory file %s\n", shared_mem); + +} + +#if VEX_HOST_WORDSIZE == 8 +typedef Addr64 CORE_ADDR; +typedef Addr64 PTRACE_XFER_TYPE; +typedef void* PTRACE_ARG3_TYPE; +#elif VEX_HOST_WORDSIZE == 4 +typedef Addr32 CORE_ADDR; +typedef Addr32 PTRACE_XFER_TYPE; +typedef void* PTRACE_ARG3_TYPE; +#else +# error "unexpected wordsize" +#endif + +static Bool pid_of_save_regs_continued = False; +// True if we have continued pid_of_save_regs after PTRACE_ATTACH + +static Bool dying = False; +// Set to True when loss of connection indicating that the Valgrind +// process is dying. + +/* To be called when connection with valgrind is lost. In case we +have lost the connection, it means that Valgrind has closed the +connection and is busy exiting. We can't and don't have to stop it in +this case. */ +static +void valgrind_dying(void) +{ + pid_of_save_regs_continued = False; + dying = True; +} + + +#ifdef PTRACEINVOKER +/* ptrace_(read|write)_memory are modified extracts of linux-low.c + from gdb 6.6. Copyrighted FSF */ +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +static +int ptrace_read_memory (pid_t inferior_pid, CORE_ADDR memaddr, + unsigned char *myaddr, int len) +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) + / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register PTRACE_XFER_TYPE *buffer + = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { + errno = 0; + buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, + (PTRACE_ARG3_TYPE) addr, 0); + if (errno) + return errno; + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, + (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), len); + + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +static +int ptrace_write_memory (pid_t inferior_pid, CORE_ADDR memaddr, + const unsigned char *myaddr, int len) +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) + / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register PTRACE_XFER_TYPE *buffer + = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + + if (debuglevel >= 1) { + DEBUG (1, "Writing "); + for (i = 0; i < len; i++) + PDEBUG (1, "%02x", (unsigned)myaddr[i]); + PDEBUG(1, " to %p\n", (void *) memaddr); + } + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid, + (PTRACE_ARG3_TYPE) addr, 0); + + if (count > 1) { + buffer[count - 1] + = ptrace (PTRACE_PEEKTEXT, inferior_pid, + (PTRACE_ARG3_TYPE) (addr + (count - 1) + * sizeof (PTRACE_XFER_TYPE)), + 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), + myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { + errno = 0; + ptrace (PTRACE_POKETEXT, inferior_pid, + (PTRACE_ARG3_TYPE) addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +/* subset of VG_(threads) needed for vgdb ptrace. + This is initialized when process is attached. */ +typedef struct { + ThreadStatus status; + Int lwpid; +} +VgdbThreadState; +static VgdbThreadState vgdb_threads[VG_N_THREADS]; + +static const +HChar* name_of_ThreadStatus ( ThreadStatus status ) +{ + switch (status) { + case VgTs_Empty: return "VgTs_Empty"; + case VgTs_Init: return "VgTs_Init"; + case VgTs_Runnable: return "VgTs_Runnable"; + case VgTs_WaitSys: return "VgTs_WaitSys"; + case VgTs_Yielding: return "VgTs_Yielding"; + case VgTs_Zombie: return "VgTs_Zombie"; + default: return "VgTs_???"; + } +} + +static +char *status_image (int status) +{ + static char result[256]; + int sz = 0; +#define APPEND(...) sz += snprintf (result+sz, 256 - sz - 1, __VA_ARGS__) + + result[0] = 0; + + if (WIFEXITED(status)) + APPEND ("WIFEXITED %d ", WEXITSTATUS(status)); + + if (WIFSIGNALED(status)) { + APPEND ("WIFSIGNALED %d ", WTERMSIG(status)); + if (WCOREDUMP(status)) APPEND ("WCOREDUMP "); + } + + if (WIFSTOPPED(status)) + APPEND ("WIFSTOPPED %d ", WSTOPSIG(status)); + + if (WIFCONTINUED(status)) + APPEND ("WIFCONTINUED "); + + return result; +#undef APPEND +} + +/* Wait till the process pid is reported as stopped with signal_expected. + If other signal(s) than signal_expected are received, waitstopped + will pass them to pid, waiting for signal_expected to stop pid. + Returns True when process is in stopped state with signal_expected. + Returns False if a problem was encountered while waiting for pid + to be stopped. + + If pid is reported as being dead/exited, waitstopped will return False. +*/ +static +Bool waitstopped (int pid, int signal_expected, char *msg) +{ + pid_t p; + int status = 0; + int signal_received; + int res; + + while (1) { + DEBUG(1, "waitstopped %s before waitpid signal_expected %d\n", + msg, signal_expected); + p = waitpid(pid, &status, __WALL); + DEBUG(1, "after waitpid pid %d p %d status 0x%x %s\n", pid, p, + status, status_image (status)); + if (p != pid) { + ERROR(errno, "%s waitpid pid %d in waitstopped %d status 0x%x %s\n", + msg, pid, p, status, status_image (status)); + return False; + } + + if (WIFEXITED(status)) { + shutting_down = True; + return False; + } + + assert (WIFSTOPPED(status)); + signal_received = WSTOPSIG(status); + if (signal_received == signal_expected) + break; + + /* pid received a signal which is not the signal we are waiting for. + We continue pid, transmitting this signal. */ + DEBUG(1, "waitstopped PTRACE_CONT with signal %d\n", signal_received); + res = ptrace (PTRACE_CONT, pid, NULL, signal_received); + if (res != 0) { + ERROR(errno, "waitstopped PTRACE_CONT\n"); + return False; + } + } + + return True; +} + +/* Stops the given pid, wait for the process to be stopped. + Returns True if succesful, False otherwise. + msg is used in tracing and error reporting. */ +static +Bool stop (int pid, char *msg) +{ + long res; + + DEBUG(1, "%s SIGSTOP pid %d\n", msg, pid); + res = kill (pid, SIGSTOP); + if (res != 0) { + ERROR(errno, "%s SIGSTOP pid %d %ld\n", msg, pid, res); + return False; + } + + return waitstopped (pid, SIGSTOP, msg); + +} + +/* Attaches to given pid, wait for the process to be stopped. + Returns True if succesful, False otherwise. + msg is used in tracing and error reporting. */ +static +Bool attach (int pid, char *msg) +{ + long res; + + DEBUG(1, "%s PTRACE_ATTACH pid %d\n", msg, pid); + res = ptrace (PTRACE_ATTACH, pid, NULL, NULL); + if (res != 0) { + ERROR(errno, "%s PTRACE_ATTACH pid %d %ld\n", msg, pid, res); + return False; + } + + return waitstopped(pid, SIGSTOP, msg); +} + +/* once we are attached to the pid, get the list of threads and stop + them all. + Returns True if all threads properly suspended, False otherwise. */ +static +Bool acquire_and_suspend_threads(int pid) +{ + int i; + int rw; + Bool pid_found = False; + Addr vgt; + int sz_tst; + int off_status; + int off_lwpid; + int nr_live_threads = 0; + + if (shared32 != NULL) { + vgt = shared32->threads; + sz_tst = shared32->sizeof_ThreadState; + off_status = shared32->offset_status; + off_lwpid = shared32->offset_lwpid; + } + else if (shared64 != NULL) { + vgt = shared64->threads; + sz_tst = shared64->sizeof_ThreadState; + off_status = shared64->offset_status; + off_lwpid = shared64->offset_lwpid; + } else { + assert (0); + } + + /* note: the entry 0 is unused */ + for (i = 1; i < VG_N_THREADS; i++) { + vgt += sz_tst; + rw = ptrace_read_memory(pid, vgt+off_status, + (unsigned char *)&(vgdb_threads[i].status), + sizeof(ThreadStatus)); + if (rw != 0) { + ERROR(rw, "status ptrace_read_memory\n"); + return False; + } + + rw = ptrace_read_memory(pid, vgt+off_lwpid, + (unsigned char *)&(vgdb_threads[i].lwpid), + sizeof(Int)); + if (rw != 0) { + ERROR(rw, "lwpid ptrace_read_memory\n"); + return False; + } + + if (vgdb_threads[i].status != VgTs_Empty) { + DEBUG(1, "found tid %d status %s lwpid %d\n", + i, name_of_ThreadStatus(vgdb_threads[i].status), + vgdb_threads[i].lwpid); + nr_live_threads++; + if (vgdb_threads[i].lwpid <= 1) { + if (vgdb_threads[i].lwpid == 0 + && vgdb_threads[i].status == VgTs_Init) { + DEBUG(1, "not set lwpid tid %d status %s lwpid %d\n", + i, name_of_ThreadStatus(vgdb_threads[i].status), + vgdb_threads[i].lwpid); + } else { + ERROR(1, "unexpected lwpid tid %d status %s lwpid %d\n", + i, name_of_ThreadStatus(vgdb_threads[i].status), + vgdb_threads[i].lwpid); + } + /* in case we have a VtTs_Init thread with lwpid not yet set, + we try again later. */ + return False; + } + if (vgdb_threads[i].lwpid == pid) { + assert (!pid_found); + assert (i == 1); + pid_found = True; + } else { + if (!attach(vgdb_threads[i].lwpid, "attach_thread")) { + ERROR(0, "ERROR attach pid %d tid %d\n", + vgdb_threads[i].lwpid, i); + return False; + } + } + } + } + /* If we found no thread, it means the process is stopping, and + we better do not force anything to happen during that. */ + if (nr_live_threads > 0) + return True; + else + return False; +} + +static +void detach_from_all_threads(int pid) +{ + int i; + long res; + Bool pid_found = False; + + /* detach from all the threads */ + for (i = 1; i < VG_N_THREADS; i++) { + if (vgdb_threads[i].status != VgTs_Empty) { + if (vgdb_threads[i].status == VgTs_Init + && vgdb_threads[i].lwpid == 0) { + DEBUG(1, "skipping PTRACE_DETACH pid %d tid %d status %s\n", + vgdb_threads[i].lwpid, i, + name_of_ThreadStatus (vgdb_threads[i].status)); + } else { + if (vgdb_threads[i].lwpid == pid) { + assert (!pid_found); + pid_found = True; + } + DEBUG(1, "PTRACE_DETACH pid %d tid %d status %s\n", + vgdb_threads[i].lwpid, i, + name_of_ThreadStatus (vgdb_threads[i].status)); + res = ptrace (PTRACE_DETACH, vgdb_threads[i].lwpid, NULL, NULL); + if (res != 0) { + ERROR(errno, "PTRACE_DETACH pid %d tid %d status %s res %ld\n", + vgdb_threads[i].lwpid, i, + name_of_ThreadStatus (vgdb_threads[i].status), + res); + } + } + } + } + + if (!pid_found && pid) { + /* No threads are live. Process is busy stopping. + We need to detach from pid explicitely. */ + DEBUG(1, "no thread live => PTRACE_DETACH pid %d\n", pid); + res = ptrace (PTRACE_DETACH, pid, NULL, NULL); + if (res != 0) + ERROR(errno, "PTRACE_DETACH pid %d res %ld\n", pid, res); + } +} + +// if > 0, pid for which registers have to be restored. +static int pid_of_save_regs = 0; +static struct user user_save; + +/* Get the registers from pid into regs. + Returns True if all ok, otherwise False. */ +static +Bool getregs (int pid, void *regs) +{ +# ifdef VGA_s390x + char *pregs = (char *) regs; + long offset; + errno = 0; + DEBUG(1, "getregs PTRACE_PEEKUSER(s)\n"); + for (offset = 0; offset < PT_ENDREGS; offset = offset + sizeof(long)) { + *(long *)(pregs+offset) = ptrace(PTRACE_PEEKUSER, pid, offset, NULL); + if (errno != 0) { + ERROR(errno, "PTRACE_PEEKUSER offset %ld\n", offset); + return False; + } + } + return True; +# else + // Platforms having GETREGS + long res; + DEBUG(1, "getregs PTRACE_GETREGS\n"); + res = ptrace (PTRACE_GETREGS, pid, NULL, regs); + if (res != 0) { + ERROR(errno, "PTRACE_GETREGS %ld\n", res); + return False; + } + return True; +# endif +} + +/* Set the registers of pid to regs. + Returns True if all ok, otherwise False. */ +static +Bool setregs (int pid, void *regs) +{ +# ifdef VGA_s390x + char *pregs = (char *) regs; + long offset; + long res; + errno = 0; + DEBUG(1, "setregs PTRACE_POKEUSER(s)\n"); + for (offset = 0; offset < PT_ENDREGS; offset = offset + sizeof(long)) { + res = ptrace(PTRACE_POKEUSER, pid, offset, *(long*)(pregs+offset)); + if (errno != 0) { + ERROR(errno, "PTRACE_POKEUSER offset %ld res %ld\n", offset, res); + return False; + } + } + return True; +# else + // Platforms having SETREGS + long res; + DEBUG(1, "setregs PTRACE_SETREGS\n"); + res = ptrace (PTRACE_SETREGS, pid, NULL, regs); + if (res != 0) { + ERROR(errno, "PTRACE_SETREGS %ld\n", res); + return False; + } + return True; +# endif +} + +/* Restore the registers to the saved value, then detaches from all threads */ +static +void restore_and_detach(int pid) +{ + if (pid_of_save_regs) { + /* In case the 'main pid' has been continued, we need to stop it + before resetting the registers. */ + if (pid_of_save_regs_continued) { + pid_of_save_regs_continued = False; + if (!stop(pid_of_save_regs, "sigstop before reset regs")) + DEBUG(0, "Could not sigstop before reset"); + } + + DEBUG(1, "setregs restore registers pid %d\n", pid_of_save_regs); + if (!setregs(pid_of_save_regs, &user_save.regs)) { + ERROR(errno, "setregs restore registers pid %d after cont\n", + pid_of_save_regs); + } + pid_of_save_regs = 0; + } else { + DEBUG(1, "PTRACE_SETREGS restore registers: no pid\n"); + } + detach_from_all_threads(pid); +} + +/* Ensures that the gdbserver code is invoked by pid. + If an error occurs, resets to the valgrind process + to the state it has before being ptrace-d. + Returns True if invoke successful, False otherwise. +*/ +static +Bool invoke_gdbserver (int pid) +{ + long res; + Bool stopped; + struct user user_mod; + Addr sp; + /* A specific int value is passed to invoke_gdbserver, to check + everything goes according to the plan. */ + const int check = 0x8BADF00D; // ate bad food. + + const Addr bad_return = 0; + // A bad return address will be pushed on the stack. + // The function invoke_gdbserver cannot return. If ever it returns, a NULL + // address pushed on the stack should ensure this is detected. + + /* Not yet attached. If problem, vgdb can abort, + no cleanup needed. + + On Ubuntu>= 10.10, a /proc setting can disable ptrace. + So, Valgrind has to SET_PTRACER this vgdb. Once this + is done, this vgdb can ptrace the valgrind process. */ + + DEBUG(1, "attach to 'main' pid %d\n", pid); + if (!attach(pid, "attach main pid")) { + ERROR(0, "error attach main pid %d\n", pid); + return False; + } + + /* Now, we are attached. If problem, detach and return. */ + + if (!acquire_and_suspend_threads(pid)) { + detach_from_all_threads(pid); + /* if the pid does not exist anymore, we better stop */ + if (kill(pid, 0) != 0) + XERROR (errno, "invoke_gdbserver: check for pid %d existence failed\n", + pid); + return False; + } + + if (!getregs(pid, &user_mod.regs)) { + detach_from_all_threads(pid); + return False; + } + user_save = user_mod; + +#if defined(VGA_x86) + sp = user_mod.regs.esp; +#elif defined(VGA_amd64) + sp = user_mod.regs.rsp; + if (shared32 != NULL) { + /* 64bit vgdb speaking with a 32bit executable. + To have system call restart properly, we need to sign extend rax. + For more info: + web search '[patch] Fix syscall restarts for amd64->i386 biarch' + e.g. http://sourceware.org/ml/gdb-patches/2009-11/msg00592.html */ + *(long *)&user_save.regs.rax = *(int*)&user_save.regs.rax; + DEBUG(1, "Sign extending %8.8lx to %8.8lx\n", + user_mod.regs.rax, user_save.regs.rax); + } +#elif defined(VGA_arm) + sp = user_mod.regs.uregs[13]; +#elif defined(VGA_ppc32) + sp = user_mod.regs.gpr[1]; +#elif defined(VGA_ppc64) + sp = user_mod.regs.gpr[1]; +#elif defined(VGA_s390x) + sp = user_mod.regs.gprs[15]; +#else + I_die_here : (sp) architecture missing in vgdb.c +#endif + + + // the magic below is derived from spying what gdb sends to + // the (classical) gdbserver when invoking a C function. + if (shared32 != NULL) { + // vgdb speaking with a 32bit executable. +#if defined(VGA_x86) || defined(VGA_amd64) + const int regsize = 4; + int rw; + /* push check arg on the stack */ + sp = sp - regsize; + DEBUG(1, "push check arg ptrace_write_memory\n"); + assert(regsize == sizeof(check)); + rw = ptrace_write_memory(pid, sp, + (unsigned char *) &check, + regsize); + if (rw != 0) { + ERROR(rw, "push check arg ptrace_write_memory"); + detach_from_all_threads(pid); + return False; + } + + sp = sp - regsize; + DEBUG(1, "push bad_return return address ptrace_write_memory\n"); + // Note that for a 64 bits vgdb, only 4 bytes of NULL bad_return + // are written. + rw = ptrace_write_memory(pid, sp, + (unsigned char *) &bad_return, + regsize); + if (rw != 0) { + ERROR(rw, "push bad_return return address ptrace_write_memory"); + detach_from_all_threads(pid); + return False; + } +#if defined(VGA_x86) + /* set ebp, esp, eip and orig_eax to invoke gdbserver */ + // compiled in 32bits, speaking with a 32bits exe + user_mod.regs.ebp = sp; // bp set to sp + user_mod.regs.esp = sp; + user_mod.regs.eip = shared32->invoke_gdbserver; + user_mod.regs.orig_eax = -1L; +#elif defined(VGA_amd64) + /* set ebp, esp, eip and orig_eax to invoke gdbserver */ + // compiled in 64bits, speaking with a 32bits exe + user_mod.regs.rbp = sp; // bp set to sp + user_mod.regs.rsp = sp; + user_mod.regs.rip = shared32->invoke_gdbserver; + user_mod.regs.orig_rax = -1L; +#else + I_die_here : not x86 or amd64 in x86/amd64 section/ +#endif + +#elif defined(VGA_ppc32) || defined(VGA_ppc64) + user_mod.regs.nip = shared32->invoke_gdbserver; + user_mod.regs.trap = -1L; + /* put check arg in register 3 */ + user_mod.regs.gpr[3] = check; + /* put NULL return address in Link Register */ + user_mod.regs.link = bad_return; + +#elif defined(VGA_arm) + /* put check arg in register 0 */ + user_mod.regs.uregs[0] = check; + /* put NULL return address in Link Register */ + user_mod.regs.uregs[14] = bad_return; + user_mod.regs.uregs[15] = shared32->invoke_gdbserver; + +#elif defined(VGA_s390x) + XERROR(0, "(fn32) s390x has no 32bits implementation"); +#else + I_die_here : architecture missing in vgdb.c +#endif + } + + else if (shared64 != NULL) { +#if defined(VGA_x86) + assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe +#elif defined(VGA_amd64) + // vgdb speaking with a 64 bit executable. + const int regsize = 8; + int rw; + + /* give check arg in rdi */ + user_mod.regs.rdi = check; + + /* push return address on stack : return to breakaddr */ + sp = sp - regsize; + DEBUG(1, "push bad_return return address ptrace_write_memory\n"); + rw = ptrace_write_memory(pid, sp, + (unsigned char *) &bad_return, + sizeof(bad_return)); + if (rw != 0) { + ERROR(rw, "push bad_return return address ptrace_write_memory"); + detach_from_all_threads(pid); + return False; + } + + /* set rbp, rsp, rip and orig_rax to invoke gdbserver */ + user_mod.regs.rbp = sp; // bp set to sp + user_mod.regs.rsp = sp; + user_mod.regs.rip = shared64->invoke_gdbserver; + user_mod.regs.orig_rax = -1L; + +#elif defined(VGA_arm) + assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe +#elif defined(VGA_ppc32) + assert(0); // cannot vgdb a 64 bits executable with a 32 bits exe +#elif defined(VGA_ppc64) + Addr64 func_addr; + Addr64 toc_addr; + int rw; + rw = ptrace_read_memory(pid, shared64->invoke_gdbserver, + (unsigned char *)&func_addr, + sizeof(Addr64)); + if (rw != 0) { + ERROR(rw, "ppc64 read func_addr\n"); + detach_from_all_threads(pid); + return False; + } + rw = ptrace_read_memory(pid, shared64->invoke_gdbserver+8, + (unsigned char *)&toc_addr, + sizeof(Addr64)); + if (rw != 0) { + ERROR(rw, "ppc64 read toc_addr\n"); + detach_from_all_threads(pid); + return False; + } + // We are not pushing anything on the stack, so it is not + // very clear why the sp has to be decreased, but it seems + // needed. The ppc64 ABI might give some lights on this ? + user_mod.regs.gpr[1] = sp - 220; + user_mod.regs.gpr[2] = toc_addr; + user_mod.regs.nip = func_addr; + user_mod.regs.trap = -1L; + /* put check arg in register 3 */ + user_mod.regs.gpr[3] = check; + /* put bad_return return address in Link Register */ + user_mod.regs.link = bad_return; +#elif defined(VGA_s390x) + /* put check arg in register r2 */ + user_mod.regs.gprs[2] = check; + /* bad_return Return address is in r14 */ + user_mod.regs.gprs[14] = bad_return; + /* minimum stack frame */ + sp = sp - 160; + user_mod.regs.gprs[15] = sp; + /* set program counter */ + user_mod.regs.psw.addr = shared64->invoke_gdbserver; +#else + I_die_here: architecture missing in vgdb.c +#endif + } + else { + assert(0); + } + + if (!setregs(pid, &user_mod.regs)) { + detach_from_all_threads(pid); + return False; + } + /* Now that we have modified the registers, we set + pid_of_save_regs to indicate that restore_and_detach + must restore the registers in case of cleanup. */ + pid_of_save_regs = pid; + pid_of_save_regs_continued = False; + + + /* We PTRACE_CONT-inue pid. + Either gdbserver will be invoked directly (if all + threads are interruptible) or gdbserver will be + called soon by the scheduler. In the first case, + pid will stop on the break inserted above when + gdbserver returns. In the 2nd case, the break will + be encountered directly. */ + DEBUG(1, "PTRACE_CONT to invoke\n"); + res = ptrace (PTRACE_CONT, pid, NULL, NULL); + if (res != 0) { + ERROR(errno, "PTRACE_CONT\n"); + restore_and_detach(pid); + return False; + } + pid_of_save_regs_continued = True; + + stopped = waitstopped (pid, SIGTRAP, + "waitpid status after PTRACE_CONT to invoke"); + if (stopped) { + /* Here pid has properly stopped on the break. */ + pid_of_save_regs_continued = False; + restore_and_detach(pid); + return True; + } else { + /* Whatever kind of problem happened. We shutdown */ + shutting_down = True; + return False; + } +} +#endif + +static +void cleanup_restore_and_detach(void *v_pid) +{ + DEBUG(1, "cleanup_restore_and_detach dying: %d\n", dying); +#ifdef PTRACEINVOKER + if (!dying) + restore_and_detach(*(int*)v_pid); +#endif +} + +/* This function loops till shutting_down becomes true. In this loop, + it verifies if valgrind process is reading the characters written + by vgdb. The verification is done every max_invoke_ms ms. If + valgrind is not reading characters, it will use invoke_gdbserver + (if PTRACE_INVOKER is defined) to ensure that the gdbserver code is + called soon by valgrind. */ +static int max_invoke_ms = 100; +static +void *invoke_gdbserver_in_valgrind(void *v_pid) +{ + int pid = *(int *)v_pid; + int written_by_vgdb_before_sleep; + int seen_by_valgrind_before_sleep; + + int invoked_written = -1; + + pthread_cleanup_push(cleanup_restore_and_detach, v_pid); + + while (!shutting_down) { + written_by_vgdb_before_sleep = VS_written_by_vgdb; + seen_by_valgrind_before_sleep = VS_seen_by_valgrind; + DEBUG(3, + "written_by_vgdb_before_sleep %d " + "seen_by_valgrind_before_sleep %d\n", + written_by_vgdb_before_sleep, + seen_by_valgrind_before_sleep); + if (usleep(1000 * max_invoke_ms) != 0) { + if (errno == EINTR) + continue; + XERROR (errno, "error usleep\n"); + } + /* if nothing happened during our sleep, let's try to wake up valgrind */ + if (written_by_vgdb_before_sleep == VS_written_by_vgdb + && seen_by_valgrind_before_sleep == VS_seen_by_valgrind + && VS_written_by_vgdb > VS_seen_by_valgrind) { + DEBUG(2, + "after sleep " + "written_by_vgdb %d " + "seen_by_valgrind %d " + "invoked_written %d\n", + VS_written_by_vgdb, + VS_seen_by_valgrind, + invoked_written); + /* if the pid does not exist anymore, we better stop */ + if (kill(pid, 0) != 0) + XERROR (errno, + "invoke_gdbserver_in_valgrind: " + "check for pid %d existence failed\n", pid); + + #if defined(PTRACEINVOKER) + /* only need to wake up if the nr written has changed since + last invoke. */ + if (invoked_written != written_by_vgdb_before_sleep) { + if (invoke_gdbserver(pid)) { + /* If invoke succesful, no need to invoke again + for the same value of written_by_vgdb_before_sleep. */ + invoked_written = written_by_vgdb_before_sleep; + } + } + #else + DEBUG(2, "invoke_gdbserver via ptrace not (yet) implemented\n"); + #endif + } + } + pthread_cleanup_pop(0); + return NULL; +} + +static +int open_fifo (char* name, int flags, char* desc) +{ + int fd; + DEBUG(1, "opening %s %s\n", name, desc); + fd = open(name, flags); + if (fd == -1) + XERROR (errno, "error opening %s %s\n", name, desc); + + DEBUG(1, "opened %s %s fd %d\n", name, desc, fd); + return fd; +} + +/* acquire a lock on the first byte of the given fd. If not successful, + exits with error. + This allows to avoid having two vgdb speaking with the same Valgrind + gdbserver as this causes serious headaches to the protocol. */ +static +void acquire_lock (int fd, int valgrind_pid) +{ + if (lockf(fd, F_TLOCK, 1) < 0) { + if (errno == EAGAIN || errno == EACCES) { + XERROR(errno, + "Cannot acquire lock.\n" + "Probably vgdb pid %d already speaks with Valgrind pid %d\n", + VS_vgdb_pid, + valgrind_pid); + } else { + XERROR(errno, "cannot acquire lock.\n"); + } + } + + /* Here, we have the lock. It will be released when fd will be closed. */ + /* We indicate our pid to Valgrind gdbserver */ + if (shared32 != NULL) + shared32->vgdb_pid = getpid(); + else if (shared64 != NULL) + shared64->vgdb_pid = getpid(); + else + assert(0); +} + +#define PBUFSIZ 16384 /* keep in sync with server.h */ + +/* read some characters from fd. + Returns the nr of characters read, -1 if error. + desc is a string used in tracing */ +static +int read_buf (int fd, char* buf, char* desc) +{ + int nrread; + DEBUG(2, "reading %s\n", desc); + nrread = read(fd, buf, PBUFSIZ); + if (nrread == -1) { + ERROR (errno, "error reading %s\n", desc); + return -1; + } + buf[nrread] = '\0'; + DEBUG(2, "read %s %s\n", desc, buf); + return nrread; +} + +/* write size bytes from buf to fd. + desc is a description of the action for which the write is done. + If notify, then add size to the shared cntr indicating to the + valgrind process that there is new data. + Returns True if write is ok, False if there was a problem. */ +static +Bool write_buf(int fd, char* buf, int size, char* desc, Bool notify) +{ + int nrwritten; + int nrw; + DEBUG(2, "writing %s len %d %s notify: %d\n", desc, size, buf, notify); + nrwritten = 0; + while (nrwritten < size) { + nrw = write (fd, buf+nrwritten, size - nrwritten); + if (nrw == -1) { + ERROR(errno, "error write %s\n", desc); + return False; + } + nrwritten = nrwritten + nrw; + if (notify) + add_written(nrw); + } + return True; +} + +typedef enum { + FROM_GDB, + TO_GDB, + FROM_PID, + TO_PID } ConnectionKind; +static const int NumConnectionKind = TO_PID+1; +static +char *ppConnectionKind (ConnectionKind con) +{ + switch (con) { + case FROM_GDB: return "FROM_GDB"; + case TO_GDB: return "TO_GDB"; + case FROM_PID: return "FROM_PID"; + case TO_PID: return "TO_PID"; + default: return "invalid connection kind"; + } +} + +static char *shared_mem; + +static const int from_gdb = 0; +static char *from_gdb_to_pid; /* fifo name to write gdb command to pid */ +/* Returns True in case read/write operations were done properly. + Returns False in case of error. + to_pid is the file descriptor to write to the process pid. */ +static +Bool read_from_gdb_write_to_pid(int to_pid) +{ + char buf[PBUFSIZ]; + int nrread; + + nrread = read_buf(from_gdb, buf, "from gdb on stdin"); + if (nrread <= 0) { + if (nrread == 0) + DEBUG(1, "read 0 bytes from gdb => assume exit\n"); + else + DEBUG(1, "error reading bytes from gdb\n"); + close (from_gdb); + shutting_down = True; + return False; + } + return write_buf(to_pid, buf, nrread, "to_pid", /* notify */ True); +} + +static const int to_gdb = 1; +static char *to_gdb_from_pid; /* fifo name to read pid replies */ +/* Returns True in case read/write operations were done properly. + Returns False in case of error. + from_pid is the file descriptor to read data from the process pid. */ +static +Bool read_from_pid_write_to_gdb(int from_pid) +{ + char buf[PBUFSIZ]; + int nrread; + + nrread = read_buf(from_pid, buf, "from pid"); + if (nrread <= 0) { + if (nrread == 0) + DEBUG(1, "read 0 bytes from pid => assume exit\n"); + else + DEBUG(1, "error reading bytes from pid\n"); + close (from_pid); + shutting_down = True; + return False; + } + return write_buf(to_gdb, buf, nrread, "to_gdb", /* notify */ False); +} + +/* prepares the FIFOs filenames, map the shared memory. */ +static +void prepare_fifos_and_shared_mem(int pid) +{ + from_gdb_to_pid = vmalloc (strlen(vgdb_prefix) + 30); + to_gdb_from_pid = vmalloc (strlen(vgdb_prefix) + 30); + shared_mem = vmalloc (strlen(vgdb_prefix) + 30); + /* below 3 lines must match the equivalent in remote-utils.c */ + sprintf(from_gdb_to_pid, "%s-from-vgdb-to-%d", vgdb_prefix, pid); + sprintf(to_gdb_from_pid, "%s-to-vgdb-from-%d", vgdb_prefix, pid); + sprintf(shared_mem, "%s-shared-mem-vgdb-%d", vgdb_prefix, pid); + DEBUG (1, "vgdb: using %s %s %s\n", + from_gdb_to_pid, to_gdb_from_pid, shared_mem); + + map_vgdbshared(shared_mem); +} + +/* Convert hex digit A to a number. */ + +static int +fromhex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + XERROR(0, "Reply contains invalid hex digit %c\n", a); + return 0; +} + +/* Returns next char from fd. -1 if error, -2 if EOF. + NB: must always call it with the same fd */ +static int +readchar (int fd) +{ + static unsigned char buf[PBUFSIZ]; + static int bufcnt = 0; + static unsigned char *bufp; + + if (bufcnt-- > 0) + return *bufp++; + + bufcnt = read (fd, buf, sizeof (buf)); + + if (bufcnt <= 0) { + if (bufcnt == 0) { + fprintf (stderr, "readchar: Got EOF\n"); + return -2; + } else { + ERROR (errno, "readchar\n"); + return -1; + } + } + + bufp = buf; + bufcnt--; + return *bufp++; +} + +/* Read a packet from fromfd, with error checking, + and store it in BUF. + Returns length of packet, or -1 if error or -2 if EOF. + Writes ack on ackfd */ + +static int +getpkt (char *buf, int fromfd, int ackfd) +{ + char *bp; + unsigned char csum, c1, c2; + int c; + + while (1) { + csum = 0; + + while (1) { + c = readchar (fromfd); + if (c == '$') + break; + DEBUG(2, "[getpkt: discarding char '%c']\n", c); + if (c < 0) + return c; + } + + bp = buf; + while (1) { + c = readchar (fromfd); + if (c < 0) + return c; + if (c == '#') + break; + if (c == '*') { + int repeat; + int r; + int prev; + prev = *(bp-1); + csum += c; + repeat = readchar (fromfd); + csum += repeat; + for (r = 0; r < repeat - 29; r ++) + *bp++ = prev; + } else { + *bp++ = c; + csum += c; + } + } + *bp = 0; + + c1 = fromhex (readchar (fromfd)); + c2 = fromhex (readchar (fromfd)); + + if (csum == (c1 << 4) + c2) + break; + + fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n", + (c1 << 4) + c2, csum, buf); + if (write (ackfd, "-", 1) != 1) + ERROR(0, "error when writing - (nack)\n"); + else + add_written(1); + } + + DEBUG(2, "getpkt (\"%s\"); [sending ack] \n", buf); + if (write (ackfd, "+", 1) != 1) + ERROR(0, "error when writing + (ack)\n"); + else + add_written(1); + return bp - buf; +} + +static int sigint = 0; +static int sigterm = 0; +static int sigpipe = 0; +static int sighup = 0; +static int sigusr1 = 0; +static int sigalrm = 0; +static int sigusr1_fd = -1; +static pthread_t invoke_gdbserver_in_valgrind_thread; + +static +void received_signal (int signum) +{ + if (signum == SIGINT) + sigint++; + else if (signum == SIGUSR1) { + sigusr1++; + if (sigusr1_fd >= 0) { + char control_c = '\003'; + write_buf(sigusr1_fd, &control_c, 1, + "write \\003 on SIGUSR1", /* notify */ True); + } + } + else if (signum == SIGTERM) { + shutting_down = True; + sigterm++; + } else if (signum == SIGHUP) { + shutting_down = True; + sighup++; + } else if (signum == SIGPIPE) { + sigpipe++; + } else if (signum == SIGALRM) { + sigalrm++; + DEBUG(1, "pthread_cancel invoke_gdbserver_in_valgrind_thread\n"); + /* Note: we cannot directly invoke restore_and_detach : this must + be done by the thread that has attached. + We have in this thread pushed a cleanup handler that will + cleanup what is needed. */ + pthread_cancel(invoke_gdbserver_in_valgrind_thread); + } else { + ERROR(0, "unexpected signal %d\n", signum); + } +} + +/* install the signal handlers allowing e.g. vgdb to cleanup in + case of termination. */ +static +void install_handlers(void) +{ + struct sigaction action, oldaction; + + action.sa_handler = received_signal; + sigemptyset (&action.sa_mask); + action.sa_flags = 0; + + /* SIGINT: when user types C-c in gdb, this sends + a SIGINT to vgdb + causes a character to be sent to remote gdbserver. + The later is enough to wakeup the valgrind process. */ + if (sigaction (SIGINT, &action, &oldaction) != 0) + XERROR (errno, "vgdb error sigaction SIGINT\n"); + /* We might do something more intelligent than just + reporting this SIGINT E.g. behave similarly to the gdb: two + control-C without feedback from the debugged process would + mean to stop debugging it. */ + + /* SIGUSR1: this is used to facilitate automatic testing. When + vgdb receives this signal, it will simulate the user typing C-c. */ + if (sigaction (SIGUSR1, &action, &oldaction) != 0) + XERROR (errno, "vgdb error sigaction SIGUSR1\n"); + + + /* SIGTERM: can receive this signal (e.g. from gdb) to terminate vgdb + when detaching or similar. A clean shutdown will be done as both + the read and write side will detect an end of file. */ + if (sigaction (SIGTERM, &action, &oldaction) != 0) + XERROR (errno, "vgdb error sigaction SIGTERM\n"); + + /* SIGPIPE: can receive this signal when gdb detaches or kill the + process debugged: gdb will close its pipes to vgdb. vgdb + must resist to this signal to allow a clean shutdown. */ + if (sigaction (SIGPIPE, &action, &oldaction) != 0) + XERROR (errno, "vgdb error sigaction SIGPIPE\n"); + + /* SIGALRM: in case invoke thread is blocked, alarm is used + to cleanup. */ + if (sigaction (SIGALRM, &action, &oldaction) != 0) + XERROR (errno, "vgdb error sigaction SIGALRM\n"); +} + +/* close the FIFOs provided connections, terminate the invoker thread. */ +static +void close_connection(int to_pid, int from_pid) +{ + DEBUG(1, "nr received signals: sigint %d sigterm %d sighup %d sigpipe %d\n", + sigint, sigterm, sighup, sigpipe); + /* Note that we do not forward sigterm to the valgrind process: + a sigterm signal is (probably) received from gdb if the user wants to + kill the debugged process. The kill instruction has been given to + the valgrind process, which should execute a clean exit. */ + + /* We first close the connection to pid. The pid will then + terminates its gdbserver work. We keep the from pid + fifo opened till the invoker thread is finished. + This allows the gdbserver to finish sending its last reply. */ + if (close(to_pid) != 0) + ERROR(errno, "close to_pid\n"); + + /* if there is a task that was busy trying to wake up valgrind + process, we wait for it to be terminated otherwise threads + in the valgrind process can stay stopped if vgdb main + exits before the invoke thread had time to detach from + all valgrind threads. */ + if (max_invoke_ms > 0) { + int join; + + /* It is surprisingly complex to properly shutdown or exit the + valgrind process in which gdbserver has been invoked through + ptrace. In the normal case (gdb detaches from the process, + or process is continued), the valgrind process will reach the + breakpoint place. Using ptrace, vgdb will ensure the + previous activity of the process is resumed (e.g. restart a + blocking system call). The special case is when gdb asks the + valgrind process to exit (using either the "kill" command or + "monitor exit"). In such a case, the valgrind process will + call exit. But a ptraced process will be blocked in exit, + waiting for the ptracing process to detach or die. vgdb + cannot detach unconditionally as otherwise, in the normal + case, the valgrind process would die abnormally with SIGTRAP + (as vgdb would not be there to catch it). vgdb can also not + die unconditionally otherwise again, similar problem. So, we + assume that most of the time, we arrive here in the normal + case, and so, the breakpoint has been encountered by the + valgrind process, so the invoker thread will exit and the + join will succeed. For the "kill" case, we cause an alarm + signal to be sent after a few seconds. This means that in the + normal case, the gdbserver code in valgrind process must have + returned the control in less than the alarm nr of seconds, + otherwise, valgrind will die abnormally with SIGTRAP. */ + (void) alarm (3); + + DEBUG(1, "joining with invoke_gdbserver_in_valgrind_thread\n"); + join = pthread_join(invoke_gdbserver_in_valgrind_thread, NULL); + if (join != 0) + XERROR + (join, + "vgdb error pthread_join invoke_gdbserver_in_valgrind_thread\n"); + } + if (close(from_pid) != 0) + ERROR(errno, "close from_pid\n"); +} + +/* Relay data between gdb and Valgrind gdbserver, till EOF or an + error is encountered. */ +static +void gdb_relay (int pid) +{ + int from_pid = -1; /* fd to read from pid */ + int to_pid = -1; /* fd to write to pid */ + + int shutdown_loop = 0; + fprintf (stderr, "relaying data between gdb and process %d\n", pid); + fflush (stderr); + + if (max_invoke_ms > 0) + pthread_create(&invoke_gdbserver_in_valgrind_thread, NULL, + invoke_gdbserver_in_valgrind, (void *) &pid); + to_pid = open_fifo(from_gdb_to_pid, O_WRONLY, "write to pid"); + acquire_lock (shared_mem_fd, pid); + + from_pid = open_fifo (to_gdb_from_pid, O_RDONLY|O_NONBLOCK, + "read mode from pid"); + + sigusr1_fd = to_pid; /* allow simulating user typing control-c */ + + while (1) { + ConnectionKind ck; + int ret; + struct pollfd pollfds[NumConnectionKind]; + + /* watch data written by gdb, watch POLLERR on both gdb fd */ + pollfds[FROM_GDB].fd = from_gdb; + pollfds[FROM_GDB].events = POLLIN; + pollfds[FROM_GDB].revents = 0; + pollfds[TO_GDB].fd = to_gdb; + pollfds[TO_GDB].events = 0; + pollfds[TO_GDB].revents = 0; + + /* watch data written by pid, watch POLLERR on both pid fd */ + pollfds[FROM_PID].fd = from_pid; + pollfds[FROM_PID].events = POLLIN; + pollfds[FROM_PID].revents = 0; + pollfds[TO_PID].fd = to_pid; + pollfds[TO_PID].events = 0; + pollfds[TO_PID].revents = 0; + + ret = poll(pollfds, + NumConnectionKind, + (shutting_down ? + 1 /* one second */ + : -1 /* infinite */)); + DEBUG(2, "poll ret %d errno %d\n", ret, errno); + + /* check for unexpected error */ + if (ret <= 0 && errno != EINTR) { + ERROR (errno, "unexpected poll ret %d\n", ret); + shutting_down = True; + break; + } + + /* check for data to read */ + for (ck = 0; ck < NumConnectionKind; ck ++) { + if (pollfds[ck].revents & POLLIN) { + switch (ck) { + case FROM_GDB: + if (!read_from_gdb_write_to_pid(to_pid)) + shutting_down = True; + break; + case FROM_PID: + if (!read_from_pid_write_to_gdb(from_pid)) + shutting_down = True; + break; + default: XERROR(0, "unexpected POLLIN on %s\n", + ppConnectionKind(ck)); + } + } + } + + /* check for an fd being in error condition */ + for (ck = 0; ck < NumConnectionKind; ck ++) { + if (pollfds[ck].revents & POLLERR) { + DEBUG(1, "connection %s fd %d POLLERR error condition\n", + ppConnectionKind(ck), pollfds[ck].fd); + valgrind_dying(); + shutting_down = True; + } + if (pollfds[ck].revents & POLLHUP) { + DEBUG(1, "connection %s fd %d POLLHUP error condition\n", + ppConnectionKind(ck), pollfds[ck].fd); + valgrind_dying(); + shutting_down = True; + } + if (pollfds[ck].revents & POLLNVAL) { + DEBUG(1, "connection %s fd %d POLLNVAL error condition\n", + ppConnectionKind(ck), pollfds[ck].fd); + valgrind_dying(); + shutting_down = True; + } + } + + if (shutting_down) { + /* we let some time to the final packets to be transferred */ + shutdown_loop++; + if (shutdown_loop > 3) + break; + } + } + close_connection(to_pid, from_pid); +} + +static int packet_len_for_command(char *cmd) +{ + /* cmd will be send as a packet $qRcmd,xxxx....................xx#cc */ + return 7+ 2*strlen(cmd) +3 + 1; +} + +/* hyper-minimal protocol implementation that + sends the provided commands (using qRcmd packets) + and read and display their replies. */ +static +void standalone_send_commands(int pid, + int last_command, + char *commands[] ) +{ + int from_pid = -1; /* fd to read from pid */ + int to_pid = -1; /* fd to write to pid */ + + int i; + int hi; + unsigned char hex[3]; + unsigned char cksum; + unsigned char *hexcommand; + unsigned char buf[PBUFSIZ]; + int buflen; + int nc; + + + if (max_invoke_ms > 0) + pthread_create(&invoke_gdbserver_in_valgrind_thread, NULL, + invoke_gdbserver_in_valgrind, (void *) &pid); + + to_pid = open_fifo(from_gdb_to_pid, O_WRONLY, "write to pid"); + acquire_lock (shared_mem_fd, pid); + + /* first send a C-c \003 to pid, so that it wakes up the process + After that, we can open the fifo from the pid in read mode + We then start to wait for packets (normally first a resume reply) + At that point, we send our command and expect replies */ + buf[0] = '\003'; + write_buf(to_pid, buf, 1, "write \\003 to wake up", /* notify */ True); + from_pid = open_fifo(to_gdb_from_pid, O_RDONLY, + "read cmd result from pid"); + + for (nc = 0; nc <= last_command; nc++) { + fprintf (stderr, "sending command %s to pid %d\n", commands[nc], pid); + fflush (stderr); + + /* prepare hexcommand $qRcmd,xxxx....................xx#cc */ + hexcommand = vmalloc (packet_len_for_command(commands[nc])); + hexcommand[0] = 0; + strcat (hexcommand, "$qRcmd,"); + for (i = 0; i < strlen(commands[nc]); i++) { + sprintf(hex, "%02x", commands[nc][i]); + strcat (hexcommand, hex); + } + /* checksum (but without the $) */ + cksum = 0; + for (hi = 1; hi < strlen(hexcommand); hi++) + cksum+=hexcommand[hi]; + strcat(hexcommand, "#"); + sprintf(hex, "%02x", cksum); + strcat(hexcommand, hex); + write_buf(to_pid, hexcommand, strlen(hexcommand), + "writing hex command to pid", /* notify */ True); + + /* we exit of the below loop explicitely when the command has + been handled or because a signal handler will set + shutting_down. */ + while (!shutting_down) { + buflen = getpkt(buf, from_pid, to_pid); + if (buflen < 0) { + ERROR (0, "error reading packet\n"); + if (buflen == -2) + valgrind_dying(); + break; + } + if (strlen(buf) == 0) { + DEBUG(0, "empty packet rcvd (packet qRcmd not recognised?)\n"); + break; + } + if (strcmp(buf, "OK") == 0) { + DEBUG(1, "OK packet rcvd\n"); + break; + } + if (buf[0] == 'E') { + DEBUG(0, + "E NN error packet rcvd: %s (unknown monitor command?)\n", + buf); + break; + } + if (buf[0] == 'W') { + DEBUG(0, "W stopped packet rcvd: %s\n", buf); + break; + } + if (buf[0] == 'T') { + DEBUG(1, "T resume reply packet received: %s\n", buf); + continue; + } + + /* must be here an O packet with hex encoded string reply + => decode and print it */ + if (buf[0] != 'O') { + DEBUG(0, "expecting O packet, received: %s\n", buf); + continue; + } + { + char buf_print[buflen/2 + 1]; + for (i = 1; i < buflen; i = i + 2) + buf_print[i/2] = (fromhex(*(buf+i)) << 4) + + fromhex(*(buf+i+1)); + buf_print[buflen/2] = 0; + printf("%s", buf_print); + fflush(stdout); + } + } + free (hexcommand); + } + shutting_down = True; + + close_connection(to_pid, from_pid); +} + +/* report to user the existence of a vgdb-able valgrind process + with given pid */ +static +void report_pid (int pid) +{ + char cmdline_file[100]; + char cmdline[1000]; + int fd; + int i, sz; + + sprintf(cmdline_file, "/proc/%d/cmdline", pid); + fd = open (cmdline_file, O_RDONLY); + if (fd == -1) { + DEBUG(1, "error opening cmdline file %s %s\n", + cmdline_file, strerror(errno)); + sprintf(cmdline, "(could not obtain process command line)"); + } else { + sz = read(fd, cmdline, 1000); + for (i = 0; i < sz; i++) + if (cmdline[i] == 0) + cmdline[i] = ' '; + cmdline[sz] = 0; + } + fprintf(stderr, "use --pid=%d for %s\n", pid, cmdline); + fflush(stderr); +} + +/* Eventually produces additional usage information documenting the + ptrace restrictions. */ +static +void ptrace_restrictions(void) +{ +# ifdef PR_SET_PTRACER + char *ptrace_scope_setting_file = "/proc/sys/kernel/yama/ptrace_scope"; + int fd = -1; + char ptrace_scope = 'X'; + fd = open (ptrace_scope_setting_file, O_RDONLY, 0); + if (fd >= 0 && (read (fd, &ptrace_scope, 1) == 1) && (ptrace_scope != '0')) { + fprintf (stderr, + "Note: your kernel restricts ptrace invoker using %s\n" + "vgdb will only be able to attach to a Valgrind process\n" + "blocked in a system call *after* an initial successful attach\n", + ptrace_scope_setting_file); + } else if (ptrace_scope == 'X') { + fprintf(stderr, "Could not determine ptrace scope from %s\n", + ptrace_scope_setting_file); + } + if (fd >= 0) + close (fd); +# endif + +# ifndef PTRACEINVOKER + fprintf(stderr, + "Note: ptrace invoker not implemented\n" + "For more info: read user manual section" + " 'Limitations of the Valgrind gdbserver'\n"); +# endif +} + +static +void usage(void) +{ + fprintf(stderr, +"Usage: vgdb [OPTION]... [[-c] COMMAND]...\n" +"vgdb (valgrind gdb) has two usages\n" +" 1. standalone to send monitor commands to a Valgrind gdbserver.\n" +" The OPTION(s) must be followed by the command to send\n" +" To send more than one command, separate the commands with -c\n" +" 2. relay application between gdb and a Valgrind gdbserver.\n" +" Only OPTION(s) can be given.\n" +"\n" +" OPTIONS are [--pid=<number>] [--vgdb-prefix=<prefix>]\n" +" [--max-invoke-ms=<number>] [--wait=<number>] [-d] -D]\n" +" --pid arg must be given if multiple Valgrind gdbservers are found.\n" +" --vgdb-prefix arg must be given to both Valgrind and vgdb utility\n" +" if you want to change the default prefix for the FIFOs communication\n" +" between the Valgrind gdbserver and vgdb.\n" +" --wait arg tells vgdb to check during the specified number\n" +" of seconds if a Valgrind gdbserver can be found.\n" +" --max-invoke-ms gives the nr of milli-seconds after which vgdb will force\n" +" the invocation of the Valgrind gdbserver (if the Valgrind process\n" +" is blocked in a system call).\n" +" -d arg tells to show debug info. Multiple -d args for more debug info\n" +" -D arg tells to show shared mem status and then exit.\n" +"\n" + ); + ptrace_restrictions(); +} + +/* If arg_pid == -1, waits maximum check_trials seconds to discover + a valgrind pid appearing. + Otherwise verify arg_pid is valid and corresponds to a Valgrind process + with gdbserver activated. + + Returns the pid to work with + or exits in case of error (e.g. no pid found corresponding to arg_pid */ + +static +int search_arg_pid(int arg_pid, int check_trials) +{ + int i; + int pid = -1; + + if (arg_pid == 0 || arg_pid < -1) { + fprintf (stderr, "vgdb error: invalid pid %d given\n", arg_pid); + exit (1); + } else { + /* search for a matching named fifo. + If we have been given a pid, we will check that the matching FIFO is + there (or wait the nr of check_trials for this to appear). + If no pid has been given, then if we find only one FIFO, + we will use this to build the pid to use. + If we find multiple processes with valid FIFO, we report them and will + exit with an error. */ + DIR *vgdb_dir; + char *vgdb_dir_name = vmalloc (strlen (vgdb_prefix) + 3); + struct dirent *f; + int is; + int nr_valid_pid = 0; + const char *suffix = "-from-vgdb-to-"; /* followed by pid */ + char *vgdb_format = vmalloc (strlen(vgdb_prefix) + strlen(suffix) + 1); + + strcpy (vgdb_format, vgdb_prefix); + strcat (vgdb_format, suffix); + + strcpy (vgdb_dir_name, vgdb_prefix); + + for (is = strlen(vgdb_prefix) - 1; is >= 0; is--) + if (vgdb_dir_name[is] == '/') { + vgdb_dir_name[is+1] = '\0'; + break; + } + if (strlen(vgdb_dir_name) == 0) + strcpy (vgdb_dir_name, "./"); + + DEBUG(1, "searching pid in directory %s format %s\n", + vgdb_dir_name, vgdb_format); + + /* try to find FIFOs with valid pid. + On exit of the loop, pid is set to: + -1 if no FIFOs matching a running process is found + -2 if multiple FIFOs of running processes are found + otherwise it is set to the (only) pid found that can be debugged + */ + for (i = 0; i < check_trials; i++) { + DEBUG(1, "check_trial %d \n", i); + if (i > 0) + /* wait one second before checking again */ + sleep(1); + + vgdb_dir = opendir (vgdb_dir_name); + if (vgdb_dir == NULL) + XERROR (errno, + "vgdb error: opening directory %s searching vgdb fifo\n", + vgdb_dir_name); + + errno = 0; /* avoid complain if vgdb_dir is empty */ + while ((f = readdir (vgdb_dir))) { + struct stat st; + char pathname[strlen(vgdb_dir_name) + strlen(f->d_name)]; + char *wrongpid; + int newpid; + + strcpy (pathname, vgdb_dir_name); + strcat (pathname, f->d_name); + DEBUG(3, "trying %s\n", pathname); + if (stat (pathname, &st) != 0) { + if (debuglevel >= 3) + ERROR (errno, "vgdb error: stat %s searching vgdb fifo\n", + pathname); + } else if (S_ISFIFO (st.st_mode)) { + DEBUG(3, "trying %s\n", pathname); + if (strncmp (pathname, vgdb_format, + strlen (vgdb_format)) == 0) { + newpid = strtol(pathname + strlen (vgdb_format), + &wrongpid, 10); + if (*wrongpid == '\0' && newpid > 0 + && kill (newpid, 0) == 0) { + nr_valid_pid++; + if (arg_pid != -1) { + if (arg_pid == newpid) { + pid = newpid; + } + } else if (nr_valid_pid > 1) { + if (nr_valid_pid == 2) { + fprintf + (stderr, + "no --pid= arg given" + " and multiple valgrind pids found:\n"); + report_pid (pid); + } + pid = -2; + report_pid (newpid); + } else { + pid = newpid; + } + } + } + } + errno = 0; /* avoid complain if at the end of vgdb_dir */ + } + if (f == NULL && errno != 0) + XERROR (errno, "vgdb error: reading directory %s for vgdb fifo\n", + vgdb_dir_name); + + closedir (vgdb_dir); + if (pid != -1) + break; + } + + free (vgdb_dir_name); + free (vgdb_format); + } + + if (pid == -1) { + if (arg_pid == -1) + fprintf (stderr, "vgdb error: no FIFO found and no pid given\n"); + else + fprintf (stderr, "vgdb error: no FIFO found matching pid %d\n", + arg_pid); + exit (1); + } + else if (pid == -2) { + /* no arg_pid given, multiple FIFOs found */ + exit (1); + } + else { + return pid; + } +} + +/* return true if the numeric value of an option of the + form --xxxxxxxxx=<number> could properly be extracted + from arg. If True is returned, *value contains the + extracted value.*/ +static +Bool numeric_val(char* arg, int *value) +{ + const char *eq_pos = strchr(arg, '='); + char *wrong; + + if (eq_pos == NULL) + return False; + + *value = strtol(eq_pos+1, &wrong, 10); + if (*wrong) + return False; + + return True; +} + +/* true if arg matches the provided option */ +static +Bool is_opt(char* arg, char *option) +{ + int option_len = strlen(option); + if (option[option_len-1] == '=') + return (0 == strncmp(option, arg, option_len)); + else + return (0 == strcmp(option, arg)); +} + +/* Parse command lines options. If error(s), exits. + Otherwise returns the options in *p_... args. + commands must be big enough for the commands extracted from argv. + On return, *p_last_command gives the position in commands where + the last command has been allocated (using vmalloc). */ +static +void parse_options(int argc, char** argv, + Bool *p_show_shared_mem, + int *p_arg_pid, + int *p_check_trials, + int *p_last_command, + char *commands[]) +{ + Bool show_shared_mem = False; + int arg_pid = -1; + int check_trials = 1; + int last_command = -1; + + int i; + int arg_errors = 0; + + for (i = 1; i < argc; i++) { + if (is_opt(argv[i], "--help") || is_opt(argv[i], "-h")) { + usage(); + exit(0); + } else if (is_opt(argv[i], "-d")) { + debuglevel++; + } else if (is_opt(argv[i], "-D")) { + show_shared_mem = True; + } else if (is_opt(argv[i], "--pid=")) { + int newpid; + if (!numeric_val(argv[i], &newpid)) { + fprintf (stderr, "invalid pid argument %s\n", argv[i]); + arg_errors++; + } else if (arg_pid != -1) { + fprintf (stderr, "multiple pid arguments given\n"); + arg_errors++; + } else { + arg_pid = newpid; + } + } else if (is_opt(argv[i], "--wait=")) { + if (!numeric_val(argv[i], &check_trials)) { + fprintf (stderr, "invalid wait argument %s\n", argv[i]); + arg_errors++; + } + } else if (is_opt(argv[i], "--max-invoke-ms=")) { + if (!numeric_val(argv[i], &max_invoke_ms)) { + fprintf (stderr, "invalid max-invoke-ms argument %s\n", argv[i]); + arg_errors++; + } + } else if (is_opt(argv[i], "--vgdb-prefix=")) { + vgdb_prefix = argv[i] + 14; + } else if (is_opt(argv[i], "-c")) { + last_command++; + commands[last_command] = vmalloc (1); + commands[last_command][0] = '\0'; + } else if (0 == strncmp(argv[i], "-", 1)) { + fprintf (stderr, "unknown or invalid argument %s\n", argv[i]); + arg_errors++; + } else { + int len; + if (last_command == -1) { + /* only one command, no -c command indicator */ + last_command++; + commands[last_command] = vmalloc (1); + commands[last_command][0] = '\0'; + } + len = strlen(commands[last_command]); + commands[last_command] = vrealloc (commands[last_command], + len + 1 + strlen(argv[i]) + 1); + if (len > 0) + strcat (commands[last_command], " "); + strcat (commands[last_command], argv[i]); + if (packet_len_for_command(commands[last_command]) > PBUFSIZ) { + fprintf (stderr, "command %s too long\n", commands[last_command]); + arg_errors++; + } + + } + } + if (arg_errors > 0) { + fprintf (stderr, "args error. Try `vgdb --help` for more information\n"); + exit(1); + } + + *p_show_shared_mem = show_shared_mem; + *p_arg_pid = arg_pid; + *p_check_trials = check_trials; + *p_last_command = last_command; +} + +int main(int argc, char** argv) +{ + int i; + int pid; + + Bool show_shared_mem; + int arg_pid; + int check_trials; + int last_command; + char *commands[argc]; // we will never have more commands than args. + + parse_options(argc, argv, + &show_shared_mem, + &arg_pid, + &check_trials, + &last_command, + commands); + + /* when we are working as a relay for gdb, handle some signals by + only reporting them (according to debug level). Also handle these + when ptrace will be used: vgdb must clean up the ptrace effect before + dying. */ + if (max_invoke_ms > 0 || last_command == -1) + install_handlers(); + + pid = search_arg_pid (arg_pid, check_trials); + + prepare_fifos_and_shared_mem(pid); + + if (show_shared_mem) { + fprintf(stderr, + "vgdb %d " + "written_by_vgdb %d " + "seen_by_valgrind %d\n" + "vgdb pid %d\n", + VS_vgdb_pid, + VS_written_by_vgdb, + VS_seen_by_valgrind, + VS_vgdb_pid); + exit (0); + } + + if (last_command >= 0) { + standalone_send_commands(pid, last_command, commands); + } else { + gdb_relay(pid); + } + + + free (from_gdb_to_pid); + free (to_gdb_from_pid); + free (shared_mem); + + for (i = 0; i <= last_command; i++) + free (commands[i]); + return 0; +} diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 0fb6c9d3..acc8f73c 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -720,6 +720,49 @@ in most cases. We group the available options by rough categories.</para> </listitem> </varlistentry> + <varlistentry id="opt.vgdb" xreflabel="--vgdb"> + <term> + <option><![CDATA[--vgdb=<no|yes|full> [default: yes] ]]></option> + </term> + <listitem> + <para>Valgrind will enable its embedded gdbserver if value yes + or full is given. This allows an + external <computeroutput>gdb</computeroutput> debuggger to debug + your program running under Valgrind. See + <xref linkend="manual-core.gdbserver"/> for a detailed + description. + </para> + + <para> If the embedded gdbserver is enabled but no gdb is + currently being used, the <xref linkend="manual-core.vgdb"/> + command line utility can send "monitor commands" to Valgrind + from a shell. The Valgrind core provides a set of + <xref linkend="manual-core.valgrind-monitor-commands"/>. A tool + can optionally provide tool specific monitor commands, which are + documented in the tool specific chapter. + </para> + + <para>The value 'full' has a significant overhead + </para> + </listitem> + </varlistentry> + + <varlistentry id="opt.vgdb-error" xreflabel="--vgdb-error"> + <term> + <option><![CDATA[--vgdb-error=<number> [default: 999999999] ]]></option> + </term> + <listitem> + <para> Use this option when the Valgrind gdbserver is enabled with + <option>--vgdb</option> yes or full value. Tools that report + errors will invoke the embedded gdbserver for each error above + number. The value 0 will cause gdbserver to be invoked before + executing your program. This is typically used to insert gdb + breakpoints before execution, and will also work with tools that + do not report errors, such as Massif. + </para> + </listitem> + </varlistentry> + <varlistentry id="opt.track-fds" xreflabel="--track-fds"> <term> <option><![CDATA[--track-fds=<yes|no> [default: no] ]]></option> @@ -1140,6 +1183,13 @@ that can report errors, e.g. Memcheck, but not Cachegrind.</para> debugger, quit from it, and the program will continue. Trying to continue from inside the debugger doesn't work.</para> + <para> + Note : if you use gdb, a more powerful debugging support is + provided by the <option>--vgdb</option> yes or full value, + allowing among others to insert breakpoints, continue from + inside the debugger, etc. + </para> + <para><varname>C Ret</varname> or <varname>c Ret</varname> causes Valgrind not to start a debugger, and not to ask again.</para> </listitem> @@ -1473,6 +1523,57 @@ need to use these.</para> </listitem> </varlistentry> + <varlistentry id="opt.vgdb-poll" xreflabel="--vgdb-poll"> + <term> + <option><![CDATA[--vgdb-poll=<number> [default: 5000] ]]></option> + </term> + <listitem> + <para> As part of its main loop, the Valgrind scheduler will + poll to check if some activity (such as an external command or + some input from a gdb) has to be handled by gdbserver. This + activity poll will be done after having run the given number of + basic blocks (or slightly more than the given number of basic + blocks). This poll is quite cheap so the default value is set + relatively low. You might further decrease this value if vgdb + cannot use ptrace system call to interrupt Valgrind if all + threads are (most of the time) blocked in a system call. + </para> + <para> GDBTD??? unclear why we have sometimes slightly more BB: + it seems that from time to time, some BB are run outside of + run_thread_for_a_while. Maybe this is due to block chasing ? I + do not think this is a problem, as I never saw more than a few + additional basic blocks being run without being visible in the + blocks executed by run_thread_for_a_while. + </para> + </listitem> + </varlistentry> + + <varlistentry id="opt.vgdb-shadow-registers" xreflabel="--vgdb-shadow-registers"> + <term> + <option><![CDATA[--vgdb-shadow-registers=no|yes [default: no] ]]></option> + </term> + <listitem> + <para> When activated, gdbserver will expose the Valgrind shadow registers + to gdb. With this, the value of the Valgrind shadow registers can be examined + or changed using gdb. Exposing shadows registers only works with a gdb version + >= 7.1. + </para> + </listitem> + </varlistentry> + + <varlistentry id="opt.vgdb-prefix" xreflabel="--vgdb-prefix"> + <term> + <option><![CDATA[--vgdb-prefix=<prefix> [default: /tmp/vgdb-pipe] ]]></option> + </term> + <listitem> + <para> To communicate with gdb/vgdb, the Valgrind gdbserver + creates 3 files (2 named FIFOs and a mmap shared memory + file). The prefix option controls the directory and prefix for + the creation of these files. + </para> + </listitem> + </varlistentry> + <varlistentry id="opt.run-libc-freeres" xreflabel="--run-libc-freeres"> <term> <option><![CDATA[--run-libc-freeres=<yes|no> [default: yes] ]]></option> @@ -1630,6 +1731,13 @@ need to use these.</para> Valgrind itself. You shouldn't need to use them in the normal run of things. If you wish to see the list, use the <option>--help-debug</option> option.</para> + +<para>If you wish to debug your program rather than debugging +Valgrind itself, then you should use the options +<option>--vgdb=yes</option> or <option>--vgdb=full</option> +or <option>--db-attach=yes</option>. +</para> + <!-- end of xi:include in the manpage --> </sect2> @@ -1692,7 +1800,869 @@ don't understand </sect1> +<sect1 id="manual-core.gdbserver" + xreflabel="Debugging your program using Valgrind gdbserver and gdb"> +<title>Debugging your program using Valgrind gdbserver and gdb</title> + +<para>A program running under Valgrind is not executed directly by the +CPU. It rather runs on a synthetic CPU provided by Valgrind. This is +why a debugger cannot debug your program under Valgrind the usual way. +</para> +<para> +This section describes the special way gdb can interact with the +Valgrind gdbserver to provide a fully debuggable program under +Valgrind. Used in this way, gdb also provides an interactive usage of +Valgrind core or tool functionalities (such as incremental leak search +under Memcheck, on-demand Massif snapshot production, ...). +</para> + +<sect2 id="manual-core.gdbserver-simple" + xreflabel="gdbserver simple example"> +<title>Quick Start : debugging in 3 steps</title> + +<para>If you want to debug a program with gdb when using Memcheck +tool, start Valgrind the following way: +<screen><![CDATA[ +valgrind --vgdb=yes --vgdb-error=0 prog +]]></screen></para> + +<para>In another window, start a gdb the following way: +<screen><![CDATA[ +gdb prog +]]></screen></para> + +<para>Then give the following command to gdb: +<screen><![CDATA[ +(gdb) target remote | vgdb +]]></screen></para> + +<para>You can now debug your program e.g. by inserting a breakpoint +and then using the gdb 'continue' command.</para> + +<para> The above quick start is enough for a basic usage of the +Valgrind gdbserver. Read the sections below to learn about the +advanced functionalities provided by the combination of Valgrind and +gdb. Note that the option --vgdb=yes can be omitted, as this is the +default value. +</para> + +</sect2> + +<sect2 id="manual-core.gdbserver-concept" + xreflabel="gdbserver"> +<title>Valgrind gdbserver concept</title> +<para>The gdb debugger is typically used to debug a process running on +the same machine : gdb uses system calls to do actions such as read +the values of the process variables or registers. This technique only +allows gdb to debug a program running on the same computer. +</para> + +<para>Gdb can also debug processes running on a different computer. +For this, gdb defines a protocol (i.e. a set of query and reply +packets) that allows to e.g. fetch the value of memory or registers, +to set breakpoints, etc. A gdbserver is an implementation of this +'gdb remote debugging' protocol. To debug a process running on a +remote computer, a gdbserver (sometimes also called a gdb stub) must +run at the remote computer side. +</para> + +<para>The Valgrind core integrates an embedded gdbserver +implementation, which is activated using <option>--vgdb=yes</option> +or <option>--vgdb=full</option>. This gdbserver allows the process +running on the Valgrind synthetic CPU to be debugged 'remotely' by gdb +: gdb sends protocol query packets (such as 'get registers values') to +the Valgrind embedded gdbserver. The embedded gdbserver executes the +queries (for example, it will get the registers values of the +synthetic CPU) and give the result back to gdb. +</para> + +<para> Gdb can use various ways (tcp/ip, serial line, ...) to send and +receive the remote protocol packets to a gdbserver. In the case of the +Valgrind gdbserver, gdb communicates using a pipe and the +<xref linkend="manual-core.vgdb"/> command as a relay application. If +no gdb is currently being used, vgdb can also be used to send monitor +commands to the Valgrind gdbserver from the shell command line. +</para> + +</sect2> + +<sect2 id="manual-core.gdbserver-gdb" + xreflabel="Connecting gdb to a Valgrind gdbserver"> +<title>Connecting gdb to a Valgrind gdbserver</title> +<para>To debug a program <filename>prog</filename> running under +Valgrind, ensures that the Valgrind gdbserver is activated +(i.e. --vgdb=yes or --vgdb=full). The option +<![CDATA[--vgdb-error=<number> ]]> can be used to ask an invocation of +the gdbserver for each error above number. A zero value will cause an +invocation of the Valgrind gdbserver at startup, allowing to insert +breakpoints before starting the execution. Example: +<screen><![CDATA[ +valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog +]]></screen></para> + +<para>With the above command, the Valgrind gdbserver is invoked at startup +and indicates it is waiting for a connection from a gdb:</para> + +<programlisting><![CDATA[ +==2418== Memcheck, a memory error detector +==2418== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. +==2418== Using Valgrind-3.7.0.SVN and LibVEX; rerun with -h for copyright info +==2418== Command: ./prog +==2418== +==2418== (action at startup) vgdb me ... +]]></programlisting> + + +<para>A gdb in another window can then be connected to the Valgrind gdbserver. +For this, gdb must be started on the program <filename>prog</filename>: +<screen><![CDATA[ +gdb ./prog +]]></screen></para> + + +<para>You then indicate to gdb that a remote target debugging is to be done: +<screen><![CDATA[ +(gdb) target remote | vgdb +]]></screen> +gdb then starts a vgdb relay application to communicate with the +Valgrind embedded gdbserver:</para> + +<programlisting><![CDATA[ +(gdb) target remote | vgdb +Remote debugging using | vgdb +relaying data between gdb and process 2418 +Reading symbols from /lib/ld-linux.so.2...done. +Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so.debug...done. +Loaded symbols for /lib/ld-linux.so.2 +[Switching to Thread 2418] +0x001f2850 in _start () from /lib/ld-linux.so.2 +(gdb) +]]></programlisting> + +<para> In case vgdb detects that multiple Valgrind gdbserver can be connected +to, it will exit after reporting the list of the debuggable Valgrind +processes and their PIDs. You can then relaunch the gdb 'target' command, but +specifying the process id of the process you want to debug: +</para> + +<programlisting><![CDATA[ +(gdb) target remote | vgdb +Remote debugging using | vgdb +no --pid= arg given and multiple valgrind pids found: +use --pid=2479 for valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog +use --pid=2481 for valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog +use --pid=2483 for valgrind --vgdb=yes --vgdb-error=0 ./another_prog +Remote communication error: Resource temporarily unavailable. +(gdb) target remote | vgdb --pid=2479 +Remote debugging using | vgdb --pid=2479 +relaying data between gdb and process 2479 +Reading symbols from /lib/ld-linux.so.2...done. +Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so.debug...done. +Loaded symbols for /lib/ld-linux.so.2 +[Switching to Thread 2479] +0x001f2850 in _start () from /lib/ld-linux.so.2 +(gdb) +]]></programlisting> + +<para>Once gdb is connected to the Valgrind gdbserver, gdb can be used +similarly to a native debugging session:</para> + <itemizedlist> + <listitem> + <para> Breakpoints can be inserted or deleted. </para> + </listitem> + <listitem> + <para> Variables and registers values can be examined or modified. + </para> + </listitem> + <listitem> + <para> Signal handling can be configured (printing, ignoring, ...). + </para> + </listitem> + <listitem> + <para> Execution can be controlled (continue, step, next, stepi, ...). + </para> + </listitem> + <listitem> + <para> Program execution can be interrupted using Control-C. </para> + </listitem> + <listitem> + <para> ... </para> + </listitem> + </itemizedlist> + +<para> Refer to the gdb user manual for a complete list of gdb functionalities. +</para> + +</sect2> + +<sect2 id="manual-core.gdbserver-commandhandling" + xreflabel="Monitor command handling by the Valgrind gdbserver"> +<title>Monitor command handling by the Valgrind gdbserver</title> + +<para> The Valgrind gdbserver provides a set of additional specific +functionalities through "monitor commands". Such monitor commands can +be sent from the gdb command line or from the shell command line. See +<xref linkend="manual-core.valgrind-monitor-commands"/> for the list +of the Valgrind core monitor commands. +</para> + +<para> Each tool can also provide tool specific monitor commands. An +example of a tool specific monitor command is the Memcheck monitor +command <computeroutput>mc.leak_check any full +reachable</computeroutput>. This requests a full reporting of the +allocated memory blocks. To have this leak check executed, use the gdb +command: +<screen><![CDATA[ +(gdb) monitor mc.leak_check any full reachable +]]></screen> +</para> + +<para> gdb will send the mc.leak_check command to the Valgrind gdbserver. The +Valgrind gdbserver will either execute the monitor command itself (if +it recognises a Valgrind core monitor command) or let the tool execute the +tool specific monitor commands: +</para> +<programlisting><![CDATA[ +(gdb) monitor mc.leak_check any full reachable +==2418== 100 bytes in 1 blocks are still reachable in loss record 1 of 1 +==2418== at 0x4006E9E: malloc (vg_replace_malloc.c:236) +==2418== by 0x804884F: main (prog.c:88) +==2418== +==2418== LEAK SUMMARY: +==2418== definitely lost: 0 bytes in 0 blocks +==2418== indirectly lost: 0 bytes in 0 blocks +==2418== possibly lost: 0 bytes in 0 blocks +==2418== still reachable: 100 bytes in 1 blocks +==2418== suppressed: 0 bytes in 0 blocks +==2418== +(gdb) +]]></programlisting> + +<para> Like for the gdb commands, the Valgrind gdbserver will accept +abbreviated monitor command names and arguments, as long as the given +abbreviation is non ambiguous. For example, the above mc.leak_check +command can also be typed as: +<screen><![CDATA[ +(gdb) mo mc.l a f r +]]></screen> + +The letters <computeroutput>mo</computeroutput> are recognised by gdb as being +<computeroutput>monitor</computeroutput>. So, gdb sends the +string <computeroutput>mc.l a f r</computeroutput> to the Valgrind +gdbserver. The letters provided in this string are unambiguous for the +Valgrind gdbserver. So, this will give the same output as the non +abbreviated command and arguments. If the provided abbreviation is +ambiguous, the Valgrind gdbserver will report the list of commands (or +argument values) that can match: +<programlisting><![CDATA[ +(gdb) mo mc. a r f +mc. can match mc.get_vbits mc.leak_check mc.make_memory mc.check_memory +(gdb) +]]></programlisting> +</para> + +<para> Instead of sending a monitor command from gdb, you can also +send these from a shell command line. For example, the below command lines +given in a shell will cause the same leak search to be executed by the +process 3145: +<screen><![CDATA[ +vgdb --pid=3145 mc.leak_check any full reachable +vgdb --pid=3145 mc.l a f r +]]></screen></para> + +<para>Note that the Valgrind gdbserver automatically continues the +execution of the program after a standalone invocation of +vgdb. Monitor commands sent from gdb do not cause the program to +continue: the program execution is controlled explicitely using gdb +commands such as 'continue' or 'next'.</para> + +</sect2> + +<sect2 id="manual-core.gdbserver-threads" + xreflabel="Valgrind gdbserver thread info"> +<title>Valgrind gdbserver thread info</title> + +<para> The Valgrind gdbserver enriches the output of the +gdb <computeroutput>info threads</computeroutput> with Valgrind +specific information. The operating system thread number is followed +by the Valgrind 'tid' and the Valgrind scheduler thread state:</para> + +<programlisting><![CDATA[ +(gdb) info threads + 4 Thread 6239 (tid 4 VgTs_Yielding) 0x001f2832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2 +* 3 Thread 6238 (tid 3 VgTs_Runnable) make_error (s=0x8048b76 "called from London") at prog.c:20 + 2 Thread 6237 (tid 2 VgTs_WaitSys) 0x001f2832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2 + 1 Thread 6234 (tid 1 VgTs_Yielding) main (argc=1, argv=0xbedcc274) at prog.c:105 +(gdb) +]]></programlisting> + +</sect2> + +<sect2 id="manual-core.gdbserver-shadowregisters" + xreflabel="Examining and modifying Valgrind shadow registers"> +<title>Examining and modifying Valgrind shadow registers</title> + +<para> When the option <![CDATA[--vgdb-shadow-registers=yes ]]> is +given, the Valgrind gdbserver will let gdb examine and/or modify the +Valgrind shadow registers. A gdb version >= 7.1 is needed for this +to work.</para> + +<para> For each CPU register, the Valgrind core maintains two +shadow registers. These shadow registers can be accessed from +gdb by giving a postfix s1 or s2 for respectively the first +and second shadow registers. As an example, the x86 register +<computeroutput>eax</computeroutput> and its two shadow +registers can be examined using the following commands:</para> + +<programlisting><![CDATA[ +(gdb) p $eax +$1 = 0 +(gdb) p $eaxs1 +$2 = 0 +(gdb) p $eaxs2 +$3 = 0 +(gdb) +]]></programlisting> + +</sect2> + +<sect2 id="manual-core.gdbserver-limitations" + xreflabel="Limitations of the Valgrind gdbserver"> +<title>Limitations of the Valgrind gdbserver</title> + +<para>Debugging with the Valgrind gdbserver is very similar to native +debugging. The implementation of the Valgrind gdbserver is quite +complete, and so provides most of the gdb debugging facilities. There +are however some limitations or particularities described in details +in this section:</para> + <itemizedlist> + <listitem> + <para> Precision of 'stopped at instruction'.</para> + <para>Gdb commands such as 'step', 'next', 'stepi', breakpoints, + watchpoints, ... will stop the execution of the process. With + the option --vgdb=yes, the process might not stop at the exact + instruction needed. Instead, it might continue execution of the + current block and stop at one of the following blocks. This is + linked to the fact that Valgrind gdbserver has to instrument a + block to allow stopping at the exact instruction requested. + Currently, re-instrumenting the current block being executed is + not supported. So, if the action requested by gdb (e.g. single + stepping or inserting a breakpoint) implies to re-instrument the + current block, the gdb action might not be executed precisely. + </para> + <para> This limitation will be triggered when the current block + being executed has not (yet) been instrumented for debugging. + This typically happens when the gdbserver is activated due to the + tool reporting an error or to a watchpoint. If the gdbserver + block has been activated following a breakpoint (or if a + breakpoint has been inserted in the block before its execution), + then the block has already been instrumented for debugging. + </para> + <para> If you use the option --vgdb=full, then gdb 'stop actions' + will always be obeyed precisely, but this implies that each + instruction will be instrumented with an additional call to a + gdbserver helper function, which implies some overhead compared + to --vgdb=no. Option --vgdb=yes has neglectible overhead compared + to --vgdb=no. + </para> + </listitem> + + <listitem> + <para>Hardware watchpoint support by the Valgrind + gdbserver.</para> + + <para> The Valgrind gdbserver can simulate hardware watchpoints + (but only if the tool provides the support for this). Currently, + only Memcheck provides hardware watchpoint simulation. The + hardware watchpoint simulation provided by Memcheck is much + faster that gdb software watchpoints (which are implemented by + gdb checking the value of the watched zone(s) after each + instruction). Hardware watchpoint simulation also provides read + watchpoints. The hardware watchpoint simulation by Memcheck has + some limitations compared to the real hardware + watchpoints. However, the number and length of simulated + watchpoints are not limited. + </para> + <para> Typically, the number of (real) hardware watchpoint is + limited. For example, the x86 architecture supports a maximum of + 4 hardware watchpoints, each watchpoint watching 1, 2, 4 or 8 + bytes. The Valgrind gdbserver does not have a limitation on the + number of simulated hardware watchpoints. It also has no + limitation on the length of the memory zone being + watched. However, gdb currently does not (yet) understand that + Valgrind gdbserver watchpoints have no length limit. A gdb patch + providing a command 'set remote hardware-watchpoint-length-limit' + has been developped. The integration of this patch in gdb would + allow to fully use the flexibility of the Valgrind gdbserver + simulated hardware watchpoints (is there a gdb developper reading + this ?). + </para> + <para> Memcheck implements hardware watchpoint simulation by + marking the watched zone(s) as being unaddressable. In case a + hardware watchpoint is removed, the zone is marked as addressable + and defined. Hardware watchpoint simulation of addressable + undefined memory zones will properly work, but will have as a + side effect to mark the zone as defined when the watchpoint is + removed.</para> + <para> Write watchpoints might not be reported at the instruction + which is modifying the value unless option --vgdb=full is + given. Read watchpoints will always be reported at the exact + instruction reading the watched memory.</para> + <para> It is better to avoid using hardware watchpoint of not + addressable (yet) memory: in such a case, gdb will fallback to + extremely slow software watchpoints. Also, if you do not quit gdb + between two debugging sessions, the hardware watchpoints of the + previous sessions will be re-inserted as software watchpoints if + the watched memory zone is not addressable at program startup. + </para> + </listitem> + + <listitem> + <para> Stepping inside shared libraries on ARM.</para> + <para> For a not (yet?) clear reason, stepping inside a shared + library on ARM might fail. The bypass is to use the ldd command + to find the list of shared libraries and their loading address + and inform gdb of the loading address using the gdb command + 'add-symbol-file'. Example (for a ./p executable): + <programlisting><![CDATA[ +(gdb) shell ldd ./p + libc.so.6 => /lib/libc.so.6 (0x4002c000) + /lib/ld-linux.so.3 (0x40000000) +(gdb) add-symbol-file /lib/libc.so.6 0x4002c000 +add symbol table from file "/lib/libc.so.6" at + .text_addr = 0x4002c000 +(y or n) y +Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done. +(gdb) +]]></programlisting> + </para> + </listitem> + + <listitem> + <para> gdb version needed for ARM and PPC32/64.</para> + <para> You must use a gdb version which is able to read XML + target description sent by gdbserver (this is the standard setup + if the gdb was configured on a computer with the expat + library). If your gdb was not configured with XML support, it + will report an error message when using the target + command. Debugging will not work because gdb will then not be + able to fetch the registers from the Valgrind gdbserver. + </para> + </listitem> + + <listitem> + <para> Stack unwinding on PPC32/PPC64. </para> + <para> On PPC32/PPC64, stack unwinding for leaf functions + (i.e. functions not calling other functions) does work properly + only with <option>--vex-iropt-precise-memory-exns=yes</option> + </para> + </listitem> + + <listitem> + <para> Breakpoint encountered multiple times. </para> + <para> Some instructions (e.g. the x86 "rep movsb") + are translated by Valgrind using a loop. If a breakpoint is placed + on such an instruction, the breakpoint will be encountered + multiple times (i.e. once for each step of the "implicit" loop + implementing the instruction). + </para> + </listitem> + + <listitem> + <para> Execution of Inferior function calls by the Valgrind + gdbserver. </para> + + <para> gdb allows the user to "call" functions inside the process + being debugged. Such calls are named 'Inferior calls' in the gdb + terminology. A typical usage of an 'Inferior call' is to execute + a function that outputs a readable image of a complex data + structure. To make an Inferior call, use the gdb 'print' command + followed by the function to call and its arguments. As an + example, the following gdb command causes an Inferior call to the + libc printf function to be executed by (and inside) the process + being debugged: + </para> + <programlisting><![CDATA[ +(gdb) p printf("process being debugged has pid %d\n", getpid()) +$5 = 36 +(gdb) +]]></programlisting> + + <para>The Valgrind gdbserver accepts Inferior function + calls. During Inferior calls, the Valgrind tool will report + errors as usual. If you do not want to have such errors stopping + the execution of the Inferior call, you can use 'vg.set + vgdb-error' to set a big value before the call, and reset the + value after the Inferior call.</para> + + <para>To execute Inferior calls, gdb changes registers such as + the program counter, and then continues the execution of the + program. In a multi-thread program, all threads are continued, + not only the thread instructed to make an Inferior call. If + another thread reports an error or encounters a break, the + evaluation of the Inferior call is abandonned.</para> + + <para> Note that Inferior function calls is a powerful gdb + functionality but it has to be used with caution. For example, if + the program being debugged is stopped inside the function printf, + 'forcing' a recursive call to printf via an Inferior call will + very probably create problems. The Valgrind tool might also add + another level of complexity to Inferior calls, e.g. by reporting + tool errors during the Inferior call or due to the + instrumentation done. + </para> + + </listitem> + + <listitem> + <para>Connecting to or interrupting a Valgrind process blocked in + a system call.</para> + + <para> Connecting to or interrupting a Valgrind process blocked + in a system call is depending on ptrace system call, which might + be disabled on your kernel. </para> + + <para> At regular interval, after having executed some basic + blocks, the Valgrind scheduler checks if some input is to be + handled by the Valgrind gdbserver. However, this check is only + done if at least one thread of the process is executing (enough) + basic blocks. If all the threads of the process are blocked in a + system call, then no basic blocks are being executed, and the + Valgrind scheduler will not invoke the Valgrind gdbserver. In + such a case, the vgdb relay application will 'force' the Valgrind + gdbserver to be invoked, without the intervention of the Valgrind + scheduler. + </para> + + <para> Such forced invocation of the Valgrind gdbserver is + implemented by vgdb using ptrace system calls. On a properly + implemented kernel, the ptrace calls done by vgdb will not + influence the behaviour of the program running under Valgrind. In + case of unexpected impact, giving the option --max-invoke-ms=0 to + the vgdb relay application will disable the usage of ptrace + system call. The consequence of disabling ptrace system call in + vgdb is that a Valgrind process blocked in a system call cannot + be waken up or interrupted from gdb till it executes (enough) + basic blocks to let the scheduler poll invoke the gdbserver.. + </para> + <para>When ptrace is disabled in vgdb, you might increase the + responsiveness of the Valgrind gdbserver to commands or + interrupts by giving a lower value to the option --vgdb-poll: if + your application is most of the time blocked in a system call, + using a very low value for vgdb-poll will cause a faster + invocation of gdbserver. As the gdbserver poll done by the + scheduler is very efficient, the more frequent check by the + scheduler should not cause significant performance degradation. + </para> + <para>When ptrace is disabled in vgdb, a query packet sent by gdb + might take a significant time to be handled by the Valgrind + gdbserver. In such a case, gdb might encounter a protocol + timeout. To avoid having gdb encountering such a timeout error, + you can increase the value of this timeout by using the gdb + command 'set remotetimeout'. + </para> + + <para> Ubuntu version >= 10.10 can also restrict the scope of + ptrace to the children of the process calling ptrace. As the + Valgrind process is not a child of vgdb, such restricted scope + causes ptrace system call to fail. To avoid that, when Valgrind + gdbserver receives the first packet from a vgdb, it calls + prctl(PR_SET_PTRACER, vgdb_pid, 0, 0, 0) to ensure vgdb can use + ptrace. Once vgdb_pid has been set as ptracer, vgdb can then + properly force the invocation of Valgrind gdbserver when + needed. To ensure the vgdb is set as ptracer before the Valgrind + process could be blocked in a system call, connect your gdb to + the Valgrind gdbserver at startup (i.e. use --vgdb-error=0). + Note that this 'set ptracer' is not solving the problem for the + connection of a standalone vgdb: the first command to be sent by + a standalone vgdb must wake up the Valgrind process before + Valgrind gdbserver will set vgdb as ptracer. + </para> + + <para> Unblocking a process blocked in a system call is + not implemented on Darwin. So, waiting for vgdb on Darwin to + be enhanced, you cannot connect/interrupt a process blocked + in a system call on Darwin. + </para> + + </listitem> + + <listitem> + <para> Changing registers of a thread.</para> + <para> The Valgrind gdbserver only accepts to modify the values + of the registers of a thread when the thread is in status + Runnable or Yielding. In other states (typically, WaitSys), changing + registers values will not be accepted. This among others ensures + that Inferior calls are not executed for a thread which is in a + system call : the Valgrind gdbserver does not implement system + call restart. + </para> + </listitem> + + <listitem> + <para> gdb functionalities not supported.</para> + <para> gdb provides an awful lot of debugging functionalities. + At least the following are not supported: reversible debugging, + tracepoints. + </para> + </listitem> + + <listitem> + <para> Unknown limitations or problems.</para> + <para> The combination of gdb, Valgrind and the Valgrind + gdbserver has for sure some still unknown other + limitations/problems but we do not know about these unknown + limitations/problems :). If you encounter such (annoying) + limitations or problems, feel free to report a bug. But first + verify if the limitation or problem is not inherent to gdb or the + gdb remote protocol e.g. by checking the behaviour with the + standard gdbserver part of the gdb package. + </para> + </listitem> + + </itemizedlist> + +</sect2> + +</sect1> + +<sect1 id="manual-core.vgdb" + xreflabel="vgdb"> +<title>vgdb command line options</title> +<para> Usage: vgdb [OPTION]... [[-c] COMMAND]...</para> + +<para> vgdb (Valgrind to gdb) has two usages:</para> +<orderedlist> + <listitem id="manual-core.vgdb-standalone" xreflabel="vgdb standalone"> + <para>As a standalone utility, it is used from a shell command + line to send monitor commands to a process running under + Valgrind. For this usage, the vgdb OPTION(s) must be followed by + the monitor command to send. To send more than one command, + separate them with the -c option. + </para> + </listitem> + + <listitem id="manual-core.vgdb-relay" xreflabel="vgdb relay"> + <para>In combination with gdb 'target remote |' command, it is + used as the relay application between gdb and the Valgrind + gdbserver. For this usage, only OPTION(s) can be given, no + command can be given. + </para> + </listitem> + +</orderedlist> + +<para><computeroutput>vgdb</computeroutput> accepts the following +options:</para> +<itemizedlist> + <listitem> + <para><option>--pid=<number></option>: specifies the pid of + the process to which vgdb must connect to. This option is useful + in case more than one Valgrind gdbserver can be connected to. If + --pid argument is not given and multiple Valgrind gdbserver + processes are running, vgdb will report the list of such processes + and then exit.</para> + </listitem> + + <listitem> + <para><option>--vgdb-prefix</option> must be given to both + Valgrind and vgdb utility if you want to change the default prefix + for the FIFOs communication between the Valgrind gdbserver and + vgdb. </para> + </listitem> + + <listitem> + <para><option>--max-invoke-ms=<number></option> gives the + number of milli-seconds after which vgdb will force the invocation + of gdbserver embedded in valgrind. Default value is 100 + milli-seconds. A value of 0 disables the forced invocation. + </para> + + <para>If you specify a big value here, you might need to increase + the gdb remote timeout. The default value of the gdb remotetimeout + is 2 seconds. You should ensure that the gdb remotetimeout (in + seconds) is bigger than the max-invoke-ms value. For example, for + a 5000 --max-invoke-ms, the following gdb command will set a value + big enough: + <screen><![CDATA[ + (gdb) set remotetimeout 6 + ]]></screen> + </para> + </listitem> + + <listitem> + <para><option>--wait=<number></option> instructs vgdb to + check during the specified number of seconds if a Valgrind + gdbserver can be found. This allows to start a vgdb before the + Valgrind gdbserver is started. This option will be more useful in + combination with a --vgdb-prefix unique for the process you want + to wait for. Also, if you use the --wait argument in the gdb + 'target remote' command, you must set the gdb remotetimeout to a + value bigger than the --wait argument value. See option + --max-invoke-ms for an example of setting this remotetimeout + value.</para> + </listitem> + + <listitem> + <para><option>-c</option> To give more than one command, separate + the commands by an option -c. Example: + <screen><![CDATA[ +vgdb vg.set log_output -c mc.leak_check any +]]></screen></para> + </listitem> + + <listitem> + <para><option>-d</option> instructs vgdb to produce debugging + output. Give multiple -d args for more debug info.</para> + </listitem> + + <listitem> + <para><option>-D</option> instructs vgdb to show the state of the + shared memory used by the Valgrind gdbserver. vgdb will exit after + having shown the Valgrind gdbserver shared memory state.</para> + </listitem> +</itemizedlist> + +</sect1> + + +<sect1 id="manual-core.valgrind-monitor-commands" + xreflabel="Valgrind monitor commands"> +<title>Valgrind monitor commands</title> + +<para>The Valgrind monitor commands are available whatever the +tool. They can be sent either from a shell command line (using a +standalone vgdb) or from gdb (using the gdb 'monitor' command).</para> + +<itemizedlist> + <listitem> + <para><varname>help [debug]</varname> instructs Valgrind gdbserver + to give the list of all monitor commands of the Valgrind core and + of the tool. The optional 'debug' argument tells to also give help + for the monitor commands aimed at Valgrind internals debugging. + </para> + </listitem> + + <listitem> + <para><varname>vg.info all_errors</varname> shows all errors found + so far.</para> + </listitem> + <listitem> + <para><varname>vg.info last_error</varname> shows the last error + found.</para> + </listitem> + + <listitem> + <para><varname>vg.info n_errs_found</varname> shows the nr of + errors found so far and the current value of the --vgdb-error + argument.</para> + </listitem> + + <listitem> + <para><varname>vg.set {gdb_output | log_output | + mixed_output}</varname> allows to redirect the Valgrind output + (e.g. the errors detected by the tool). By default, the setting is + mixed_output. </para> + + <para>With mixed_output, the Valgrind output goes to the Valgrind + log (typically stderr) while the output of the interactive gdb + monitor commands (e.g. vg.info last_error) is displayed by + gdb.</para> + + <para>With gdb_output, both the Valgrind output and the + interactive gdb monitor commands output is displayed by + gdb.</para> + + <para>With log_output, both the Valgrind output and the + interactive gdb monitor commands output go to the Valgrind + log.</para> + </listitem> + + <listitem> + <para><varname>vg.wait [ms (default 0)]</varname> instructs + Valgrind gdbserver to sleep 'ms' milli-seconds and then + continue. When sent from a standalone vgdb, if this is the last + command, the Valgrind process will continue the execution of the + guest process. The typical usage of this is to use vgdb to send a + "no-op" command to a Valgrind gdbserver so as to continue the + execution of the guess process. + </para> + </listitem> + + <listitem> + <para><varname>vg.kill;</varname> requests the gdbserver to kill + the process. This can be used from a standalone vgdb to properly + kill a Valgrind process which is currently expecting a vgdb + connection.</para> + </listitem> + + <listitem> + <para><varname>vg.set vgdb-error <errornr></varname> + dynamically changes the value of the --vgdb-error argument. A + typical usage of this is to start with --vgdb-error=0 on the + command line, then set a few breakpoints, set the vgdb-error value + to a huge value and continue execution.</para> + </listitem> + +</itemizedlist> + +<para>The below Valgrind monitor commands are useful to investigate +the behaviour of Valgrind or Valgrind gdbserver in case of problem or +bug.</para> + +<itemizedlist> + + <listitem> + <para><varname>vg.info gdbserver_status</varname> shows the + gdbserver status. In case of problem (e.g. of communications), + this gives the value of some relevant Valgrind gdbserver internal + variables. Note that the variables related to breakpoints and + watchpoints (e.g. the nr of gdbserved addresses and the nr of + watchpoints) will be zero, as gdb by default removes all + watchpoints and breakpoints when execution stops, and re-inserts + them when resuming the execution of the debugged process. You can + change this gdb behaviour by using the gdb command 'set breakpoint + always-inserted on'. + </para> + </listitem> + + <listitem> + <para><varname>vg.info memory</varname> shows the statistics of + the Valgrind heap management. If + option <option>--profile-heap=yes=yes</option> was given, detailed + statistics will be output. + </para> + </listitem> + + <listitem> + <para><varname>vg.set debuglog <intvalue></varname> sets the + valgrind debug log level to <intvalue>. This allows to + dynamically change the log level of Valgrind e.g. when a problem + is detected.</para> + </listitem> + + <listitem> + <para><varname>vg.translate <address> + [<traceflags>]</varname> traces the translation of the block + containing address with the given trace flags. The traceflags is a + bit pattern similar to the --trace-flags option. It can be given + in hexadecimal (e.g. 0x20) or decimal (e.g. 32) or in binary 1s + and 0s bit (e.g. 0b00100000). The default value of the traceflags + is 0b00100000, corresponding to 'show after instrumentation'. Note + that the output of this command always goes to the Valgrind + log. The additional bit flag 0b100000000 traces in addition the + gdbserver specific instrumentation. Note that bit can only enable + the addition of the gdbserver instrumentation in the trace. + Keeping this flag to 0 will not disable the tracing of the + gdbserver instrumentation if it is active for another reason + (e.g. because there is a breakpoint at this address or because + gdbserver is in single stepping mode). </para> + </listitem> + +</itemizedlist> + +</sect1> <sect1 id="manual-core.pthreads" xreflabel="Support for Threads"> <title>Support for Threads</title> diff --git a/gdbserver_tests/Makefile.am b/gdbserver_tests/Makefile.am new file mode 100644 index 00000000..6d995190 --- /dev/null +++ b/gdbserver_tests/Makefile.am @@ -0,0 +1,86 @@ + +include $(top_srcdir)/Makefile.tool-tests.am + +dist_noinst_SCRIPTS = \ + invoker simulate_control_c make_local_links \ + filter_gdb filter_make_empty \ + filter_memcheck_monitor filter_stderr filter_vgdb + +EXTRA_DIST = \ + mcbreak.stderrB.exp \ + mcbreak.stderr.exp \ + mcbreak.stdinB.gdb \ + mcbreak.stdoutB.exp \ + mcbreak.stdout.exp \ + mcbreak.vgtest \ + mcclean_after_fork.stderrB.exp \ + mcclean_after_fork.stderr.exp \ + mcclean_after_fork.stdinB.gdb \ + mcclean_after_fork.stdoutB.exp \ + mcclean_after_fork.vgtest \ + mchelp.stderrB.exp \ + mchelp.stderr.exp \ + mchelp.stdoutB.exp \ + mchelp.vgtest \ + mcinfcallRU.stderrB.exp \ + mcinfcallRU.stderr.exp \ + mcinfcallRU.stdinB.gdb \ + mcinfcallRU.vgtest \ + mcinfcallWSRU.stderrB.exp \ + mcinfcallWSRU.stderr.exp \ + mcinfcallWSRU.stdinB.gdb \ + mcinfcallWSRU.vgtest \ + mcinvokeRU.stderrB.exp \ + mcinvokeRU.stderr.exp \ + mcinvokeRU.stdoutB.exp \ + mcinvokeRU.vgtest \ + mcinvokeWS.stderrB.exp \ + mcinvokeWS.stderr.exp \ + mcinvokeWS.stdoutB.exp \ + mcinvokeWS.vgtest \ + mcleak.stderrB.exp \ + mcleak.stderr.exp \ + mcleak.stdinB.gdb \ + mcleak.stdoutB.exp \ + mcleak.vgtest \ + mcsignopass.stderrB.exp \ + mcsignopass.stderr.exp \ + mcsignopass.stdinB.gdb \ + mcsignopass.stdoutB.exp \ + mcsignopass.vgtest \ + mcsigpass.stderrB.exp \ + mcsigpass.stderr.exp \ + mcsigpass.stdinB.gdb \ + mcsigpass.stdoutB.exp \ + mcsigpass.vgtest \ + mcvabits.stderrB.exp \ + mcvabits.stderr.exp \ + mcvabits.stdinB.gdb \ + mcvabits.stdoutB.exp \ + mcvabits.vgtest \ + mcwatchpoints.stderrB.exp \ + mcwatchpoints.stderr.exp \ + mcwatchpoints.stdinB.gdb \ + mcwatchpoints.stdoutB.exp \ + mcwatchpoints.vgtest \ + mssnapshot.stderrB.exp \ + mssnapshot.stderr.exp \ + mssnapshot.stdinB.gdb \ + mssnapshot.stdoutB.exp \ + mssnapshot.vgtest \ + nlcontrolc.stderrB.exp \ + nlcontrolc.stderr.exp \ + nlcontrolc.stdinB.gdb \ + nlcontrolc.stdoutB.exp \ + nlcontrolc.vgtest + +check_PROGRAMS = \ + clean_after_fork \ + sleepers \ + t \ + watchpoints + +AM_CFLAGS += $(AM_FLAG_M3264_PRI) +AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) + +LDADD = -lpthread diff --git a/gdbserver_tests/README_DEVELOPPERS b/gdbserver_tests/README_DEVELOPPERS new file mode 100644 index 00000000..e9c5e11a --- /dev/null +++ b/gdbserver_tests/README_DEVELOPPERS @@ -0,0 +1,203 @@ +Automatic regression tests: +--------------------------- +The Valgrind gdbserver automatic tests are by default +executed as part of the Valgrind test suite: + make regtest + +By default, these tests are running with the gdb found at +Valgrind configure time. + +If you want to test with another gdb version, you can do: + make regtest GDB=/path/to/another/gdb + +or (to just run the gdbserver tests with another gdb): + cd gdbserver_tests + make check + cd .. + gdbserver_tests/make_local_links /path/to/another/gdb + perl tests/vg_regtest gdbserver_tests + +The minimum version to use the Valgrind gdbserver is gdb >= 6.5. + Previous versions do not have the 'target remote |' command. It + would be possible to use an older version by having a tcp relay + application between gdb and vgdb (or, alternatively, change vgdb + so that it could read/write from/to a tcpip socket rather than + read/write from stdin/stdout. + +The tests have been run on various platforms using gdb 7.2 +and on some platforms gdb 7.0. +Some gdb tests implies a gdb >= 7.2. (these are automatically disabled +if testing with a lower version). + +Some tests implies to have a vgdb "ptrace invoker" capable. + +The prerequisite are established during make regtest (using marker files). +Each test verifies the prerequisite using the prereq: line. + + +Naming conventions: +------------------- + +The gdbserver tests are done with various Valgrind tools. A gdbserver +test using a tool xxxxxx should have its name starting with the 'short +two letters code' of this tool. For example, the test mcvabits.vgtest +is using Memcheck tool, while the test mssnapshot.vgtest is using +massif tool. + +Typically, a gdbserver test implies to launch two programs: + prog: a program running under valgrind + progB: another program (typically, either gdb or vgdb standalone) +The conventions about how to specify the 2nd program in a .vgtest +are explained in the file ../tests/vg_regtest.in +Many tests are using gdb as progB. The input for gdb is named +xxxxxx.stdinB.gdb. + +One day, we might imagine to run tests in parallel. +So, each test gets its own '--vgdb-prefix' argument. +This also help to avoid interactions between two successive tests: +if a previous test stayed blocked, the vgdb of the next test +will not report an error due to multiple possible FIFOs. + + +Rational for test approach +-------------------------- +Two approaches have been looked at: + V: use the 'vg_regtest' approach used by the rest of Valgrind tests + G: use the gdb Dejagnu test framework. + +Advantages of V: much simpler that G, known by Valgrind developpers, +no additional dependency for the Valgrind build and test. + +Disadvantages of V: not well suited to testing of interactive tools, +very unflexible way to test the results (everything is in "template" +files), templates contains often irrelevant data for the test but it +can make the test fail. After writing 13 tests, it looks like the +template diff approach is quite fragile (e.g. changing the gdb version +and/or the OS version influences the output of irrelevant things which +are part of the template). + +A more flexible template filtering is needed. +Maybe something like: +The program under test is outputting its instructions to be filtered in +special markers e.g. +#pushf filter_addresses | filter_messages +... some output +#pushf an_additional_filter +... some other output, filtered by both the first and second push +#popf +... here output filtered only by the first pushf +#popf + +Advantages of G: much more powerful, well suited to test a gdb with a +gdbserver, tests can verify specifically some output without being +impacted by other output, allow to test Valgrind gdbserver with the +all of the gdb test suite (but a lot of tests will rather test gdb +than Valgrind gdbserver). + +Disadvantages: not an easy beast to understand and master, running the +whole gdb testsuite with Valgrind gdbserver looks to be a challenge. + +Currently, tests are written with vg_regtest. Approach G will be looked at it +again (e.g. to complement V) once a basic set of tests are available. + + +Manual tests still to automate: +------------------------------- + +Validate monitor commands abbreviation recognition +*************************************************** +mo vg.info all_errors # to show all errors recorded so far +mo vg.i a # the same +mo v # must give an error: v can match vg.set vg.info +mo vg # the same vg +mo vg. # the same vg. + +test of gdb detaching or dying +****************************** +valgrind --vgdb=yes --vgdb-error=0 --vgdb-poll=500 ./t + +in another window + +gdb ./t +set remotetimeout 100 +target remote|vgdb +detach valgrind continues +target remote|vgdb reattach +detach valgrind continues +target remote|vgdb reattach +monitor vg.wait no effect + + + +test of valgrind/gdb output redirection +*************************************** +valgrind --vgdb=yes --vgdb-error=1 --vgdb-poll=500 ./t + +in another window + +**** command to type*** ****** expected behaviour +gdb ./t +set remotetimeout 1000 +target remote | vgdb +mo vg.set vgdb-error 1000 # so that valgrind does stop only at error 1000 and after +mo vg.set gdb_output # to have further valgrind errors output in gdb +c # continue, some errors will appear +C-c # interrupt program +mo vg.set log_output # to set back the valgrind output to normal process log output +mo mc.l # leak output to appear in log of process +mo vg.set mixed_output +mo mc.l # leak output to appear in gdb + + + +test with a big executable: firefox +*********************************** +valgrind --vgdb=yes --vgdb-error=1000 --vgdb-poll=50000 --trace-children=yes firefox 2>&1 | tee f.out + +wait for some messages from the "big" firefox executable to appear. +Then: + +gdb /usr/lib/firefox-3.5/firefox +target remote | vgdb +... then you can do various next/print/bt/bt full/break/... to see it is working + +bulk test with the above +************************ +to verify there is no race condition/no reentrance problem +between gdbserver code and valgrind: +start firefox like in the previous test. +In another window, do: + while true + do + vgdb mc.leak + sleep 1 + done + +NB: this will make firefox run extremely slow, as it will do a leak +search every second. + + +Test of "jump" functionality +---------------------------- +... to be done : put two breaks, jump over one. +... same but when error is encountered + + +* test with --max-invoke-ms=0 +----------------------------- +valgrind --vgdb=yes ./t +... wait till you see "petachounok sleeping 4 of 15 +then try to gdb it + +!!!! this often causes gdb to report a protocol timeout. +use gdb set remotetimeout <a big time> to avoid that. +The symptoms of a timeout are: + (gdb) tar rem|vgdb --max-invoke-ms=0 + Remote debugging using |vgdb --max-invoke-ms=0 + relaying data between gdb and process 2930 + Ignoring packet error, continuing... + warning: unrecognized item "timeout" in "qSupported" response + +* tests of shadow registers +---------------------------- +Show/modify shadow registers diff --git a/gdbserver_tests/clean_after_fork.c b/gdbserver_tests/clean_after_fork.c new file mode 100644 index 00000000..3ef0f67d --- /dev/null +++ b/gdbserver_tests/clean_after_fork.c @@ -0,0 +1,34 @@ +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +main() +{ + int mem = 0; + int pid; + + pid = fork(); + if (pid == -1) { + mem = 1; + perror("fork"); + exit(1); + } + + if (pid == 0) { + if (mem == 0) + exit(0); + else + exit(1); + } else { + int ret; + int status; + while((ret = waitpid(pid, &status, 0)) != pid) { + if (errno != EINTR) { + perror("waitpid"); + exit(1); + } + } + mem = status; + } + if (mem == 0) + printf("mem is zero\n"); +} diff --git a/gdbserver_tests/filter_gdb b/gdbserver_tests/filter_gdb new file mode 100755 index 00000000..9dd7a3b4 --- /dev/null +++ b/gdbserver_tests/filter_gdb @@ -0,0 +1,95 @@ +#! /bin/sh + +# filter the output of gdb. + +dir=`dirname $0` + +$dir/filter_stderr | + +# Anonymise addresses +$dir/../tests/filter_addresses | + +# memcheck stuff +$dir/filter_memcheck_monitor | + + +# Anonymise or remove : +# initial tty control character sent by gdb 7.0 +# remove missing debuginfos +# vgdb message +# pid numbers +# Thread numbers +# delete thread switches +# info threads output (e.g. which thread is running and syscall) +# delete Reading symbols file lines +# delete Loaded symbols file lines +# initial break message +# remove gdb prompts. +# remove gdb continuation prompts. +# remove gdb done prompts. +# a 'general' system calls stack trace part +# a more specialised system call select stack trace part +# (on 32 bits, we have an int_80, on 64 bits, directly select) +# and yet another (gdb 7.0 way) to get a system call +# and yet another (gdb 7.0 arm way) to get a system call +# and cleanup some lines for a system call (on ubuntu 10 64 bits) +# (pay attention : there are tab characters there in) +# + yet another way to get a select system call +# which registers can't be modified +# special transform for arm/ppc watchpoints which have an additional address +# at the beginning +# special transform for backtrace of arm division by zero which gives +# a location in the nptl lib rather than our sources (same as +# standard gdb gdbserver) gdb 7.0 +# same special transform but for gdb 7.2 +# delete lines telling that some memory can't be accessed: this is +# a.o. produced by gdb 7.2 on arm (same with standard gdbserver) +# delete empty lines (the last line (only made of prompts) sometimes +# finishes with a new line, sometimes not ???). +sed -e 's/^\[?1034hReading symbols/Reading symbols/' \ + -e '/Missing separate debuginfos, use: debuginfo-install/d' \ + -e 's/\(relaying data between gdb and process \)[0-9][0-9]*/\1..../' \ + -e 's/pid [0-9][0-9]*/pid ..../g' \ + -e 's/Thread [0-9][0-9]*/Thread ..../g' \ + -e '/\[Switching to Thread ....\]/d' \ + -e 's/^\([ \* ] [0-9] Thread .... (tid [0-9] VgTs_WaitSys) 0x........ in\).*$/\1 syscall .../' \ + -e 's/#[0-9]\( 0x........ in sleeper_or_burner\)/#.\1/' \ + -e '/^Reading symbols from .*\.\.\.done\./d' \ + -e '/^Loaded symbols for .*$/d' \ + -e '1,8s/_start () from [^ ][^ ]*/_start () from ...start file.../' \ + -e '1,8s/\(0x........ in\) ?? () from \/lib\/ld-linux\.so\../\1 _start () from ...start file.../' \ + -e '1,8s/\(0x........ in\) ?? () from \/lib\/ld\.so\../\1 _start () from ...start file.../' \ + -e '1,8s/\(0x........ in\) ?? () from \/lib64\/ld64\.so\../\1 _start () from ...start file.../' \ + -e '1,8s/\(0x........ in\) ?? () from \/lib64\/ld-linux-x86-64\.so\../\1 _start () from ...start file.../' \ + -e '1,8s/\(0x........ in\) ?? ()$/\1 _start () from ...start file.../' \ + -e 's/(gdb) //g' \ + -e 's/^>[> ]*//' \ + -e '/^done\.$/d' \ + -e 's/in _dl_sysinfo_int80 () from \/lib\/ld-linux.so.*/in syscall .../' \ + -e 's/in _dl_sysinfo_int80 ()/in syscall .../' \ + -e '/^ from \/lib\/ld-linux.so.*$/d' \ + -e 's/\(0x........\) in ?? () from \/lib\/ld-linux\.so\../\1 in syscall .../' \ + -e 's/\(0x........\) in ?? () from \/lib64\/tls\/libc\.so\../\1 in syscall .../' \ + -e 's/in \(.__\)\{0,1\}select () from \/.*$/in syscall .../' \ + -e '/^ from \/lib\/libc.so.*$/d' \ + -e 's/in select ()$/in syscall .../' \ + -e 's/in select () at \.\.\/sysdeps\/unix\/syscall-template\.S.*$/in syscall .../' \ + -e '/^[ ]*at \.\.\/sysdeps\/unix\/syscall-template\.S/d' \ + -e '/^[ ]*in \.\.\/sysdeps\/unix\/syscall-template\.S/d' \ + -e '/^[1-9][0-9]*[ ]*\.\.\/sysdeps\/unix\/syscall-template\.S/d' \ + -e '/^[1-9][0-9]*[ ]in *\.\.\/sysdeps\/unix\/syscall-template\.S/d' \ + -e 's/\(Could not write register \)".*"/\1 "xxx"/' \ + -e 's/\(ERROR changing register \).*$/\1 xxx regno y/' \ + -e 's/0x........ in \(main (argc=1, argv=0x........) at watchpoints.c:[24][3689]\)/\1/' \ + -e 's/0x........ in \(main () at clean_after_fork.c:32\)/\1/' \ + -e 's/0x........ in \*__GI_raise (sig=8)/0x........ in test4 () at faultstatus.c:120/' \ + -e 's/ at ..\/nptl\/sysdeps\/unix\/sysv\/linux\/raise.c:[0-9]*/120 volatile int v = 44\/zero();/' \ + -e '/ in ..\/nptl\/sysdeps\/unix\/sysv\/linux\/raise.c/d' \ + -e 's/0x........ in \.\{0,1\}raise () from \/lib[0-9]\{0,2\}\/libc\.so\../0x........ in test4 () at faultstatus.c:120\n120 volatile int v = 44\/zero();'/ \ + -e '/Cannot access memory at address 0x......../d' \ + -e '/^$/d' | + +# join together two lines that gdb 7.1 splits in two (???) +# (in a separate sed, as the below influences the behaviour of the other expressions) +sed -e :a -e '$!N;s/\n at sleepers.c:39/ at sleepers.c:39/;ta' -e 'P;D' + diff --git a/gdbserver_tests/filter_make_empty b/gdbserver_tests/filter_make_empty new file mode 100755 index 00000000..f22998b7 --- /dev/null +++ b/gdbserver_tests/filter_make_empty @@ -0,0 +1,10 @@ +#! /bin/sh + +# A filter filtering everything. + +# To still allow to see what is happening, the content +# is copied to a file, together with date and process. + +ps -lf -p $PPID >> garbage.filtered.out +date >> garbage.filtered.out +cat >> garbage.filtered.out diff --git a/gdbserver_tests/filter_memcheck_monitor b/gdbserver_tests/filter_memcheck_monitor new file mode 100755 index 00000000..9a503369 --- /dev/null +++ b/gdbserver_tests/filter_memcheck_monitor @@ -0,0 +1,16 @@ +#! /bin/sh + +# used to filter memcheck output shown by vgdb. + +dir=`dirname $0` + +$dir/../memcheck/tests/filter_stderr | + +# filter vgdb messages +$dir/filter_vgdb | + + +# Bypass a s390x kernel bug which makes faultstatus test3 fail. In our case, we are +# not interested in checking the si_code, but rather the signal passing +# in mcsig(no)pass +sed -e 's/Test 3: FAIL: expected si_code==2, not 128/Test 3: PASS/' diff --git a/gdbserver_tests/filter_stderr b/gdbserver_tests/filter_stderr new file mode 100755 index 00000000..1d26ab79 --- /dev/null +++ b/gdbserver_tests/filter_stderr @@ -0,0 +1,6 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../tests/filter_stderr_basic | +sed -e '/^Copyright (C) /d' diff --git a/gdbserver_tests/filter_vgdb b/gdbserver_tests/filter_vgdb new file mode 100755 index 00000000..71b35119 --- /dev/null +++ b/gdbserver_tests/filter_vgdb @@ -0,0 +1,15 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/filter_stderr | + +# Anonymise addresses +$dir/../tests/filter_addresses | + +# filter vgdb process id, pid +# gdb 7.2 sometimes tries to access address 0x0 +# (same as with standard gdbserver) +sed -e 's/\(relaying data between gdb and process \)[0-9][0-9]*/\1..../' \ + -e 's/\(sending command .* to pid \)[0-9][0-9]*/\1..../' \ + -e '/Cannot access memory at address 0x......../d' diff --git a/gdbserver_tests/invoker b/gdbserver_tests/invoker new file mode 100755 index 00000000..b9fba58b --- /dev/null +++ b/gdbserver_tests/invoker @@ -0,0 +1,19 @@ +#! /bin/sh + +# invoker is used to test the invocation of gdbserver. +# The first argument is the nr of times vgdb has to be called. +# rest of args are given to vgdb +# At the end of the loop, an additional call is done +# but adding " -c vg.kill" to kill Valgrind process. + +LOOPS=$1 +shift + +i=0 +while [ $i -lt $LOOPS ] +do + ./vgdb "$@" + i=`expr $i + 1` +done + +./vgdb "$@" -c vg.kill diff --git a/gdbserver_tests/make_local_links b/gdbserver_tests/make_local_links new file mode 100755 index 00000000..f7291d65 --- /dev/null +++ b/gdbserver_tests/make_local_links @@ -0,0 +1,58 @@ +#! /bin/sh + +# (must be called from the valgrind top source dir). +# +# Make local links in the gdbserver_tests directory +# so that tests needing gdb can be disabled if +# a tool old version of gdb is provided or if no gdb is +# provided. +# +# The vgdb link is needed either for gdb tests +# or for standalone vgdb tests. + +ln -f -s $1 gdbserver_tests/gdb +if [ -x gdbserver_tests/gdb ] +then + # Try to extract the gdb version. + VERSIONLINE=`gdbserver_tests/gdb --version | head -1` + VERSION=`echo $VERSIONLINE | + sed -e 's/[^0-9\.]//g' -e 's/\./ /g'` + + # We need at least a 6.5 version to run any gdb test + VERSIONOK=`echo $VERSION | + awk '{ if ( ($1 >= 7) || (($1 == 6) && ($2 >= 5)) ) print "version ok"}'` + if [ "$VERSIONOK" = "" ] + then + echo "gdbserver tests suppressed as $1 version is not >= 6.5: " $VERSIONLINE + rm gdbserver_tests/gdb + fi + + # We need at least a 7.2 version for gdb tests using eval command + VERSIONOK=`echo $VERSION | + awk '{ if ( ($1 >= 8) || (($1 == 7) && ($2 >= 2)) ) print "version ok"}'` + if [ "$VERSIONOK" = "" ] + then + echo "gdbserver eval tests suppressed as $1 version is not >= 7.2: " $VERSIONLINE + rm -f gdbserver_tests/gdb.eval + else + touch gdbserver_tests/gdb.eval + fi +else + echo "gdbserver gdb tests suppressed as $1 is not executable" +fi + +ln -f -s ../coregrind/vgdb gdbserver_tests/vgdb + +# if ptrace not implemented in vgdb or OS restricts the initial attach, +# some tests would block for a loooonnnng time. +if gdbserver_tests/vgdb --help 2>&1 | + grep -e 'ptrace invoker not implemented' \ + -e 'kernel restricts ptrace invoker' > /dev/null +then + rm -f gdbserver_tests/vgdb.ptraceinvoker +else + touch gdbserver_tests/vgdb.ptraceinvoker +fi + +# cleanup the possibly big garbage previously collected output +rm -f gdbserver_tests/garbage.filtered.out diff --git a/gdbserver_tests/mcbreak.stderr.exp b/gdbserver_tests/mcbreak.stderr.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcbreak.stderr.exp diff --git a/gdbserver_tests/mcbreak.stderrB.exp b/gdbserver_tests/mcbreak.stderrB.exp new file mode 100644 index 00000000..d5eaecf1 --- /dev/null +++ b/gdbserver_tests/mcbreak.stderrB.exp @@ -0,0 +1,7 @@ +relaying data between gdb and process .... +vgdb-error value changed from 0 to 999999 +vgdb-error value changed from 999999 to 0 +n_errs_found 1 (vgdb-error 0) +vgdb-error value changed from 0 to 0 +monitor command request to kill this process +Remote connection closed diff --git a/gdbserver_tests/mcbreak.stdinB.gdb b/gdbserver_tests/mcbreak.stdinB.gdb new file mode 100644 index 00000000..a1549256 --- /dev/null +++ b/gdbserver_tests/mcbreak.stdinB.gdb @@ -0,0 +1,68 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcbreak +monitor vg.set vgdb-error 999999 +# +define checkstep + set $old_pc=$pc + step + if $old_pc == $pc + echo Bizarre the oldpc has not changed after step\n + print $oldpc + print $pc + else + echo old_pc has changed after step\n + end +end +# +# break1 and break2 +break t.c:112 +break t.c:117 +# +continue +# first break encountered. +checkstep +checkstep +checkstep +# +monitor vg.set vgdb-error 0 +# +next +print whoami("first") +print undefined +print i +checkstep +checkstep +next +print whoami("second") +print undefined +print i +next +print whoami("third") +print undefined +print i +next +print whoami("fourth") +print undefined +print i +# modify sleeps so as to have a shorter test: +print sleeps=1 +# +print whoami("after next: inferior call pushed from mcbreak.stdinB.gdb") +continue +# +# encountered second break +step +finish +# delete all breaks +delete +continue +monitor vg.info n_errs_found +# inferior call "in the middle" of an instruction is not working at least +# on all platforms, so comment the below. +# print whoami("after error: inferior call pushed from mcbreak.stdinB.gdb") +checkstep +monitor vg.set vgdb-error 0 +continue +# stop the process a.o. to avoid non deterministic output +monitor vg.kill +quit diff --git a/gdbserver_tests/mcbreak.stdout.exp b/gdbserver_tests/mcbreak.stdout.exp new file mode 100644 index 00000000..8907c6fb --- /dev/null +++ b/gdbserver_tests/mcbreak.stdout.exp @@ -0,0 +1,8 @@ +pid .... Thread .... first +pid .... Thread .... second +pid .... Thread .... third +pid .... Thread .... fourth +pid .... Thread .... after next: inferior call pushed from mcbreak.stdinB.gdb +pid .... Thread .... called from level +called from level int_und is not zero +pid .... Thread .... called from main diff --git a/gdbserver_tests/mcbreak.stdoutB.exp b/gdbserver_tests/mcbreak.stdoutB.exp new file mode 100644 index 00000000..2c51b39d --- /dev/null +++ b/gdbserver_tests/mcbreak.stdoutB.exp @@ -0,0 +1,56 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcbreak +0x........ in _start () from ...start file... +Breakpoint 1 at 0x........: file t.c, line 112. +Breakpoint 2 at 0x........: file t.c, line 117. +Continuing. +Breakpoint 1, main (argc=1, argv=0x........) at t.c:112 +112 breakme(__LINE__); //break1 +breakme (line=112) at t.c:100 +100 if (line > 1000) +old_pc has changed after step +102 } +old_pc has changed after step +main (argc=1, argv=0x........) at t.c:113 +113 for (i = len-1; i >= 0; i=i-2) +old_pc has changed after step +114 undefined[i] = undef; +$1 = void +$2 = "undefined" +$3 = 8 +113 for (i = len-1; i >= 0; i=i-2) +old_pc has changed after step +114 undefined[i] = undef; +old_pc has changed after step +113 for (i = len-1; i >= 0; i=i-2) +$4 = void +$5 = "undefi?e?" +$6 = 6 +114 undefined[i] = undef; +$7 = void +$8 = "undefi?e?" +$9 = 4 +113 for (i = len-1; i >= 0; i=i-2) +$10 = void +$11 = "unde?i?e?" +$12 = 4 +$13 = 1 +$14 = void +Continuing. +Breakpoint 2, main (argc=1, argv=0x........) at t.c:117 +117 breakme(__LINE__); //break2 +breakme (line=117) at t.c:100 +100 if (line > 1000) +Run till exit from #0 breakme (line=117) at t.c:100 +main (argc=1, argv=0x........) at t.c:119 +119 if (argc > 1) +Delete all breakpoints? (y or n) [answered Y; input not from terminal] +Continuing. +Program received signal SIGTRAP, Trace/breakpoint trap. +0x........ in make_error (s=0x........ "called from level") at t.c:40 +40 if (int_und == 0) +43 printf ("%s int_und is not zero\n", s); +old_pc has changed after step +Continuing. +Program received signal SIGTRAP, Trace/breakpoint trap. +0x........ in make_error (s=0x........ "called from main") at t.c:40 +40 if (int_und == 0) diff --git a/gdbserver_tests/mcbreak.vgtest b/gdbserver_tests/mcbreak.vgtest new file mode 100644 index 00000000..b79f41a1 --- /dev/null +++ b/gdbserver_tests/mcbreak.vgtest @@ -0,0 +1,11 @@ +# test execution control (break, next, step) and inferior calls +# when stopped on these events +prog: t +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcbreak +stdout_filter: filter_gdb +stderr_filter: filter_make_empty +progB: gdb +argsB: --quiet -l 60 --nx ./t +stdinB: mcbreak.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_memcheck_monitor diff --git a/gdbserver_tests/mcclean_after_fork.stderr.exp b/gdbserver_tests/mcclean_after_fork.stderr.exp new file mode 100644 index 00000000..0e8e017b --- /dev/null +++ b/gdbserver_tests/mcclean_after_fork.stderr.exp @@ -0,0 +1,12 @@ + +(action at startup) vgdb me ... + +HEAP SUMMARY: + in use at exit: 0 bytes in 0 blocks + total heap usage: 0 allocs, 0 frees, 0 bytes allocated + +For a detailed leak analysis, rerun with: --leak-check=full + +For counts of detected and suppressed errors, rerun with: -v +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +Reset valgrind output to log (orderly_finish) diff --git a/gdbserver_tests/mcclean_after_fork.stderrB.exp b/gdbserver_tests/mcclean_after_fork.stderrB.exp new file mode 100644 index 00000000..995b42fd --- /dev/null +++ b/gdbserver_tests/mcclean_after_fork.stderrB.exp @@ -0,0 +1,4 @@ +relaying data between gdb and process .... +vgdb-error value changed from 0 to 999999 +monitor command request to kill this process +Remote connection closed diff --git a/gdbserver_tests/mcclean_after_fork.stdinB.gdb b/gdbserver_tests/mcclean_after_fork.stdinB.gdb new file mode 100644 index 00000000..145ad939 --- /dev/null +++ b/gdbserver_tests/mcclean_after_fork.stdinB.gdb @@ -0,0 +1,24 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcclean_after_fork +monitor vg.set vgdb-error 999999 +# +# put a break in main, and then a watch +# also put breaks in code that only the child will execute. +# These breaks should not be encountered. +break clean_after_fork.c:9 +break clean_after_fork.c:18 +break clean_after_fork.c:20 +# +continue +# first break encountered. +# put a read watchpoint on mem +# we expect that the read watchpoint is not triggered in the child +# (as we expect it will be cleared at fork). +rwatch mem +# +continue +# +# we should now have encountered the read watchpoint in the parent. +# let's kill the parent: +monitor vg.kill +quit diff --git a/gdbserver_tests/mcclean_after_fork.stdoutB.exp b/gdbserver_tests/mcclean_after_fork.stdoutB.exp new file mode 100644 index 00000000..8c6a8d37 --- /dev/null +++ b/gdbserver_tests/mcclean_after_fork.stdoutB.exp @@ -0,0 +1,14 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcclean_after_fork +0x........ in _start () from ...start file... +Breakpoint 1 at 0x........: file clean_after_fork.c, line 9. +Breakpoint 2 at 0x........: file clean_after_fork.c, line 18. +Breakpoint 3 at 0x........: file clean_after_fork.c, line 20. +Continuing. +Breakpoint 1, main () at clean_after_fork.c:9 +9 pid = fork(); +Hardware read watchpoint 4: mem +Continuing. +Hardware read watchpoint 4: mem +Value = 0 +main () at clean_after_fork.c:32 +32 if (mem == 0) diff --git a/gdbserver_tests/mcclean_after_fork.vgtest b/gdbserver_tests/mcclean_after_fork.vgtest new file mode 100644 index 00000000..d953ed79 --- /dev/null +++ b/gdbserver_tests/mcclean_after_fork.vgtest @@ -0,0 +1,9 @@ +# test cleanup of break and watchpoints after fork +prog: clean_after_fork +vgopts: --tool=memcheck --vgdb=full --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcclean_after_fork +stderr_filter: filter_memcheck_monitor +progB: gdb +argsB: --quiet -l 60 --nx ./clean_after_fork +stdinB: mcclean_after_fork.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_memcheck_monitor diff --git a/gdbserver_tests/mchelp.stderr.exp b/gdbserver_tests/mchelp.stderr.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mchelp.stderr.exp diff --git a/gdbserver_tests/mchelp.stderrB.exp b/gdbserver_tests/mchelp.stderrB.exp new file mode 100644 index 00000000..55888e32 --- /dev/null +++ b/gdbserver_tests/mchelp.stderrB.exp @@ -0,0 +1,5 @@ +sending command help to pid .... +sending command help debug to pid .... +sending command vg.kill to pid .... +readchar: Got EOF +error reading packet diff --git a/gdbserver_tests/mchelp.stdoutB.exp b/gdbserver_tests/mchelp.stdoutB.exp new file mode 100644 index 00000000..6499f334 --- /dev/null +++ b/gdbserver_tests/mchelp.stdoutB.exp @@ -0,0 +1,66 @@ +general valgrind monitor commands: + help [debug] : monitor command help. With debug: + debugging commands + vg.wait [<ms>] : sleep <ms> (default 0) then continue + vg.info all_errors : show all errors found so far + vg.info last_error : show last error found + vg.info n_errs_found : show the nr of errors found so far + vg.kill : kill the Valgrind process + vg.set gdb_output : set valgrind output to gdb + vg.set log_output : set valgrind output to log + vg.set mixed_output : set valgrind output to log, interactive output to gdb + vg.set vgdb-error <errornr> : debug me at error >= <errornr> + +memcheck monitor commands: + mc.get_vbits <addr> [<len>] + returns validity bits for <len> (or 1) bytes at <addr> + bit values 0 = valid, 1 = invalid, __ = unaddressable byte + Example: mc.get_vbits 0x........ 10 + mc.make_memory [noaccess|undefined + |defined|ifaddressabledefined] <addr> [<len>] + mark <len> (or 1) bytes at <addr> with the given accessibility + mc.check_memory [addressable|defined] <addr> [<len>] + check that <len> (or 1) bytes at <addr> have the given accessibility + and outputs a description of <addr> + mc.leak_check [full*|summary] + [reachable|leakpossible*|definiteleak] + * = defaults + Examples: mc.leak_check + mc.leak_check any summary + +general valgrind monitor commands: + help [debug] : monitor command help. With debug: + debugging commands + vg.wait [<ms>] : sleep <ms> (default 0) then continue + vg.info all_errors : show all errors found so far + vg.info last_error : show last error found + vg.info n_errs_found : show the nr of errors found so far + vg.kill : kill the Valgrind process + vg.set gdb_output : set valgrind output to gdb + vg.set log_output : set valgrind output to log + vg.set mixed_output : set valgrind output to log, interactive output to gdb + vg.set vgdb-error <errornr> : debug me at error >= <errornr> +debugging valgrind internals monitor commands: + vg.info gdbserver_status : show gdbserver status + vg.info memory : show valgrind heap memory stats + vg.set debuglog <level> : set valgrind debug log level to <level> + vg.translate <addr> [<traceflags>] : debug translation of <addr> with <traceflags> + (default traceflags 0b00100000 : show after instrumentation) + An additional flag 0b100000000 allows to show gdbserver instrumentation + +memcheck monitor commands: + mc.get_vbits <addr> [<len>] + returns validity bits for <len> (or 1) bytes at <addr> + bit values 0 = valid, 1 = invalid, __ = unaddressable byte + Example: mc.get_vbits 0x........ 10 + mc.make_memory [noaccess|undefined + |defined|ifaddressabledefined] <addr> [<len>] + mark <len> (or 1) bytes at <addr> with the given accessibility + mc.check_memory [addressable|defined] <addr> [<len>] + check that <len> (or 1) bytes at <addr> have the given accessibility + and outputs a description of <addr> + mc.leak_check [full*|summary] + [reachable|leakpossible*|definiteleak] + * = defaults + Examples: mc.leak_check + mc.leak_check any summary + +monitor command request to kill this process diff --git a/gdbserver_tests/mchelp.vgtest b/gdbserver_tests/mchelp.vgtest new file mode 100644 index 00000000..9f88c498 --- /dev/null +++ b/gdbserver_tests/mchelp.vgtest @@ -0,0 +1,9 @@ +# test the memcheck monitor help +prog: t +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mchelp +stdout_filter: filter_make_empty +stderr_filter: filter_make_empty +progB: vgdb +argsB: --wait=60 --vgdb-prefix=./vgdb-prefix-mchelp -c help -c help debug -c vg.kill +stdoutB_filter: filter_memcheck_monitor +stderrB_filter: filter_vgdb diff --git a/gdbserver_tests/mcinfcallRU.stderr.exp b/gdbserver_tests/mcinfcallRU.stderr.exp new file mode 100644 index 00000000..c1dd15b0 --- /dev/null +++ b/gdbserver_tests/mcinfcallRU.stderr.exp @@ -0,0 +1,7 @@ +loops/sleep_ms/burn/threads_spec: 1 0 2000000000 B-B-B-B- +Brussels ready to sleep and/or burn +London ready to sleep and/or burn +Petaouchnok ready to sleep and/or burn +main ready to sleep and/or burn +pid .... Thread .... inferior call pushed from gdb in mcinfcallRU.stdinB.gdb +Reset valgrind output to log (orderly_finish) diff --git a/gdbserver_tests/mcinfcallRU.stderrB.exp b/gdbserver_tests/mcinfcallRU.stderrB.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcinfcallRU.stderrB.exp diff --git a/gdbserver_tests/mcinfcallRU.stdinB.gdb b/gdbserver_tests/mcinfcallRU.stdinB.gdb new file mode 100644 index 00000000..08a149b2 --- /dev/null +++ b/gdbserver_tests/mcinfcallRU.stdinB.gdb @@ -0,0 +1,21 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcinfcallRU +monitor vg.set vgdb-error 999999 +# +# We will interrupt in a few seconds (be sure all tasks are in +# Runnable/Yielding state). We need to wait enough seconds to be sure +# Valgrind has started to execute the threads. +# On a heavily loaded slow arm gcc compile farm system, 5 seconds +# was not enough. +shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-mcinfcallRU 10 +# +continue +info threads +thread apply all bt full +# Would like to call this for all threads with 'thread apply all', but due to unfair scheduling, +# the below can either take a long time and/or have threads finished +# before they have a chance to execute the whoami +# thread apply all +print whoami("inferior call pushed from gdb in mcinfcallRU.stdinB.gdb") +monitor vg.kill +quit diff --git a/gdbserver_tests/mcinfcallRU.vgtest b/gdbserver_tests/mcinfcallRU.vgtest new file mode 100644 index 00000000..957458bc --- /dev/null +++ b/gdbserver_tests/mcinfcallRU.vgtest @@ -0,0 +1,15 @@ +# test inferior calls when all threads are in Runnable or Yielding mode +prog: sleepers +args: 1 0 2000000000 B-B-B-B- +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcinfcallRU +# filter_gdb to replace pid and Thread numbers. +# Well, the test will verify we have 4 calls (for each thread) +# but no way to check that this is effectively in the 4 different threads. +stderr_filter: filter_gdb +# Disable on Darwin: inferior call rejected as it cannot find malloc. +prereq: ../tests/os_test linux +progB: gdb +argsB: --quiet -l 60 --nx ./sleepers +stdinB: mcinfcallRU.stdinB.gdb +stdoutB_filter: filter_make_empty +stderrB_filter: filter_make_empty diff --git a/gdbserver_tests/mcinfcallWSRU.stderr.exp b/gdbserver_tests/mcinfcallWSRU.stderr.exp new file mode 100644 index 00000000..227f862d --- /dev/null +++ b/gdbserver_tests/mcinfcallWSRU.stderr.exp @@ -0,0 +1,8 @@ +loops/sleep_ms/burn/threads_spec: 100 100000000 1000000000 -S-SB-B- +Brussels ready to sleep and/or burn +London ready to sleep and/or burn +Petaouchnok ready to sleep and/or burn +main ready to sleep and/or burn +pid .... Thread .... thread 1 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb +pid .... Thread .... thread 4 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb +Reset valgrind output to log (orderly_finish) diff --git a/gdbserver_tests/mcinfcallWSRU.stderrB.exp b/gdbserver_tests/mcinfcallWSRU.stderrB.exp new file mode 100644 index 00000000..2e954dbd --- /dev/null +++ b/gdbserver_tests/mcinfcallWSRU.stderrB.exp @@ -0,0 +1,48 @@ +relaying data between gdb and process .... +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU +0x........ in _start () from ...start file... +vgdb-error value changed from 0 to 999999 +Breakpoint 1 at 0x........: file sleepers.c, line 72. +Continuing. +[New Thread ....] +Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72 +72 int i = 0; +Continuing. +[New Thread ....] +Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72 +72 int i = 0; +Continuing. +[New Thread ....] +Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72 +72 int i = 0; +Continuing. +Breakpoint 1, sleeper_or_burner (v=0x........) at sleepers.c:72 +72 int i = 0; +Continuing. +Program received signal SIGTRAP, Trace/breakpoint trap. +0x........ in do_burn () at sleepers.c:39 +39 for (i = 0; i < burn; i++) loopnr++; +[Switching to thread 1 (Thread ....)]#0 0x........ in do_burn () at sleepers.c:39 +39 for (i = 0; i < burn; i++) loopnr++; +$1 = void +[Switching to thread 2 (Thread ....)]#0 0x........ in syscall ... +Could not write register "xxx"; remote failure reply 'E. +ERROR changing register xxx regno y +gdb commands changing registers (pc, sp, ...) (e.g. 'jump', +set pc, calling from gdb a function in the debugged process, ...) +can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state +Thread status is VgTs_WaitSys +' +[Switching to thread 3 (Thread ....)]#0 0x........ in syscall ... +Could not write register "xxx"; remote failure reply 'E. +ERROR changing register xxx regno y +gdb commands changing registers (pc, sp, ...) (e.g. 'jump', +set pc, calling from gdb a function in the debugged process, ...) +can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state +Thread status is VgTs_WaitSys +' +[Switching to thread 4 (Thread ....)]#0 0x........ in do_burn () at sleepers.c:39 +39 for (i = 0; i < burn; i++) loopnr++; +$2 = void +monitor command request to kill this process +Remote connection closed diff --git a/gdbserver_tests/mcinfcallWSRU.stdinB.gdb b/gdbserver_tests/mcinfcallWSRU.stdinB.gdb new file mode 100644 index 00000000..8429ddec --- /dev/null +++ b/gdbserver_tests/mcinfcallWSRU.stdinB.gdb @@ -0,0 +1,28 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU +monitor vg.set vgdb-error 999999 +# +# ensure all threads are known +break sleeper_or_burner +continue +continue +continue +continue +# +# Here the 4 threads have been started. +# We will interrupt in a few seconds (be sure all tasks are in Runnable/Yielding state +# or in WaitSys state. +shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU 10 +# +continue +# +thread 1 +print whoami("thread 1 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb") +thread 2 +print whoami("thread 2 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb") +thread 3 +print whoami("thread 3 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb") +thread 4 +print whoami("thread 4 inferior call pushed from gdb in mcinfcallWSRU.stdinB.gdb") +monitor vg.kill +quit diff --git a/gdbserver_tests/mcinfcallWSRU.vgtest b/gdbserver_tests/mcinfcallWSRU.vgtest new file mode 100644 index 00000000..859ba178 --- /dev/null +++ b/gdbserver_tests/mcinfcallWSRU.vgtest @@ -0,0 +1,13 @@ +# test inferior calls when some threads are in Runnable or Yielding mode, +# some threads are in WaitSys. +prog: sleepers +args: 100 100000000 1000000000 -S-SB-B- +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcinfcallWSRU +# Disable on Darwin: inferior call rejected as it cannot find malloc. +prereq: ../tests/os_test linux +# filter_gdb to replace pid and Thread numbers. +stderr_filter: filter_gdb +progB: gdb +argsB: --quiet -l 60 --nx 1>&2 ./sleepers +stdinB: mcinfcallWSRU.stdinB.gdb +stderrB_filter: filter_gdb diff --git a/gdbserver_tests/mcinvokeRU.stderr.exp b/gdbserver_tests/mcinvokeRU.stderr.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcinvokeRU.stderr.exp diff --git a/gdbserver_tests/mcinvokeRU.stderrB.exp b/gdbserver_tests/mcinvokeRU.stderrB.exp new file mode 100644 index 00000000..41e660bd --- /dev/null +++ b/gdbserver_tests/mcinvokeRU.stderrB.exp @@ -0,0 +1,14 @@ +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.kill to pid .... +readchar: Got EOF +error reading packet diff --git a/gdbserver_tests/mcinvokeRU.stdoutB.exp b/gdbserver_tests/mcinvokeRU.stdoutB.exp new file mode 100644 index 00000000..e12ed905 --- /dev/null +++ b/gdbserver_tests/mcinvokeRU.stdoutB.exp @@ -0,0 +1,23 @@ +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +monitor command request to kill this process diff --git a/gdbserver_tests/mcinvokeRU.vgtest b/gdbserver_tests/mcinvokeRU.vgtest new file mode 100644 index 00000000..ab933bbe --- /dev/null +++ b/gdbserver_tests/mcinvokeRU.vgtest @@ -0,0 +1,12 @@ +# test that vgdb can invoke a process when all threads are in Runnable or Yielding mode +# If the test goes wrong, it might consume CPU during a long time. +prog: sleepers +args: 1 0 1000000000 B-B-B-B- +vgopts: --tool=memcheck --vgdb=yes --vgdb-prefix=./vgdb-prefix-mcinvokeRU +stderr_filter: filter_make_empty +# as the Valgrind process is always busy, we do not need the vgdb.ptraceinvoker prereq. +progB: invoker +argsB: 10 --vgdb-prefix=./vgdb-prefix-mcinvokeRU --wait=60 -c vg.wait 0 +# if the --wait is not enough, the test will fail or block. +stdoutB_filter: filter_memcheck_monitor +stderrB_filter: filter_vgdb diff --git a/gdbserver_tests/mcinvokeWS.stderr.exp b/gdbserver_tests/mcinvokeWS.stderr.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcinvokeWS.stderr.exp diff --git a/gdbserver_tests/mcinvokeWS.stderrB.exp b/gdbserver_tests/mcinvokeWS.stderrB.exp new file mode 100644 index 00000000..41e660bd --- /dev/null +++ b/gdbserver_tests/mcinvokeWS.stderrB.exp @@ -0,0 +1,14 @@ +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.wait 0 to pid .... +sending command vg.kill to pid .... +readchar: Got EOF +error reading packet diff --git a/gdbserver_tests/mcinvokeWS.stdoutB.exp b/gdbserver_tests/mcinvokeWS.stdoutB.exp new file mode 100644 index 00000000..e12ed905 --- /dev/null +++ b/gdbserver_tests/mcinvokeWS.stdoutB.exp @@ -0,0 +1,23 @@ +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +gdbserver: continuing in 0 ms ... +gdbserver: continuing after wait ... +monitor command request to kill this process diff --git a/gdbserver_tests/mcinvokeWS.vgtest b/gdbserver_tests/mcinvokeWS.vgtest new file mode 100644 index 00000000..1be48e83 --- /dev/null +++ b/gdbserver_tests/mcinvokeWS.vgtest @@ -0,0 +1,12 @@ +# test that vgdb can invoke a process when all threads are in WaitSys mode. +# If the test goes wrong, it might be blocked during 10000 seconds. +prog: sleepers +args: 1 10000000 0 -S-S-S-S +vgopts: --tool=memcheck --vgdb=yes --vgdb-prefix=./vgdb-prefix-mcinvokeWS +stderr_filter: filter_make_empty +prereq: test -f vgdb.ptraceinvoker +progB: invoker +argsB: 10 --vgdb-prefix=./vgdb-prefix-mcinvokeWS --wait=60 -c vg.wait 0 +# if the --wait is not enough, the test will fail or block +stdoutB_filter: filter_memcheck_monitor +stderrB_filter: filter_vgdb diff --git a/gdbserver_tests/mcleak.stderr.exp b/gdbserver_tests/mcleak.stderr.exp new file mode 100644 index 00000000..734ff4a5 --- /dev/null +++ b/gdbserver_tests/mcleak.stderr.exp @@ -0,0 +1,40 @@ +(action at startup) vgdb me ... +expecting details 10 bytes reachable +10 bytes in 1 blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +expecting to have NO details +expecting details +10 bytes lost, +21 bytes reachable +expecting details +65 bytes reachable +expecting to have NO details +expecting details +10 bytes reachable +expecting details -10 bytes reachable, +10 bytes lost +expecting details -10 bytes lost, +10 bytes reachable +expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable +finished +leaked: 32 bytes in 1 blocks +dubious: 0 bytes in 0 blocks +reachable: 64 bytes in 3 blocks +suppressed: 0 bytes in 0 blocks +10 bytes in 1 blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +21 bytes in 1 blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:23) + by 0x........: main (leak-delta.c:60) + +32 bytes in 1 blocks are definitely lost in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:28) + by 0x........: main (leak-delta.c:60) + +33 bytes in 1 blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:28) + by 0x........: main (leak-delta.c:60) + diff --git a/gdbserver_tests/mcleak.stderrB.exp b/gdbserver_tests/mcleak.stderrB.exp new file mode 100644 index 00000000..081cde75 --- /dev/null +++ b/gdbserver_tests/mcleak.stderrB.exp @@ -0,0 +1,58 @@ +relaying data between gdb and process .... +vgdb-error value changed from 0 to 999999 +10 bytes in 1 blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +10 (+10) bytes in 1 (+1) blocks are definitely lost in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +21 (+21) bytes in 1 (+1) blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:23) + by 0x........: main (leak-delta.c:60) + +65 (+65) bytes in 2 (+2) blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:28) + by 0x........: main (leak-delta.c:60) + +10 (+10) bytes in 1 (+1) blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +0 (-10) bytes in 0 (-1) blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +10 (+10) bytes in 1 (+1) blocks are definitely lost in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +0 (-10) bytes in 0 (-1) blocks are definitely lost in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +10 (+10) bytes in 1 (+1) blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:14) + by 0x........: main (leak-delta.c:60) + +32 (+32) bytes in 1 (+1) blocks are definitely lost in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:28) + by 0x........: main (leak-delta.c:60) + +33 (-32) bytes in 1 (-1) blocks are still reachable in loss record ... of ... + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: f (leak-delta.c:28) + by 0x........: main (leak-delta.c:60) + +Remote connection closed diff --git a/gdbserver_tests/mcleak.stdinB.gdb b/gdbserver_tests/mcleak.stdinB.gdb new file mode 100644 index 00000000..5cdcbe94 --- /dev/null +++ b/gdbserver_tests/mcleak.stdinB.gdb @@ -0,0 +1,75 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcleak +monitor vg.set vgdb-error 999999 +# +# +# insert break: +break breakme +# +# continue till each break and execute via gdb the leak search as done in the C code. +continue +# +# +# fprintf(stderr, "expecting details 10 bytes reachable\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check any reachable full +continue +# VALGRIND_DO_LEAK_CHECK; +# +# fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); +up +monitor mc.leak_check increased reachable full +continue +# VALGRIND_DO_ADDED_LEAK_CHECK; +# +# b10--; // lose b10 +# b21 = malloc (21); +# fprintf(stderr, "expecting details +10 bytes lost, +21 bytes reachable\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check increased reachable full +continue +# VALGRIND_DO_ADDED_LEAK_CHECK; +# +# for (i = 0; i < 2; i ++) +# b32_33[i] = malloc (32+i); +# fprintf(stderr, "expecting details +65 bytes reachable\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check increased reachable full +continue +# VALGRIND_DO_ADDED_LEAK_CHECK; +# +# fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check increased reachable full +continue +# VALGRIND_DO_ADDED_LEAK_CHECK; +# +# b10++; +# fprintf(stderr, "expecting details +10 bytes reachable\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check increased reachable full +continue +# VALGRIND_DO_ADDED_LEAK_CHECK; +# +# b10--; +# fprintf(stderr, "expecting details -10 bytes reachable, +10 bytes lost\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check changed reachable full +continue +# VALGRIND_DO_CHANGED_LEAK_CHECK; +# +# b10++; +# fprintf(stderr, "expecting details -10 bytes lost, +10 bytes reachable\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check changed reachable full +continue +# VALGRIND_DO_CHANGED_LEAK_CHECK; +# +# b32_33[0]--; +# fprintf(stderr, "expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable\n"); fflush(stderr); breakme(); +up +monitor mc.leak_check changed reachable full +continue +# VALGRIND_DO_CHANGED_LEAK_CHECK; +# +quit diff --git a/gdbserver_tests/mcleak.stdoutB.exp b/gdbserver_tests/mcleak.stdoutB.exp new file mode 100644 index 00000000..b3e1aec6 --- /dev/null +++ b/gdbserver_tests/mcleak.stdoutB.exp @@ -0,0 +1,49 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcleak +0x........ in _start () from ...start file... +Breakpoint 1 at 0x........: file leak-delta.c, line 9. +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:16 +16 fprintf(stderr, "expecting details 10 bytes reachable\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:19 +19 fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:24 +24 fprintf(stderr, "expecting details +10 bytes lost, +21 bytes reachable\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:29 +29 fprintf(stderr, "expecting details +65 bytes reachable\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:32 +32 fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:36 +36 fprintf(stderr, "expecting details +10 bytes reachable\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:40 +40 fprintf(stderr, "expecting details -10 bytes reachable, +10 bytes lost\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:44 +44 fprintf(stderr, "expecting details -10 bytes lost, +10 bytes reachable\n"); fflush(stderr); breakme(); +Continuing. +Breakpoint 1, breakme () at leak-delta.c:9 +9 static void breakme() {}; +#1 0x........ in f () at leak-delta.c:48 +48 fprintf(stderr, "expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable\n"); fflush(stderr); breakme(); +Continuing. diff --git a/gdbserver_tests/mcleak.vgtest b/gdbserver_tests/mcleak.vgtest new file mode 100644 index 00000000..3cdd1ea4 --- /dev/null +++ b/gdbserver_tests/mcleak.vgtest @@ -0,0 +1,12 @@ +# test the memcheck leak functionality. +prog: ../memcheck/tests/leak-delta +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcleak -q --leak-check=yes --show-reachable=yes --leak-resolution=high +# temorarily disabled, waiting for leak-delta test program (next patch) +prereq: test -x ../memcheck/tests/leak-delta +stdout_filter: filter_memcheck_monitor +stderr_filter: filter_memcheck_monitor +progB: gdb +argsB: --quiet -l 60 --nx ../memcheck/tests/leak-delta +stdinB: mcleak.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_memcheck_monitor diff --git a/gdbserver_tests/mcsignopass.stderr.exp b/gdbserver_tests/mcsignopass.stderr.exp new file mode 100644 index 00000000..f60f30e8 --- /dev/null +++ b/gdbserver_tests/mcsignopass.stderr.exp @@ -0,0 +1,20 @@ + +(action at startup) vgdb me ... +Test 1: Invalid write of size 4 + at 0x........: test1 (faultstatus.c:105) + by 0x........: main (faultstatus.c:168) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + + PASS +Test 2: PASS +Test 3: PASS +Test 4: PASS + +HEAP SUMMARY: + in use at exit: 0 bytes in 0 blocks + total heap usage: 0 allocs, 0 frees, 0 bytes allocated + +For a detailed leak analysis, rerun with: --leak-check=full + +For counts of detected and suppressed errors, rerun with: -v +ERROR SUMMARY: 11 errors from 1 contexts (suppressed: 0 from 0) diff --git a/gdbserver_tests/mcsignopass.stderrB.exp b/gdbserver_tests/mcsignopass.stderrB.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcsignopass.stderrB.exp diff --git a/gdbserver_tests/mcsignopass.stdinB.gdb b/gdbserver_tests/mcsignopass.stdinB.gdb new file mode 100644 index 00000000..44a5c088 --- /dev/null +++ b/gdbserver_tests/mcsignopass.stdinB.gdb @@ -0,0 +1,41 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsignopass +monitor vg.set vgdb-error 999999 +# +# instruct gdb to not pass (i.e. ignore) these signals. +# +# Trap the below signals, we make them stop and then continue. +# For SIGSEGV, we make it continue a few times, till we pass it. +handle SIGSEGV nopass print stop +handle SIGBUS pass print stop +handle SIGFPE pass print stop +# +continue +# +# SIGTRAP : caused by invalid write error detected by memcheck +continue +# +# SIGSEGV can't be ignored, so it is re-signaled. We continue many times +# to be sure it is this signal which is re-signalled. Then will pass it. +continue +continue +continue +continue +continue +continue +continue +continue +continue +# +# Change handling so that we just see the 2nd SIGSEGV +handle SIGSEGV pass print nostop +continue +# +# SIGBUS will be shown and passed: +continue +# +# then SIGFPE is shown and passed: +continue +# +# program will exit +quit diff --git a/gdbserver_tests/mcsignopass.stdoutB.exp b/gdbserver_tests/mcsignopass.stdoutB.exp new file mode 100644 index 00000000..70ff5ba1 --- /dev/null +++ b/gdbserver_tests/mcsignopass.stdoutB.exp @@ -0,0 +1,64 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsignopass +0x........ in _start () from ...start file... +Signal Stop Print Pass to program Description +SIGSEGV Yes Yes No Segmentation fault +Signal Stop Print Pass to program Description +SIGBUS Yes Yes Yes Bus error +Signal Stop Print Pass to program Description +SIGFPE Yes Yes Yes Arithmetic exception +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Signal Stop Print Pass to program Description +SIGSEGV No Yes Yes Segmentation fault +Continuing. +Program received signal SIGSEGV, Segmentation fault. +Program received signal SIGBUS, Bus error. +0x........ in test3 () at faultstatus.c:115 +115 mapping[FILESIZE+10]; +Continuing. +Program received signal SIGFPE, Arithmetic exception. +0x........ in test4 () at faultstatus.c:120 +120 volatile int v = 44/zero(); +Continuing. diff --git a/gdbserver_tests/mcsignopass.vgtest b/gdbserver_tests/mcsignopass.vgtest new file mode 100644 index 00000000..6568c629 --- /dev/null +++ b/gdbserver_tests/mcsignopass.vgtest @@ -0,0 +1,15 @@ +# test the signal handling, when signals are *not* passed to the Valgrind guest. +# We detect this two ways: +# the gdb output will not contain the signal handling +# faultstatus C code will report a failure for the signal not passed +# (i.e. SIGBUG, Test 3). Other tests will be succesful, because signals +# are eventually passed. +prog: ../none/tests/faultstatus +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcsignopass +stderr_filter: filter_memcheck_monitor +progB: gdb +argsB: --quiet -l 60 --nx ../none/tests/faultstatus +stdinB: mcsignopass.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_make_empty + diff --git a/gdbserver_tests/mcsigpass.stderr.exp b/gdbserver_tests/mcsigpass.stderr.exp new file mode 100644 index 00000000..af2b53aa --- /dev/null +++ b/gdbserver_tests/mcsigpass.stderr.exp @@ -0,0 +1,20 @@ + +(action at startup) vgdb me ... +Test 1: Invalid write of size 4 + at 0x........: test1 (faultstatus.c:105) + by 0x........: main (faultstatus.c:168) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + + PASS +Test 2: PASS +Test 3: PASS +Test 4: PASS + +HEAP SUMMARY: + in use at exit: 0 bytes in 0 blocks + total heap usage: 0 allocs, 0 frees, 0 bytes allocated + +For a detailed leak analysis, rerun with: --leak-check=full + +For counts of detected and suppressed errors, rerun with: -v +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/gdbserver_tests/mcsigpass.stderrB.exp b/gdbserver_tests/mcsigpass.stderrB.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcsigpass.stderrB.exp diff --git a/gdbserver_tests/mcsigpass.stdinB.gdb b/gdbserver_tests/mcsigpass.stdinB.gdb new file mode 100644 index 00000000..ebd0cc75 --- /dev/null +++ b/gdbserver_tests/mcsigpass.stdinB.gdb @@ -0,0 +1,25 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsigpass +monitor vg.set vgdb-error 999999 +# +# After this continue, we will receive 5 signals. +continue +# +# SIGTRAP : caused by invalid write error detected by memcheck +continue +# +# SIGSEGV : line 99 +continue +# +# SIGSEGV : line 104 +continue +# +# SIGBUS : line 109 +continue +# +# SIGFPE : line 114 +continue +# +# program will exit +quit + diff --git a/gdbserver_tests/mcsigpass.stdoutB.exp b/gdbserver_tests/mcsigpass.stdoutB.exp new file mode 100644 index 00000000..e9245922 --- /dev/null +++ b/gdbserver_tests/mcsigpass.stdoutB.exp @@ -0,0 +1,19 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcsigpass +0x........ in _start () from ...start file... +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test1 () at faultstatus.c:105 +105 *BADADDR = 'x'; +Continuing. +Program received signal SIGSEGV, Segmentation fault. +0x........ in test2 () at faultstatus.c:110 +110 mapping[0] = 'x'; +Continuing. +Program received signal SIGBUS, Bus error. +0x........ in test3 () at faultstatus.c:115 +115 mapping[FILESIZE+10]; +Continuing. +Program received signal SIGFPE, Arithmetic exception. +0x........ in test4 () at faultstatus.c:120 +120 volatile int v = 44/zero(); +Continuing. diff --git a/gdbserver_tests/mcsigpass.vgtest b/gdbserver_tests/mcsigpass.vgtest new file mode 100644 index 00000000..6cb0e3f8 --- /dev/null +++ b/gdbserver_tests/mcsigpass.vgtest @@ -0,0 +1,10 @@ +# test the signal handling, when signals are passed to the Valgrind guest. +prog: ../none/tests/faultstatus +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcsigpass +stderr_filter: filter_memcheck_monitor +progB: gdb +argsB: --quiet -l 60 --nx ../none/tests/faultstatus +stdinB: mcsigpass.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_make_empty + diff --git a/gdbserver_tests/mcvabits.stderr.exp b/gdbserver_tests/mcvabits.stderr.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcvabits.stderr.exp diff --git a/gdbserver_tests/mcvabits.stderrB.exp b/gdbserver_tests/mcvabits.stderrB.exp new file mode 100644 index 00000000..6b4635e5 --- /dev/null +++ b/gdbserver_tests/mcvabits.stderrB.exp @@ -0,0 +1,55 @@ +relaying data between gdb and process .... +vgdb-error value changed from 0 to 999999 +Address 0x........ len 10 addressable + Address 0x........ is 0 bytes inside data symbol "undefined" +Address 0x........ len 10 defined + Address 0x........ is 0 bytes inside data symbol "undefined" +00000000 00000000 0000 +Address 0x........ len 10 addressable + Address 0x........ is 0 bytes inside data symbol "undefined" +Address 0x........ len 10 not defined: +Uninitialised value at 0x........ + Address 0x........ is 0 bytes inside data symbol "undefined" +ff00ff00 ff00ff00 ff00 +Address 0x........ len 10 addressable + Address 0x........ is 0 bytes inside data symbol "undefined" +Address 0x........ len 10 not defined: +Uninitialised value at 0x........ + Address 0x........ is 0 bytes inside data symbol "undefined" +ff000000 0000ff00 ff00 +Address 0x........ len 10 addressable + Address 0x........ is 0 bytes inside data symbol "undefined" +Address 0x........ len 10 not defined: +Uninitialised value at 0x........ + Address 0x........ is 0 bytes inside data symbol "undefined" +ff00ffff ffffff00 ff00 +Address 0x........ len 2 addressable + Address 0x........ is 0 bytes inside data symbol "undefined" +Address 0x........ len 2 not defined: +Uninitialised value at 0x........ + Address 0x........ is 0 bytes inside data symbol "undefined" +ff00 +Address 0x........ len 2 not addressable: +bad address 0x........ + Address 0x........ is 2 bytes inside data symbol "undefined" +Address 0x........ len 2 not addressable: +bad address 0x........ + Address 0x........ is 2 bytes inside data symbol "undefined" +____ +Address 0x........ len 2 has 2 bytes unaddressable +Address 0x........ len 6 addressable + Address 0x........ is 4 bytes inside data symbol "undefined" +Address 0x........ len 6 not defined: +Uninitialised value at 0x........ + Address 0x........ is 4 bytes inside data symbol "undefined" +ffffff00 ff00 +Address 0x........ len 10 not addressable: +bad address 0x........ + Address 0x........ is 0 bytes inside data symbol "undefined" +Address 0x........ len 10 not addressable: +bad address 0x........ + Address 0x........ is 0 bytes inside data symbol "undefined" +0000____ 00000000 0000 +Address 0x........ len 10 has 2 bytes unaddressable +monitor command request to kill this process +Remote connection closed diff --git a/gdbserver_tests/mcvabits.stdinB.gdb b/gdbserver_tests/mcvabits.stdinB.gdb new file mode 100644 index 00000000..7aa423a7 --- /dev/null +++ b/gdbserver_tests/mcvabits.stdinB.gdb @@ -0,0 +1,73 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcvabits +monitor vg.set vgdb-error 999999 +# +# +# insert break: +break breakme +# +# continue till //1break: +continue +# +# up to main: +up +# +# print local string variables: +print main_name +print undefined +# save address of undefined +set $0xundefined = &undefined +# +# Verif A-bits, V-bits, Get V-bits: A,V,G [0..9] +eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined +eval "monitor mc.check_memory defined 0x%x 10", $0xundefined +eval "monitor mc.get_vbits 0x%x 10", $0xundefined +# +# continue till //2break: +continue +# +# A,V,G [0..9] after the undefinition of some bytes by executable: +eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined +eval "monitor mc.check_memory defined 0x%x 10", $0xundefined +eval "monitor mc.get_vbits 0x%x 10", $0xundefined +# +# Redefine [2..4] +set $0xundefined_2 = (char*)$0xundefined + 2 +eval "monitor mc.make_memory defined 0x%x 3", $0xundefined_2 +# A,V,G +eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined +eval "monitor mc.check_memory defined 0x%x 10", $0xundefined +eval "monitor mc.get_vbits 0x%x 10", $0xundefined +# +# Undefine [2..5] +eval "monitor mc.make_memory undefined 0x%x 4", $0xundefined_2 +# A,V,G [0..9] +eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined +eval "monitor mc.check_memory defined 0x%x 10", $0xundefined +eval "monitor mc.get_vbits 0x%x 10", $0xundefined +# +# noaccess [2..3] +eval "monitor mc.make_memory noaccess 0x%x 2", $0xundefined_2 +# A,V,G [0..1] +eval "monitor mc.check_memory addressable 0x%x 2", $0xundefined +eval "monitor mc.check_memory defined 0x%x 2", $0xundefined +eval "monitor mc.get_vbits 0x%x 2", $0xundefined +# A,V,G [2..3] +eval "monitor mc.check_memory addressable 0x%x 2", $0xundefined_2 +eval "monitor mc.check_memory defined 0x%x 2", $0xundefined_2 +eval "monitor mc.get_vbits 0x%x 2", $0xundefined_2 +# A,V,G [4..9] +set $0xundefined_4 = (char*) $0xundefined_2 + 2 +eval "monitor mc.check_memory addressable 0x%x 6", $0xundefined_4 +eval "monitor mc.check_memory defined 0x%x 6", $0xundefined_4 +eval "monitor mc.get_vbits 0x%x 6", $0xundefined_4 +# +# ifaddressabledefined undefined[0..9] +eval "monitor mc.make_memory ifaddressabledefined 0x%x 10", $0xundefined +# A,V,G +eval "monitor mc.check_memory addressable 0x%x 10", $0xundefined +eval "monitor mc.check_memory defined 0x%x 10", $0xundefined +eval "monitor mc.get_vbits 0x%x 10", $0xundefined +# +monitor vg.kill +quit diff --git a/gdbserver_tests/mcvabits.stdoutB.exp b/gdbserver_tests/mcvabits.stdoutB.exp new file mode 100644 index 00000000..68df7e14 --- /dev/null +++ b/gdbserver_tests/mcvabits.stdoutB.exp @@ -0,0 +1,13 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcvabits +0x........ in _start () from ...start file... +Breakpoint 1 at 0x........: file t.c, line 100. +Continuing. +Breakpoint 1, breakme (line=112) at t.c:100 +100 if (line > 1000) +#1 0x........ in main (argc=1, argv=0x........) at t.c:112 +112 breakme(__LINE__); //break1 +$1 = 0x........ "main name" +$2 = "undefined" +Continuing. +Breakpoint 1, breakme (line=117) at t.c:100 +100 if (line > 1000) diff --git a/gdbserver_tests/mcvabits.vgtest b/gdbserver_tests/mcvabits.vgtest new file mode 100644 index 00000000..689c26f7 --- /dev/null +++ b/gdbserver_tests/mcvabits.vgtest @@ -0,0 +1,12 @@ +# test the memcheck V and A bits monitor functionality +prog: t +vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcvabits +stdout_filter: filter_make_empty +stderr_filter: filter_make_empty +prereq: test -e ./gdb.eval +progB: gdb +argsB: --quiet -l 60 --nx ./t +stdinB: mcvabits.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_memcheck_monitor + diff --git a/gdbserver_tests/mcwatchpoints.stderr.exp b/gdbserver_tests/mcwatchpoints.stderr.exp new file mode 100644 index 00000000..b73233a9 --- /dev/null +++ b/gdbserver_tests/mcwatchpoints.stderr.exp @@ -0,0 +1,25 @@ + +(action at startup) vgdb me ... +breakme function called from line 19 +before reading 0/4/8 +u: Expected value at 0 +f: Expected value at 4 +d: Expected value at 8 +before writing 0 +before writing 4 +before writing 8 +after writing 8 +value UndeFineD +before rewriting 0 +before rewriting 4 +before rewriting 8 +value 0nde4ine8 + +HEAP SUMMARY: + in use at exit: 0 bytes in 0 blocks + total heap usage: 0 allocs, 0 frees, 0 bytes allocated + +For a detailed leak analysis, rerun with: --leak-check=full + +For counts of detected and suppressed errors, rerun with: -v +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/gdbserver_tests/mcwatchpoints.stderrB.exp b/gdbserver_tests/mcwatchpoints.stderrB.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mcwatchpoints.stderrB.exp diff --git a/gdbserver_tests/mcwatchpoints.stdinB.gdb b/gdbserver_tests/mcwatchpoints.stdinB.gdb new file mode 100644 index 00000000..5ec79fa4 --- /dev/null +++ b/gdbserver_tests/mcwatchpoints.stdinB.gdb @@ -0,0 +1,24 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcwatchpoints +monitor vg.set vgdb-error 999999 +# +# +# insert break: +break breakme +# +# continue till //break1: +continue +# +# insert the watchpoints +rwatch undefined[0] +awatch undefined[4] +watch undefined[8] +# +# now we should encounter 4 break points +continue +continue +continue +continue +del +continue +quit diff --git a/gdbserver_tests/mcwatchpoints.stdoutB.exp b/gdbserver_tests/mcwatchpoints.stdoutB.exp new file mode 100644 index 00000000..7c6e09d6 --- /dev/null +++ b/gdbserver_tests/mcwatchpoints.stdoutB.exp @@ -0,0 +1,33 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcwatchpoints +0x........ in _start () from ...start file... +Breakpoint 1 at 0x........: file watchpoints.c, line 7. +Continuing. +Breakpoint 1, breakme (line=19) at watchpoints.c:7 +7 fprintf(stderr, "breakme function called from line %d\n", line); +Hardware read watchpoint 2: undefined[0] +Hardware access (read/write) watchpoint 3: undefined[4] +Hardware watchpoint 4: undefined[8] +Continuing. +Hardware read watchpoint 2: undefined[0] +Value = 117 'u' +main (argc=1, argv=0x........) at watchpoints.c:23 +23 if (undefined[0] == 'u') +Continuing. +Hardware access (read/write) watchpoint 3: undefined[4] +Value = 102 'f' +main (argc=1, argv=0x........) at watchpoints.c:28 +28 if (undefined[4] == 'f') +Continuing. +Hardware access (read/write) watchpoint 3: undefined[4] +Old value = 102 'f' +New value = 70 'F' +main (argc=1, argv=0x........) at watchpoints.c:46 +46 fprintf(stderr, "before writing 8\n"); +Continuing. +Hardware watchpoint 4: undefined[8] +Old value = 100 'd' +New value = 68 'D' +main (argc=1, argv=0x........) at watchpoints.c:49 +49 fprintf(stderr, "after writing 8\n"); +Delete all breakpoints? (y or n) [answered Y; input not from terminal] +Continuing. diff --git a/gdbserver_tests/mcwatchpoints.vgtest b/gdbserver_tests/mcwatchpoints.vgtest new file mode 100644 index 00000000..72714eba --- /dev/null +++ b/gdbserver_tests/mcwatchpoints.vgtest @@ -0,0 +1,12 @@ +# test the memcheck watchpoint functionality +# Note: we need --vgdb=full to stop at the instruction following the watchpoint. +prog: watchpoints +vgopts: --tool=memcheck --vgdb=full --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcwatchpoints +stdout_filter: filter_make_empty +stderr_filter: filter_memcheck_monitor +progB: gdb +argsB: --quiet -l 60 --nx ./watchpoints +stdinB: mcwatchpoints.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_make_empty + diff --git a/gdbserver_tests/mssnapshot.stderr.exp b/gdbserver_tests/mssnapshot.stderr.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/mssnapshot.stderr.exp diff --git a/gdbserver_tests/mssnapshot.stderrB.exp b/gdbserver_tests/mssnapshot.stderrB.exp new file mode 100644 index 00000000..4b7a012d --- /dev/null +++ b/gdbserver_tests/mssnapshot.stderrB.exp @@ -0,0 +1,22 @@ +relaying data between gdb and process .... +vgdb-error value changed from 0 to 999999 +general valgrind monitor commands: + help [debug] : monitor command help. With debug: + debugging commands + vg.wait [<ms>] : sleep <ms> (default 0) then continue + vg.info all_errors : show all errors found so far + vg.info last_error : show last error found + vg.info n_errs_found : show the nr of errors found so far + vg.kill : kill the Valgrind process + vg.set gdb_output : set valgrind output to gdb + vg.set log_output : set valgrind output to log + vg.set mixed_output : set valgrind output to log, interactive output to gdb + vg.set vgdb-error <errornr> : debug me at error >= <errornr> + +massif monitor commands: + ms.snapshot [<filename>] [detailed] + takes a snapshot and saves it in <filename> + default <filename> is massif.vgdb.out + if present, detailed argument indicates to take a detailed snapshot + +monitor command request to kill this process +Remote connection closed diff --git a/gdbserver_tests/mssnapshot.stdinB.gdb b/gdbserver_tests/mssnapshot.stdinB.gdb new file mode 100644 index 00000000..42b56fe7 --- /dev/null +++ b/gdbserver_tests/mssnapshot.stdinB.gdb @@ -0,0 +1,21 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mssnapshot +monitor vg.set vgdb-error 999999 +# +# +# insert break: +break main +# +# continue till main +continue +# +# test the massif help +monitor help +# +# test non detailed and detailed snapshot +monitor ms.snapshot +monitor ms.snapshot detailed +# +# +monitor vg.kill +quit diff --git a/gdbserver_tests/mssnapshot.stdoutB.exp b/gdbserver_tests/mssnapshot.stdoutB.exp new file mode 100644 index 00000000..274eab02 --- /dev/null +++ b/gdbserver_tests/mssnapshot.stdoutB.exp @@ -0,0 +1,6 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mssnapshot +0x........ in _start () from ...start file... +Breakpoint 1 at 0x........: file t.c, line 105. +Continuing. +Breakpoint 1, main (argc=1, argv=0x........) at t.c:105 +105 char *main_name = "main name"; diff --git a/gdbserver_tests/mssnapshot.vgtest b/gdbserver_tests/mssnapshot.vgtest new file mode 100644 index 00000000..a11a04df --- /dev/null +++ b/gdbserver_tests/mssnapshot.vgtest @@ -0,0 +1,10 @@ +# test the memcheck monitor help +prog: t +vgopts: --tool=massif --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mssnapshot +stdout_filter: filter_make_empty +stderr_filter: filter_make_empty +progB: gdb +argsB: --quiet -l 60 --nx ./t +stdinB: mssnapshot.stdinB.gdb +stdoutB_filter: filter_gdb +stderrB_filter: filter_vgdb diff --git a/gdbserver_tests/nlcontrolc.stderr.exp b/gdbserver_tests/nlcontrolc.stderr.exp new file mode 100644 index 00000000..f6eef783 --- /dev/null +++ b/gdbserver_tests/nlcontrolc.stderr.exp @@ -0,0 +1,9 @@ +Nulgrind, the minimal Valgrind tool + +(action at startup) vgdb me ... +loops/sleep_ms/burn/threads_spec: 1000000000 1000000000 1000000000 BSBSBSBS +Brussels ready to sleep and/or burn +London ready to sleep and/or burn +Petaouchnok ready to sleep and/or burn +main ready to sleep and/or burn + diff --git a/gdbserver_tests/nlcontrolc.stderrB.exp b/gdbserver_tests/nlcontrolc.stderrB.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gdbserver_tests/nlcontrolc.stderrB.exp diff --git a/gdbserver_tests/nlcontrolc.stdinB.gdb b/gdbserver_tests/nlcontrolc.stdinB.gdb new file mode 100644 index 00000000..53a8d68c --- /dev/null +++ b/gdbserver_tests/nlcontrolc.stdinB.gdb @@ -0,0 +1,38 @@ +# connect gdb to Valgrind gdbserver: +target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-nlcontrolc +monitor vg.set vgdb-error 999999 +# +# +# simulate control-c in a few seconds +shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-nlcontrolc 10 +# +continue +# +# Here, all tasks should be blocked in a loooonnnng select, all in WaitSys +info threads +# We will unblock them by changing their timeout argument +# To avoid going into the frame where the timeval arg is, +# it has been defined as global variables, as the nr +# of calls on the stack differs between 32bits and 64bits, +# and/or between OS. +# ensure select finishes in a few milliseconds max: +p t[0].tv_sec = 0 +p t[1].tv_sec = 0 +p t[2].tv_sec = 0 +p t[3].tv_sec = 0 +# +# We will change the burning parameters in a few seconds +shell ./simulate_control_c --vgdb-prefix=./vgdb-prefix-nlcontrolc 10 +# +continue +# +# Threads are burning cpu now +# We would like to test info threads here, but which thread are Runnable or Yielding +# is unpredictable. +# info threads +p burn = 0 +p loops = 0 +p report_finished = 0 +continue +# and the process should stop very quickly now +quit diff --git a/gdbserver_tests/nlcontrolc.stdoutB.exp b/gdbserver_tests/nlcontrolc.stdoutB.exp new file mode 100644 index 00000000..a79f5d2c --- /dev/null +++ b/gdbserver_tests/nlcontrolc.stdoutB.exp @@ -0,0 +1,24 @@ +Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-nlcontrolc +0x........ in _start () from ...start file... +Continuing. +Program received signal SIGTRAP, Trace/breakpoint trap. +0x........ in syscall ... +[New Thread ....] +[New Thread ....] +[New Thread ....] + 4 Thread .... (tid 4 VgTs_WaitSys) 0x........ in syscall ... + 3 Thread .... (tid 3 VgTs_WaitSys) 0x........ in syscall ... + 2 Thread .... (tid 2 VgTs_WaitSys) 0x........ in syscall ... +* 1 Thread .... (tid 1 VgTs_WaitSys) 0x........ in syscall ... +$1 = 0 +$2 = 0 +$3 = 0 +$4 = 0 +Continuing. +Program received signal SIGTRAP, Trace/breakpoint trap. +0x........ in do_burn () at sleepers.c:39 +39 for (i = 0; i < burn; i++) loopnr++; +$5 = 0 +$6 = 0 +$7 = 0 +Continuing. diff --git a/gdbserver_tests/nlcontrolc.vgtest b/gdbserver_tests/nlcontrolc.vgtest new file mode 100644 index 00000000..6c059010 --- /dev/null +++ b/gdbserver_tests/nlcontrolc.vgtest @@ -0,0 +1,20 @@ +# test : +# info threads valgrind specific output +# the user can control-c an process with all threads in WaitSys +# and modify some variables +# the user can control-c an process with all threads in Running/Yielding +# and modify some variables +# sleepers is started with argument so that it will compute during ages. +# The variable modifications means it will exit in a reasonable time. +prog: sleepers +args: 1000000000 1000000000 1000000000 BSBSBSBS +vgopts: --tool=none --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-nlcontrolc +stderr_filter: filter_stderr +prereq: test -f vgdb.ptraceinvoker +progB: gdb +argsB: --quiet -l 60 --nx ./sleepers +stdinB: nlcontrolc.stdinB.gdb +#stdoutB_filter: /bin/cat +stdoutB_filter: filter_gdb +#stderrB_filter: /bin/cat +stderrB_filter: filter_make_empty diff --git a/gdbserver_tests/simulate_control_c b/gdbserver_tests/simulate_control_c new file mode 100755 index 00000000..cea1e596 --- /dev/null +++ b/gdbserver_tests/simulate_control_c @@ -0,0 +1,11 @@ +#! /bin/sh + +# simulate control_c by sending SIGUSR1 to the vgdb using prefix $1 in $2 seconds +VGDBPID=`./vgdb -D $1 2>&1 | awk '/vgdb pid/ {print $3}'` +if [ "$VGDBPID" = "" ] +then + echo "simulate_control_c could not determine the vgdb pid with " $1 + exit 1 +fi +(sleep $2; kill -10 $VGDBPID) & + diff --git a/gdbserver_tests/sleepers.c b/gdbserver_tests/sleepers.c new file mode 100644 index 00000000..cea20cb6 --- /dev/null +++ b/gdbserver_tests/sleepers.c @@ -0,0 +1,187 @@ +#define _GNU_SOURCE +#include <string.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sched.h> + +static int loops = 15; // each thread+main will do this amount of loop +static int sleepms = 1000; // in each loop, will sleep "sleepms" milliseconds +static int burn = 0; // after each sleep, will burn cpu in a tight 'burn' loop + + +static pid_t gettid() +{ +#ifdef __NR_gettid + return syscall(__NR_gettid); +#else + return getpid(); +#endif +} + +// will be invoked from gdb. +static void whoami(char *msg) +{ + fprintf(stderr, "pid %d Thread %d %s\n", getpid(), gettid(), msg); + fflush(stderr); +} + + +static void do_burn () +{ + int i; + int loopnr = 0; + // one single line for the below, to ensure interrupt on this line. + for (i = 0; i < burn; i++) loopnr++; +} + +static int thread_ready = 0; +static pthread_cond_t ready = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER; +static void signal_ready (void) +{ + int rc; + rc = pthread_mutex_lock(&ready_mutex); + if (rc != 0) + fprintf(stderr, "signal_ready lock error %d_n", rc); + thread_ready = 1; + rc = pthread_cond_signal(&ready); + if (rc != 0) + fprintf(stderr, "signal_ready signal error %d_n", rc); + rc = pthread_mutex_unlock(&ready_mutex); + if (rc != 0) + fprintf(stderr, "signal_ready unlock error %d_n", rc); +} + +struct spec { + char *name; + int sleep; + int burn; + int t; +}; +static struct timeval t[4]; +static int nr_sleeper_or_burner = 0; +static volatile int report_finished = 1; +// set to 0 to have no finish msg (as order is non-deterministic) +static void *sleeper_or_burner(void *v) +{ + int i = 0; + struct spec* s = (struct spec*)v; + + fprintf(stderr, "%s ready to sleep and/or burn\n", s->name); + fflush (stderr); + signal_ready(); + nr_sleeper_or_burner++; + + for (i = 0; i < loops; i++) { + if (sleepms > 0 && s->sleep) { + t[s->t].tv_sec = sleepms / 1000; + t[s->t].tv_usec = (sleepms % 1000) * 1000; + select (0, NULL, NULL, NULL, &t[s->t]); + } + if (burn > 0 && s->burn) + do_burn(); + } + if (report_finished) { + fprintf(stderr, "%s finished to sleep and/or burn\n", s->name); + fflush (stderr); + } + return NULL; +} + +// wait till a thread signals it is ready +static void wait_ready(void) +{ + int rc; + rc = pthread_mutex_lock(&ready_mutex); + if (rc != 0) + fprintf(stderr, "wait_ready lock error %d_n", rc); + while (! thread_ready && rc == 0) { + rc = pthread_cond_wait(&ready, &ready_mutex); + if (rc != 0) + fprintf(stderr, "wait_ready wait error %d_n", rc); + } + thread_ready = 0; + rc = pthread_mutex_unlock(&ready_mutex); + if (rc != 0) + fprintf(stderr, "wait_ready unlock error %d_n", rc); +} + +// We will lock ourselves on one single cpu. +// This bypasses the unfairness of the Valgrind scheduler +// when a multi-cpu machine has enough cpu to run all the +// threads wanting to burn cpu. +static void setaffinity(void) +{ +#ifdef VGO_linux + cpu_set_t single_cpu; + CPU_ZERO(&single_cpu); + CPU_SET(1, &single_cpu); + (void) sched_setaffinity(0, sizeof(single_cpu), &single_cpu); +#endif + // GDBTD: equivalent for Darwin ? +} + +int main (int argc, char *argv[]) +{ + char *threads_spec; + pthread_t ebbr, egll, zzzz; + struct spec b, l, p, m; + char *some_mem = malloc(100); + setaffinity(); + + if (argc > 1) + loops = atoi(argv[1]); + + if (argc > 2) + sleepms = atoi(argv[2]); + + if (argc > 3) + burn = atoll(argv[3]); + + if (argc > 4) + threads_spec = argv[4]; + else + threads_spec = "BSBSBSBS"; + + fprintf(stderr, "loops/sleep_ms/burn/threads_spec: %d %d %d %s\n", + loops, sleepms, burn, threads_spec); + fflush(stderr); + + b.name = "Brussels"; + b.burn = *threads_spec++ == 'B'; + b.sleep = *threads_spec++ == 'S'; + b.t = 1; + pthread_create(&ebbr, NULL, sleeper_or_burner, &b); + wait_ready(); + + l.name = "London"; + l.burn = *threads_spec++ == 'B'; + l.sleep = *threads_spec++ == 'S'; + l.t = 2; + pthread_create(&egll, NULL, sleeper_or_burner, &l); + wait_ready(); + + p.name = "Petaouchnok"; + p.burn = *threads_spec++ == 'B'; + p.sleep = *threads_spec++ == 'S'; + p.t = 3; + pthread_create(&zzzz, NULL, sleeper_or_burner, &p); + wait_ready(); + + m.name = "main"; + m.burn = *threads_spec++ == 'B'; + m.sleep = *threads_spec++ == 'S'; + m.t = 0; + sleeper_or_burner(&m); + + pthread_join(ebbr, NULL); + pthread_join(egll, NULL); + pthread_join(zzzz, NULL); + + return 0; +} diff --git a/gdbserver_tests/t.c b/gdbserver_tests/t.c new file mode 100644 index 00000000..86004809 --- /dev/null +++ b/gdbserver_tests/t.c @@ -0,0 +1,153 @@ +#include <string.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include "../memcheck/memcheck.h" +int using_threads = 0; /* test collision with a global in gdbserver */ +/* we will undefine one char on two */ +static char undefined[10] = "undefined"; + +#define LOOPS 10000000 +static int loopmain, loopt1, loopt2; + +static double pi = 3.14159265358979323846264338327950288; + +static pid_t gettid() +{ +#ifdef __NT_gettid + return syscall(__NR_gettid); +#else + return getpid(); +#endif +} +static void whoami(char *msg) +{ + printf("pid %d Thread %d %s\n", getpid(), gettid(), msg); fflush(stdout); +} + +static int int_und; +static int sleeps = 15; +static void make_error (char *s) +{ + char *make_error_name = "make_error name"; + char c; + double pi2 = 2.0 * pi; + whoami(s); + if (int_und == 0) + printf ("%s int_und is zero %d\n", s, int_und); + else + printf ("%s int_und is not zero\n", s); + fflush(stdout); +} + +static void level () +{ + char *level_name = "level name"; + make_error ("called from level"); +} + +static void loops (int *loopnr) +{ + int i, j; + for (i = 0; i < LOOPS; i++) + for (j = 0; i < LOOPS; i++) + *loopnr++; +} + +static void *brussels_fn(void *v) +{ + char *brussels_name = "Brussels"; + make_error ("called from Brussels"); + loopt1 = 1; + while (! (loopt1 && loopt2 && loopmain)) + loopt1++; + loops (&loopt1); + return NULL; +} +static void *london_fn(void *v) +{ + char *london_name = "London"; + make_error ("called from London"); + loopt2 = 1; + while (! (loopt1 && loopt2 && loopmain)) + loopt2++; + loops (&loopt2); + sleep(10); + return NULL; +} +static void *petaouchnok_fn(void *v) +{ + char *petaouchnok_name = "Petaouchnok"; + struct timeval t; + int i; + for (i = 1; i <= sleeps; i++) { + t.tv_sec = 5; + t.tv_usec = 0; + fprintf (stderr, "Petaouchnok sleep nr %d out of %d sleeping 5 seconds\n", + i, sleeps); + fflush(stderr); + select (0, NULL, NULL, NULL, &t); + } + return NULL; +} +static void leaf(void) {} +static void breakme(int line) +{ + if (line > 1000) + leaf(); // ensures not leaf, as ppc unwind implies VEX iropt precise exns +} +int main (int argc, char *argv[]) +{ + char *main_name = "main name"; + pthread_t ebbr, egll, zzzz; + int i = 1234; + char undef = '?'; + char *some_mem = malloc(100); + VALGRIND_MAKE_MEM_UNDEFINED(&undef, 1); + int len = strlen(undefined); + breakme(__LINE__); //break1 + for (i = len-1; i >= 0; i=i-2) + undefined[i] = undef; + *(char*)&int_und = undef; + + breakme(__LINE__); //break2 + + if (argc > 1) + sleeps = atoi(argv[1]); + + level(); + make_error ("called from main"); + + pthread_create(&ebbr, NULL, brussels_fn, NULL); + pthread_create(&egll, NULL, london_fn, NULL); + pthread_create(&zzzz, NULL, petaouchnok_fn, NULL); + + loopmain = 1; + while (! (loopt1 && loopt2 && loopmain)) + loopmain++; + for (i = 0; i < LOOPS; i++) { + loopmain++; + + if (loopmain == 10000) + make_error ("in main loop"); + } + + pthread_join(ebbr, NULL); + + make_error ("called from main (the end, before joining t3)"); + + pthread_join(zzzz, NULL); + + if (argc > 2) { + for (i = 0; i < 100; i++) + if ((*(&undef + i*4000) == 0) || (*(&undef - i*4000) == 0)) { + printf ("there are some null bytes here and there %d\n", i); + fflush(stdout); + } + } + exit(0); +} diff --git a/gdbserver_tests/watchpoints.c b/gdbserver_tests/watchpoints.c new file mode 100644 index 00000000..ae05d8fc --- /dev/null +++ b/gdbserver_tests/watchpoints.c @@ -0,0 +1,67 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +static void breakme(int line) +{ + fprintf(stderr, "breakme function called from line %d\n", line); + fflush(stderr); +} +static char undefined[10] = "undefined"; +int main (int argc, char *argv[]) +{ + + /* we will test + read watchpoint at 0, + read/write watchpoints at 4 + write watchpoints at 8 */ + + breakme(__LINE__); //break1 + + /* We verify read watchpoints are triggered at 0 and 4, not at 8 */ + fprintf(stderr, "before reading 0/4/8\n"); + if (undefined[0] == 'u') + fprintf(stderr, "u: Expected value at 0\n"); + else + fprintf(stderr, "u: Unexpected value at 0\n"); + + if (undefined[4] == 'f') + fprintf(stderr, "f: Expected value at 4\n"); + else + fprintf(stderr, "f: Unexpected value at 4\n"); + + if (undefined[8] == 'd') + fprintf(stderr, "d: Expected value at 8\n"); + else + fprintf(stderr, "d: Unexpected value at 8\n"); + + + /* We verify write watchpoints are triggered at 4 and 8, not at 0 */ + fprintf(stderr, "before writing 0\n"); + undefined[0] = 'U'; + + fprintf(stderr, "before writing 4\n"); + undefined[4] = 'F'; + + fprintf(stderr, "before writing 8\n"); + undefined[8] = 'D'; + + fprintf(stderr, "after writing 8\n"); + + /* after having remove the watchpoints, check we can read and write + without break. */ + fprintf(stderr, "value %s\n", undefined); + + fprintf(stderr, "before rewriting 0\n"); + undefined[0] = '0'; + + fprintf(stderr, "before rewriting 4\n"); + undefined[4] = '4'; + + fprintf(stderr, "before rewriting 8\n"); + undefined[8] = '8'; + + fprintf(stderr, "value %s\n", undefined); + + exit(0); +} diff --git a/include/pub_tool_gdbserver.h b/include/pub_tool_gdbserver.h new file mode 100644 index 00000000..514bcaa9 --- /dev/null +++ b/include/pub_tool_gdbserver.h @@ -0,0 +1,179 @@ + +/*--------------------------------------------------------------------*/ +/*--- Handle remote gdb protocol. pub_tool_gdbserver.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2011 Philippe Waroquiers + + 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. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PUB_TOOL_GDBSERVER_H +#define __PUB_TOOL_GDBSERVER_H + +#include "libvex.h" +#include "libvex_ir.h" + +//-------------------------------------------------------------------- +// PURPOSE: This module provides the support to have a gdb +// connecting to a valgrind process using remote gdb protocol. It provides +// * A function to allow a tool (or the valgrind core) to +// wait for a gdb to connect and then handle gdb commands. +// Typically, this can be used to let the user debug the process +// when valgrind reports an error. +// * A function allowing to instrument the code to support gdb breakpoints. +// * A function allowing the tool to support watchpoints. +// * A utility function to help implementing the processing of the +// gdb_monitor_command strings. + + +// Function to be used by tool or coregrind to allow a gdb to connect +// to this process. +// Calling VG_(gdbserver) with tid > 0 means to let a debugger attach +// to the valgrind process. gdbserver will report to gdb that the +// process stopped in thread tid. +// tid == 0 indicates to stop gdbserver and report to gdb +// that the valgrind-ified process has exited. +//-------------------------------------------------------------------- +extern void VG_(gdbserver) ( ThreadId tid ); + +/* VG_(dyn_vgdb_error) gets its initial value from + VG_(clo_vgdb_error). It can be changed after initial command + processing in order to enable/disable the call to VG_(gdbserver) in + m_errormgr.c. The main reasons to change the below is either + because the user updates it via a monitor command or to + (temporarily) avoid calling gdbserver for error reporting during + monitor command handling. +*/ +extern Int VG_(dyn_vgdb_error); + +/* defines the various kinds of breakpoints that gdbserver + might ask to insert/remove. Note that the below matches + the gdbserver protocol definition. The level of support + of the various breakpoint kinds depends on the tool. + + For the moment, it is unclear how a tool would implement + hardware_breakpoint in valgrind :). + + software_breakpoint implies some (small) specific + instrumentation to be done for gdbserver. This instrumentation + is implemented for all tools in m_translate.c. + + write/read/access watchpoints can only be done by tools + which are maintaining some notion of address accessibility + as part of their instrumentation. watchpoints can then + be done by marking the watched address(es) as not accessible. + But instead of giving back an error (or whatever the tool + wants to do with unaccessible mechanism), the tool must then + just call gdbserver. See memcheck for an example of reusing + accessibility for watchpoint support. +*/ +typedef + enum { + software_breakpoint, + hardware_breakpoint, + write_watchpoint, + read_watchpoint, + access_watchpoint } PointKind; +extern char* VG_(ppPointKind) (PointKind kind); + + +/* watchpoint support --------------------------------------*/ +/* True if one or more bytes in [addr, addr+len[ are being watched by + gdbserver for write or read or access. + In addition, VG_(is_watched) will invoke gdbserver if + the access provided by the tool matches the watchpoint kind. + For this, the tool must pass the kind of access it has detected: + write_watchpoint indicates the tool has detected a write + read_watchpoint indicates the tool has detected a read + access_watchpoint indicates the tool has detected an access but does + not know if this is a read or a write +*/ +extern Bool VG_(is_watched)(PointKind kind, Addr addr, Int szB); + +extern void VG_(needs_watchpoint) ( + // indicates the given Addr/len is being watched (insert) + // or not watched anymore (! insert). + // gdbserver will maintain the list of watched addresses. + // The tool can use VG_(is_watched) to verify if an + // access to an Addr is in one of the watched intervals. + // Must return True if the watchpoint has been properly inserted or + // removed. False if not supported. + // Note that an address can only be watched for a single kind. + // The tool must be ready to be called successively with + // multiple kinds for the same addr and len and with + // different kinds. The last kind must replace the previous values. + // Behaviour with multiple watches having overlapping addr+len + // is undefined. + Bool (*watchpoint) (PointKind kind, Bool insert, Addr addr, SizeT len) +); + + +// can be used during the processing of the VG_USERREQ__GDB_MONITOR_COMMAND +// tool client request to output information to gdb or vgdb. +extern UInt VG_(gdb_printf) ( const HChar *format, ... ) PRINTF_CHECK(1, 2); + +/* Utility functions to (e.g.) parse gdb monitor commands. + + keywords is a set of keywords separated by a space + keyword_id will search for the keyword starting with the string input_word + and return its position. + It returns -1 if no keyword matches. + It returns -2 if two or more keywords are starting with input_word + and none of these matches exactly input_word + Example with keywords = "hello world here is hell" : + input_word result + ---------- ------ + paradise => -1 + i => 3 + hell => 4 + hel => -2 + ishtar => -1 + + report indicates when to output an error msg with VG_(gdb_printf). + kwd_report_none : no error is reported. + kwd_report_all : the error msg will show all possible keywords + kwd_report_duplicated_matches : the error msg will show only the + ambiguous matches. +*/ +typedef + enum { + kwd_report_none, + kwd_report_all, + kwd_report_duplicated_matches } kwd_report_error; +extern Int VG_(keyword_id) (Char* keywords, Char* input_word, + kwd_report_error report); + +/* Extract an address and (optionally) a size from the string + currently being parsed by strtok_r (see pub_tool_libcbase.h). + If no size in the string, keeps the current value of szB. + Returns address 0 and szB 0 if there is an error. Reports to the + user problems via VG_(gdb_printf). */ +extern void VG_(strtok_get_address_and_size) (Addr* address, + SizeT* szB, + Char **ssaveptr); + +#endif // __PUB_TOOL_GDBSERVER_H + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/include/pub_tool_libcbase.h b/include/pub_tool_libcbase.h index 2897235c..0e616913 100644 --- a/include/pub_tool_libcbase.h +++ b/include/pub_tool_libcbase.h @@ -63,6 +63,8 @@ extern Char VG_(tolower) ( Char c ); // If you really want that behaviour, you can use "VG_(strtoll10)(str, NULL)". extern Long VG_(strtoll10) ( Char* str, Char** endptr ); extern Long VG_(strtoll16) ( Char* str, Char** endptr ); +extern ULong VG_(strtoull10) ( Char* str, Char** endptr ); +extern ULong VG_(strtoull16) ( Char* str, Char** endptr ); // Convert a string to a double. After leading whitespace is ignored, a // '+' or '-' is allowed, and then it accepts a non-empty sequence of @@ -97,6 +99,16 @@ extern Char* VG_(strrchr) ( const Char* s, Char c ); extern SizeT VG_(strspn) ( const Char* s, const Char* accpt ); extern SizeT VG_(strcspn) ( const Char* s, const char* reject ); +/* strtok* functions and some parsing utilities. */ +extern Char* VG_(strtok_r) (Char* s, const Char* delim, Char** saveptr); +extern Char* VG_(strtok) (Char* s, const Char* delim); + +/* Parse a 32- or 64-bit hex number, including leading 0x, from string + starting at *ppc, putting result in *result, and return True. Or + fail, in which case *ppc and *result are undefined, and return + False. */ +extern Bool VG_(parse_Addr) ( UChar** ppc, Addr* result ); + /* Like strncpy(), but if 'src' is longer than 'ndest' inserts a '\0' as the last character. */ extern void VG_(strncpy_safely) ( Char* dest, const Char* src, SizeT ndest ); diff --git a/include/pub_tool_libcfile.h b/include/pub_tool_libcfile.h index 8f08cd2c..4b63a537 100644 --- a/include/pub_tool_libcfile.h +++ b/include/pub_tool_libcfile.h @@ -68,7 +68,11 @@ struct vg_stat { ULong ctime_nsec; }; +extern SysRes VG_(mknod) ( const Char* pathname, Int mode, UWord dev ); extern SysRes VG_(open) ( const Char* pathname, Int flags, Int mode ); +/* fd_open words like the open(2) system call: + returns fd if success, -1 otherwise */ +extern Int VG_(fd_open) (const Char* pathname, Int flags, Int mode); extern void VG_(close) ( Int fd ); extern Int VG_(read) ( Int fd, void* buf, Int count); extern Int VG_(write) ( Int fd, const void* buf, Int count); @@ -82,6 +86,8 @@ extern SysRes VG_(dup2) ( Int oldfd, Int newfd ); extern Int VG_(rename) ( const Char* old_name, const Char* new_name ); extern Int VG_(unlink) ( const Char* file_name ); +extern Int VG_(poll) (struct vki_pollfd *fds, Int nfds, Int timeout); + extern Int VG_(readlink)( const Char* path, Char* buf, UInt bufsize ); extern Int VG_(getdents)( Int fd, struct vki_dirent *dirp, UInt count ); diff --git a/include/pub_tool_libcproc.h b/include/pub_tool_libcproc.h index 2770dda6..423a7f75 100644 --- a/include/pub_tool_libcproc.h +++ b/include/pub_tool_libcproc.h @@ -58,11 +58,13 @@ extern Int VG_(fork) ( void); extern void VG_(execv) ( Char* filename, Char** argv ); /* --------------------------------------------------------------------- - Resource limits + Resource limits and capabilities ------------------------------------------------------------------ */ extern Int VG_(getrlimit) ( Int resource, struct vki_rlimit *rlim ); extern Int VG_(setrlimit) ( Int resource, const struct vki_rlimit *rlim ); +extern Int VG_(prctl) (Int option, + ULong arg2, ULong arg3, ULong arg4, ULong arg5); /* --------------------------------------------------------------------- pids, etc diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h index 7f854925..3dffd42b 100644 --- a/include/pub_tool_options.h +++ b/include/pub_tool_options.h @@ -146,6 +146,12 @@ extern Int VG_(clo_verbosity); /* Show tool and core statistics */ extern Bool VG_(clo_stats); +/* wait for vgdb/gdb after reporting that amount of error. + Note that this is the initial value provided from the command line. + The real value is maintained in VG_(dyn_vgdb_error) and + can be changed dynamically.*/ +extern Int VG_(clo_vgdb_error); + /* Emit all messages as XML? default: NO */ /* If clo_xml is set, various other options are set in a non-default way. See vg_main.c and mc_main.c. */ diff --git a/include/valgrind.h b/include/valgrind.h index 8933e0f0..10f3d9bc 100644 --- a/include/valgrind.h +++ b/include/valgrind.h @@ -4843,6 +4843,10 @@ typedef errors. */ VG_USERREQ__COUNT_ERRORS = 0x1201, + /* Allows a string (gdb monitor command) to be passed to the tool + Used for interaction with vgdb/gdb */ + VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, + /* These are useful and can be interpreted by any tool that tracks malloc() et al, by using vg_replace_malloc.c. */ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, diff --git a/include/vki/vki-s390x-linux.h b/include/vki/vki-s390x-linux.h index 7d554fde..ccc73a4c 100644 --- a/include/vki/vki-s390x-linux.h +++ b/include/vki/vki-s390x-linux.h @@ -296,6 +296,7 @@ typedef struct vki_sigaltstack { extend change to end of growsup vma */ +#define VKI_MAP_SHARED 0x0001 /* Share changes */ #define VKI_MAP_PRIVATE 0x0002 /* */ #define VKI_MAP_FIXED 0x0010 /* */ #define VKI_MAP_ANONYMOUS 0x0020 /* */ @@ -314,6 +315,7 @@ typedef struct vki_sigaltstack { #define VKI_O_NOCTTY 00000400 /* not fcntl */ #define VKI_O_TRUNC 00001000 /* not fcntl */ #define VKI_O_APPEND 00002000 +#define VKI_O_NONBLOCK 00004000 #define VKI_AT_FDCWD -100 @@ -605,6 +607,7 @@ struct vki_pollfd { short revents; }; +#define VKI_POLLIN 0x0001 //---------------------------------------------------------------------- // From linux-2.6.16.60/include/asm-s390/ptrace.h diff --git a/massif/docs/ms-manual.xml b/massif/docs/ms-manual.xml index 51f872b9..f9cc1ff6 100644 --- a/massif/docs/ms-manual.xml +++ b/massif/docs/ms-manual.xml @@ -858,6 +858,21 @@ in a particular column, which makes following the allocation chains easier. </sect1> +<sect1 id="ms-manual.monitor-commands" xreflabel="Massif Monitor Commands"> +<title>Massif Monitor Commands</title> +<para>The Massif tool provides monitor commands handled by the Valgrind +gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>). +</para> + +<itemizedlist> + <listitem> + <para><varname>ms.snapshot [<filename>] [detailed]</varname> requests to take + a snapshot and save it in the given <filename> (default massif.vgdb.out). + If present, the 'detailed' argument indicates to take a detailed snapshot. + </para> + </listitem> +</itemizedlist> +</sect1> <sect1 id="ms-manual.clientreqs" xreflabel="Client requests"> <title>Massif Client Requests</title> diff --git a/massif/ms_main.c b/massif/ms_main.c index 67702cda..6bc49254 100644 --- a/massif/ms_main.c +++ b/massif/ms_main.c @@ -180,6 +180,7 @@ Number of snapshots: 50 #include "pub_tool_tooliface.h" #include "pub_tool_xarray.h" #include "pub_tool_clientstate.h" +#include "pub_tool_gdbserver.h" #include "valgrind.h" // For {MALLOC,FREE}LIKE_BLOCK @@ -1977,6 +1978,21 @@ static void die_mem_stack_signal(Addr a, SizeT len) //--- Client Requests ---// //------------------------------------------------------------// +static void print_monitor_help ( void ) +{ + VG_(gdb_printf) ("\n"); + VG_(gdb_printf) ("massif monitor commands:\n"); + VG_(gdb_printf) (" ms.snapshot [<filename>] [detailed]\n"); + VG_(gdb_printf) (" takes a snapshot and saves it in <filename>\n"); + VG_(gdb_printf) (" default <filename> is massif.vgdb.out\n"); + VG_(gdb_printf) (" if present, detailed argument indicates to take a detailed snapshot\n"); + VG_(gdb_printf) ("\n"); +} + + +/* Forward declaration. + return True if request recognised, False otherwise */ +static Bool handle_gdb_monitor_command (ThreadId tid, Char *req); static Bool ms_handle_client_request ( ThreadId tid, UWord* argv, UWord* ret ) { switch (argv[0]) { @@ -2003,6 +2019,15 @@ static Bool ms_handle_client_request ( ThreadId tid, UWord* argv, UWord* ret ) *ret = 0; return True; } + case VG_USERREQ__GDB_MONITOR_COMMAND: { + Bool handled = handle_gdb_monitor_command (tid, (Char*)argv[1]); + if (handled) + *ret = 1; + else + *ret = 0; + return handled; + } + default: *ret = 0; return False; @@ -2287,19 +2312,13 @@ static void pp_snapshot(Int fd, Snapshot* snapshot, Int snapshot_n) } } -static void write_snapshots_to_file(void) +static void write_snapshots_to_file(Char* massif_out_file, + Snapshot snapshots_array[], + Int nr_elements) { Int i, fd; SysRes sres; - // Setup output filename. Nb: it's important to do this now, ie. as late - // as possible. If we do it at start-up and the program forks and the - // output file format string contains a %p (pid) specifier, both the - // parent and child will incorrectly write to the same file; this - // happened in 3.3.0. - Char* massif_out_file = - VG_(expand_file_name)("--massif-out-file", clo_massif_out_file); - sres = VG_(open)(massif_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY, VKI_S_IRUSR|VKI_S_IWUSR); if (sr_isError(sres)) { @@ -2307,11 +2326,9 @@ static void write_snapshots_to_file(void) // between multiple cachegrinded processes?), give up now. VG_(umsg)("error: can't open output file '%s'\n", massif_out_file ); VG_(umsg)(" ... so profiling results will be missing.\n"); - VG_(free)(massif_out_file); return; } else { fd = sr_Res(sres); - VG_(free)(massif_out_file); } // Print massif-specific options that were used. @@ -2342,12 +2359,75 @@ static void write_snapshots_to_file(void) FP("time_unit: %s\n", TimeUnit_to_string(clo_time_unit)); - for (i = 0; i < next_snapshot_i; i++) { - Snapshot* snapshot = & snapshots[i]; + for (i = 0; i < nr_elements; i++) { + Snapshot* snapshot = & snapshots_array[i]; pp_snapshot(fd, snapshot, i); // Detailed snapshot! } + VG_(close) (fd); +} + +static void write_snapshots_array_to_file(void) +{ + // Setup output filename. Nb: it's important to do this now, ie. as late + // as possible. If we do it at start-up and the program forks and the + // output file format string contains a %p (pid) specifier, both the + // parent and child will incorrectly write to the same file; this + // happened in 3.3.0. + Char* massif_out_file = + VG_(expand_file_name)("--massif-out-file", clo_massif_out_file); + write_snapshots_to_file (massif_out_file, snapshots, next_snapshot_i); + VG_(free)(massif_out_file); } +static Bool handle_gdb_monitor_command (ThreadId tid, Char *req) +{ + Char* wcmd; + Char s[VG_(strlen(req))]; /* copy for strtok_r */ + Char *ssaveptr; + + VG_(strcpy) (s, req); + + wcmd = VG_(strtok_r) (s, " ", &ssaveptr); + switch (VG_(keyword_id) ("help ms.snapshot", + wcmd, kwd_report_duplicated_matches)) { + case -2: /* multiple matches */ + return True; + case -1: /* not found */ + return False; + case 0: /* help */ + print_monitor_help(); + return True; + case 1: { /* ms.snapshot */ + Char* kw; + Char* filename = NULL; + Bool detailed = False; + Snapshot snapshot; + + for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr); + kw != NULL; + kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) { + if (filename == NULL) + filename = kw; + else if (0 == VG_(strncmp)(kw, "detailed", VG_(strlen) (kw))) + detailed = True; + else { + VG_(gdb_printf) ("invalid 2nd arg\n"); + return True; + } + } + clear_snapshot(&snapshot, /* do_sanity_check */ False); + take_snapshot(&snapshot, Normal, get_time(), detailed); + write_snapshots_to_file ((filename == NULL) ? (Char*) "massif.vgdb.out" : filename, + &snapshot, + 1); + delete_snapshot(&snapshot); + return True; + } + default: + tl_assert(0); + return False; + } +} //------------------------------------------------------------// //--- Finalisation ---// @@ -2356,7 +2436,7 @@ static void write_snapshots_to_file(void) static void ms_fini(Int exit_status) { // Output. - write_snapshots_to_file(); + write_snapshots_array_to_file(); // Stats tl_assert(n_xpts > 0); // always have alloc_xpt diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml index 433e2e05..8c6294c6 100644 --- a/memcheck/docs/mc-manual.xml +++ b/memcheck/docs/mc-manual.xml @@ -1270,7 +1270,126 @@ is:</para> </sect2> </sect1> +<sect1 id="mc-manual.monitor-commands" xreflabel="Memcheck Monitor Commands"> +<title>Memcheck Monitor Commands</title> +<para>The Memcheck tool provides monitor commands handled by the Valgrind +gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>). +</para> + +<itemizedlist> + <listitem> + <para><varname>mc.get_vbits <addr> [<len>]</varname> + outputs the validity bits for the range of <len> (default 1) + bytes at <addr>. The validity of each byte of the range is + given using two hexadecimal digits. These hexadecimal digits are + encoding the validity of each bit of the corresponding byte, using + 0 if the bit is valid and 1 if the bit is invalid. In the + following example, 'string10' is an array of 10 characters in + which one byte on two is undefined. If a byte is not addressable, + its validity bits are replaced by __. In the below example, the byte 6 + is not addressable.</para> +<programlisting><![CDATA[ +(gdb) p &string10 +$4 = (char (*)[10]) 0x8049e28 +(gdb) monitor mc.get_vbits 0x8049e28 10 +ff00ff00 ff__ff00 ff00 +(gdb) +]]></programlisting> + </listitem> + + <listitem> + <para><varname>mc.make_memory [noaccess|undefined|defined|ifaddressabledefined] <addr> [<len>]</varname> + marks the range of <len> (default 1) bytes at <addr> + with the given accessibility. Marking with 'noaccess' changes the + (A) bits of the range to be not addressable. Marking with + 'undefined' or 'defined' are changing the definedness of the + range. 'ifaddressabledefined' marks the range as defined but only + if the range is addressable. In the following example, the first + byte of the 'string10' is marked as defined. + </para> +<programlisting><![CDATA[ +(gdb) monitor mc.make_memory defined 0x8049e28 1 +(gdb) monitor mc.get_vbits 0x8049e28 10 +0000ff00 ff00ff00 ff00 +(gdb) +]]></programlisting> + </listitem> + <listitem> + <para><varname>mc.check_memory [addressable|defined] <addr> + [<len>]</varname> checks that the range of <len> + (default 1) bytes at <addr> has the given accessibility. It + then outputs a description of <addr>. In the below case, a + detailed description is given as the option --read-var-info=yes + was used to start Valgrind. + </para> +<programlisting><![CDATA[ +(gdb) monitor mc.check_memory defined 0x8049e28 1 +Address 0x8049E28 len 1 defined +==14698== Location 0x8049e28 is 0 bytes inside string10[0], +==14698== declared at prog.c:10, in frame #0 of thread 1 +(gdb) +]]></programlisting> + </listitem> + + <listitem> + <para><varname>mc.leak_check + [full*|summary] [reachable|leakpossible*|definiteleak]</varname> + starts a leak checking. The * in the arguments above indicates the + default value. </para> + + <para> If the first argument is 'summary', only a summary of + the leak search is given. + </para> + + <para>The second argument controls which entries are output + for a 'full' leak search. The value 'definiteleak' indicates to + output only the definitely leaked blocks. The value 'leakpossible' + will output in addition the possibly leaked blocks. The value + 'reachable' will output all blocks (reachable, possibly leaked, + definitely leaked). + </para> + <para>The below is an example of using the mc.leak_check monitor + command on the leak-cases Memcheck regression tests.</para> +<programlisting><![CDATA[ +(gdb) monitor mc.leak_check full leakpossible +==14729== 16 bytes in 1 blocks are possibly lost in loss record 13 of 16 +==14729== at 0x4006E9E: malloc (vg_replace_malloc.c:236) +==14729== by 0x80484D5: mk (leak-cases.c:52) +==14729== by 0x804855F: f (leak-cases.c:81) +==14729== by 0x80488F5: main (leak-cases.c:107) +==14729== +==14729== LEAK SUMMARY: +==14729== definitely lost: 32 bytes in 2 blocks +==14729== indirectly lost: 16 bytes in 1 blocks +==14729== possibly lost: 32 bytes in 2 blocks +==14729== still reachable: 96 bytes in 6 blocks +==14729== suppressed: 0 bytes in 0 blocks +==14729== Reachable blocks (those to which a pointer was found) are not shown. +==14729== To see them, rerun with: --leak-check=full --show-reachable=yes +==14729== +(gdb) mo mc.l +==14729== LEAK SUMMARY: +==14729== definitely lost: 32 bytes in 2 blocks +==14729== indirectly lost: 16 bytes in 1 blocks +==14729== possibly lost: 32 bytes in 2 blocks +==14729== still reachable: 96 bytes in 6 blocks +==14729== suppressed: 0 bytes in 0 blocks +==14729== Reachable blocks (those to which a pointer was found) are not shown. +==14729== To see them, rerun with: --leak-check=full --show-reachable=yes +==14729== +(gdb) +]]></programlisting> + <para>Note that when using the Valgrind gdbserver, it is not + needed to rerun with --leak-check=full --show-reachable=yes to see + the reachable blocks. You can obtain the same information without + rerunning by using the gdb command 'monitor mc.leak_check full + reachable' (or, using abbreviation: 'mo mc.l f r'). + </para> + </listitem> +</itemizedlist> + +</sect1> <sect1 id="mc-manual.clientreqs" xreflabel="Client requests"> <title>Client Requests</title> diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c index e09d5147..e0133289 100644 --- a/memcheck/mc_errors.c +++ b/memcheck/mc_errors.c @@ -30,6 +30,7 @@ */ #include "pub_tool_basics.h" +#include "pub_tool_gdbserver.h" #include "pub_tool_hashtable.h" // For mc_include.h #include "pub_tool_libcbase.h" #include "pub_tool_libcassert.h" @@ -787,6 +788,9 @@ void MC_(record_address_error) ( ThreadId tid, Addr a, Int szB, if (MC_(in_ignored_range)(a)) return; + if (VG_(is_watched)( (isWrite ? write_watchpoint : read_watchpoint), a, szB)) + return; + # if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) /* AIX zero-page handling. On AIX, reads from page zero are, bizarrely enough, legitimate. Writes to page zero aren't, @@ -1182,6 +1186,15 @@ static void describe_addr ( Addr a, /*OUT*/AddrInfo* ai ) return; } +void MC_(pp_describe_addr) ( Addr a ) +{ + AddrInfo ai; + + ai.tag = Addr_Undescribed; + describe_addr (a, &ai); + mc_pp_AddrInfo (a, &ai, /* maybe_gcc */ False); +} + /* Fill in *origin_ec as specified by otag, or NULL it out if otag does not refer to a known origin. */ static void update_origin ( /*OUT*/ExeContext** origin_ec, diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index ece6eac3..d6edbf2d 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -355,6 +355,9 @@ Bool MC_(record_leak_error) ( ThreadId tid, Bool print_record, Bool count_error ); +/* prints a description of address a */ +void MC_(pp_describe_addr) (Addr a); + /* Is this address in a user-specified "ignored range" ? */ Bool MC_(in_ignored_range) ( Addr a ); diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index 47122215..0cdb4a50 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -32,6 +32,7 @@ #include "pub_tool_basics.h" #include "pub_tool_aspacemgr.h" +#include "pub_tool_gdbserver.h" #include "pub_tool_hashtable.h" // For mc_include.h #include "pub_tool_libcbase.h" #include "pub_tool_libcassert.h" @@ -1049,67 +1050,17 @@ INLINE Bool MC_(in_ignored_range) ( Addr a ) return False; } - -/* Parse a 32- or 64-bit hex number, including leading 0x, from string - starting at *ppc, putting result in *result, and return True. Or - fail, in which case *ppc and *result are undefined, and return - False. */ - -static Bool isHex ( UChar c ) -{ - return ((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F')); -} - -static UInt fromHex ( UChar c ) -{ - if (c >= '0' && c <= '9') - return (UInt)c - (UInt)'0'; - if (c >= 'a' && c <= 'f') - return 10 + (UInt)c - (UInt)'a'; - if (c >= 'A' && c <= 'F') - return 10 + (UInt)c - (UInt)'A'; - /*NOTREACHED*/ - tl_assert(0); - return 0; -} - -static Bool parse_Addr ( UChar** ppc, Addr* result ) -{ - Int used, limit = 2 * sizeof(Addr); - if (**ppc != '0') - return False; - (*ppc)++; - if (**ppc != 'x') - return False; - (*ppc)++; - *result = 0; - used = 0; - while (isHex(**ppc)) { - UInt d = fromHex(**ppc); - tl_assert(d < 16); - *result = ((*result) << 4) | fromHex(**ppc); - (*ppc)++; - used++; - if (used > limit) return False; - } - if (used == 0) - return False; - return True; -} - -/* Parse two such numbers separated by a dash, or fail. */ +/* Parse two Addr separated by a dash, or fail. */ static Bool parse_range ( UChar** ppc, Addr* result1, Addr* result2 ) { - Bool ok = parse_Addr(ppc, result1); + Bool ok = VG_(parse_Addr) (ppc, result1); if (!ok) return False; if (**ppc != '-') return False; (*ppc)++; - ok = parse_Addr(ppc, result2); + ok = VG_(parse_Addr) (ppc, result2); if (!ok) return False; return True; @@ -4513,17 +4464,20 @@ static Int mc_get_or_set_vbits_for_client ( Addr a, Addr vbits, SizeT szB, - Bool setting /* True <=> set vbits, False <=> get vbits */ + Bool setting, /* True <=> set vbits, False <=> get vbits */ + Bool is_client_request /* True <=> real user request + False <=> internal call from gdbserver */ ) { SizeT i; Bool ok; UChar vbits8; - /* Check that arrays are addressible before doing any getting/setting. */ + /* Check that arrays are addressible before doing any getting/setting. + vbits to be checked only for real user request. */ for (i = 0; i < szB; i++) { if (VA_BITS2_NOACCESS == get_vabits2(a + i) || - VA_BITS2_NOACCESS == get_vabits2(vbits + i)) { + (is_client_request && VA_BITS2_NOACCESS == get_vabits2(vbits + i))) { return 3; } } @@ -4542,8 +4496,9 @@ static Int mc_get_or_set_vbits_for_client ( tl_assert(ok); ((UChar*)vbits)[i] = vbits8; } - // The bytes in vbits[] have now been set, so mark them as such. - MC_(make_mem_defined)(vbits, szB); + if (is_client_request) + // The bytes in vbits[] have now been set, so mark them as such. + MC_(make_mem_defined)(vbits, szB); } return 1; @@ -4974,6 +4929,220 @@ static void show_client_block_stats ( void ) ); } +static void print_monitor_help ( void ) +{ + VG_(gdb_printf) + ( +"\n" +"memcheck monitor commands:\n" +" mc.get_vbits <addr> [<len>]\n" +" returns validity bits for <len> (or 1) bytes at <addr>\n" +" bit values 0 = valid, 1 = invalid, __ = unaddressable byte\n" +" Example: mc.get_vbits 0x8049c78 10\n" +" mc.make_memory [noaccess|undefined\n" +" |defined|ifaddressabledefined] <addr> [<len>]\n" +" mark <len> (or 1) bytes at <addr> with the given accessibility\n" +" mc.check_memory [addressable|defined] <addr> [<len>]\n" +" check that <len> (or 1) bytes at <addr> have the given accessibility\n" +" and outputs a description of <addr>\n" +" mc.leak_check [full*|summary]\n" +" [reachable|leakpossible*|definiteleak]\n" +" * = defaults\n" +" Examples: mc.leak_check\n" +" mc.leak_check any summary\n" +"\n"); +} + +/* return True if request recognised, False otherwise */ +static Bool handle_gdb_monitor_command (ThreadId tid, Char *req) +{ + Char* wcmd; + Char s[VG_(strlen(req))]; /* copy for strtok_r */ + Char *ssaveptr; + + VG_(strcpy) (s, req); + + wcmd = VG_(strtok_r) (s, " ", &ssaveptr); + /* NB: if possible, avoid introducing a new command below which + starts with the same 4 first letters as an already existing + command. This ensures a shorter abbreviation for the user. */ + switch (VG_(keyword_id) + ("help mc.get_vbits mc.leak_check mc.make_memory mc.check_memory", + wcmd, kwd_report_duplicated_matches)) { + case -2: /* multiple matches */ + return True; + case -1: /* not found */ + return False; + case 0: /* help */ + print_monitor_help(); + return True; + case 1: { /* mc.get_vbits */ + Addr address; + SizeT szB = 1; + VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr); + if (szB != 0) { + UChar vbits; + Int i; + Int unaddressable = 0; + for (i = 0; i < szB; i++) { + Int res = mc_get_or_set_vbits_for_client + (address+i, (Addr) &vbits, 1, + False, /* get them */ + False /* is client request */ ); + if ((i % 32) == 0 && i != 0) + VG_(gdb_printf) ("\n"); + else if ((i % 4) == 0 && i != 0) + VG_(gdb_printf) (" "); + if (res == 1) { + VG_(gdb_printf) ("%02x", vbits); + } else { + tl_assert(3 == res); + unaddressable++; + VG_(gdb_printf) ("__"); + } + } + if ((i % 80) != 0) + VG_(gdb_printf) ("\n"); + if (unaddressable) { + VG_(gdb_printf) + ("Address %p len %ld has %d bytes unaddressable\n", + (void *)address, szB, unaddressable); + } + } + return True; + } + case 2: { /* mc.leak_check */ + Int err = 0; + Bool save_clo_show_reachable = MC_(clo_show_reachable); + Bool save_clo_show_possibly_lost = MC_(clo_show_possibly_lost); + Char* kw; + + LeakCheckMode mode; + + MC_(clo_show_reachable) = False; + mode = LC_Full; + + for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr); + kw != NULL; + kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) { + switch (VG_(keyword_id) + ("full summary " + "reachable leakpossible definiteleak", + kw, kwd_report_all)) { + case -2: err++; break; + case -1: err++; break; + case 0: mode = LC_Full; break; + case 1: mode = LC_Summary; break; + case 2: MC_(clo_show_reachable) = True; + MC_(clo_show_possibly_lost) = True; break; + case 3: MC_(clo_show_reachable) = False; + MC_(clo_show_possibly_lost) = True; break; + case 4: MC_(clo_show_reachable) = False; + MC_(clo_show_possibly_lost) = False; break; + default: tl_assert (0); + } + } + if (!err) + MC_(detect_memory_leaks)(tid, mode); + + MC_(clo_show_reachable) = save_clo_show_reachable; + MC_(clo_show_possibly_lost) = save_clo_show_possibly_lost; + return True; + } + + case 3: { /* mc.make_memory */ + Addr address; + SizeT szB = 1; + int kwdid = VG_(keyword_id) + ("noaccess undefined defined ifaddressabledefined", + VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all); + VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr); + if (address == (Addr) 0 && szB == 0) return True; + switch (kwdid) { + case -2: break; + case -1: break; + case 0: MC_(make_mem_noaccess) (address, szB); break; + case 1: make_mem_undefined_w_tid_and_okind ( address, szB, tid, + MC_OKIND_USER ); break; + case 2: MC_(make_mem_defined) ( address, szB ); break; + case 3: make_mem_defined_if_addressable ( address, szB ); break;; + default: tl_assert(0); + } + return True; + } + + case 4: { /* mc.check_memory */ + Addr address; + SizeT szB = 1; + Addr bad_addr; + UInt okind; + char* src; + UInt otag; + UInt ecu; + ExeContext* origin_ec; + MC_ReadResult res; + + int kwdid = VG_(keyword_id) + ("addressable defined", + VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all); + VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr); + if (address == (Addr) 0 && szB == 0) return True; + switch (kwdid) { + case -2: break; + case -1: break; + case 0: + if (is_mem_addressable ( address, szB, &bad_addr )) + VG_(gdb_printf) ("Address %p len %ld addressable\n", + (void *)address, szB); + else + VG_(gdb_printf) + ("Address %p len %ld not addressable:\nbad address %p\n", + (void *)address, szB, (void *) bad_addr); + MC_(pp_describe_addr) (address); + break; + case 1: res = is_mem_defined ( address, szB, &bad_addr, &otag ); + if (MC_AddrErr == res) + VG_(gdb_printf) + ("Address %p len %ld not addressable:\nbad address %p\n", + (void *)address, szB, (void *) bad_addr); + else if (MC_ValueErr == res) { + okind = otag & 3; + switch (okind) { + case MC_OKIND_STACK: + src = " was created by a stack allocation"; break; + case MC_OKIND_HEAP: + src = " was created by a heap allocation"; break; + case MC_OKIND_USER: + src = " was created by a client request"; break; + case MC_OKIND_UNKNOWN: + src = ""; break; + default: tl_assert(0); + } + VG_(gdb_printf) + ("Address %p len %ld not defined:\n" + "Uninitialised value at %p%s\n", + (void *)address, szB, (void *) bad_addr, src); + ecu = otag & ~3; + if (VG_(is_plausible_ECU)(ecu)) { + origin_ec = VG_(get_ExeContext_from_ECU)( ecu ); + VG_(pp_ExeContext)( origin_ec ); + } + } + else + VG_(gdb_printf) ("Address %p len %ld defined\n", + (void *)address, szB); + MC_(pp_describe_addr) (address); + break; + default: tl_assert(0); + } + return True; + } + + default: + tl_assert(0); + return False; + } +} /*------------------------------------------------------------*/ /*--- Client requests ---*/ @@ -4996,7 +5165,8 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) && VG_USERREQ__MEMPOOL_TRIM != arg[0] && VG_USERREQ__MOVE_MEMPOOL != arg[0] && VG_USERREQ__MEMPOOL_CHANGE != arg[0] - && VG_USERREQ__MEMPOOL_EXISTS != arg[0]) + && VG_USERREQ__MEMPOOL_EXISTS != arg[0] + && VG_USERREQ__GDB_MONITOR_COMMAND != arg[0]) return False; switch (arg[0]) { @@ -5074,12 +5244,16 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) case VG_USERREQ__GET_VBITS: *ret = mc_get_or_set_vbits_for_client - ( arg[1], arg[2], arg[3], False /* get them */ ); + ( arg[1], arg[2], arg[3], + False /* get them */, + True /* is client request */ ); break; case VG_USERREQ__SET_VBITS: *ret = mc_get_or_set_vbits_for_client - ( arg[1], arg[2], arg[3], True /* set them */ ); + ( arg[1], arg[2], arg[3], + True /* set them */, + True /* is client request */ ); break; case VG_USERREQ__COUNT_LEAKS: { /* count leaked bytes */ @@ -5215,6 +5389,14 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) return True; } + case VG_USERREQ__GDB_MONITOR_COMMAND: { + Bool handled = handle_gdb_monitor_command (tid, (Char*)arg[1]); + if (handled) + *ret = 1; + else + *ret = 0; + return handled; + } default: VG_(message)( @@ -5790,6 +5972,22 @@ static void mc_fini ( Int exitcode ) } } +/* mark the given addr/len unaddressable for watchpoint implementation + The PointKind will be handled at access time */ +static Bool mc_mark_unaddressable_for_watchpoint (PointKind kind, Bool insert, + Addr addr, SizeT len) +{ + /* GDBTD this is somewhat fishy. We might rather have to save the previous + accessibility and definedness in gdbserver so as to allow restoring it + properly. Currently, we assume that the user only watches things + which are properly addressable and defined */ + if (insert) + MC_(make_mem_noaccess) (addr, len); + else + MC_(make_mem_defined) (addr, len); + return True; +} + static void mc_pre_clo_init(void) { VG_(details_name) ("Memcheck"); @@ -5935,6 +6133,8 @@ static void mc_pre_clo_init(void) VG_(track_post_reg_write) ( mc_post_reg_write ); VG_(track_post_reg_write_clientcall_return)( mc_post_reg_write_clientcall ); + VG_(needs_watchpoint) ( mc_mark_unaddressable_for_watchpoint ); + init_shadow_memory(); MC_(malloc_list) = VG_(HT_construct)( "MC_(malloc_list)" ); MC_(mempool_list) = VG_(HT_construct)( "MC_(mempool_list)" ); diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp index fadaa595..641304a8 100644 --- a/none/tests/cmdline1.stdout.exp +++ b/none/tests/cmdline1.stdout.exp @@ -16,6 +16,9 @@ usage: valgrind [options] prog-and-args but check the argv[] entries for children, rather than the exe name, to make a follow/no-follow decision --child-silent-after-fork=no|yes omit child output between fork & exec? [no] + --vgdb=no|yes|full activate gdbserver? [yes] + full is slower but provides precise watchpoint/step + --vgdb-error=<number> invoke gdbserver after <number> errors [999999999] --track-fds=no|yes track open file descriptors? [no] --time-stamp=no|yes add timestamps to log messages? [no] --log-fd=<number> log messages to file descriptor [2=stderr] @@ -60,6 +63,9 @@ usage: valgrind [options] prog-and-args and use it to print better error messages in tools that make use of it (Memcheck, Helgrind, DRD) [no] + --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000] + --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no] + --vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe] --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes] --sim-hints=hint1,hint2,... known hints: lax-ioctls, enable-outer [none] diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp index 6603e25b..e66858d3 100644 --- a/none/tests/cmdline2.stdout.exp +++ b/none/tests/cmdline2.stdout.exp @@ -16,6 +16,9 @@ usage: valgrind [options] prog-and-args but check the argv[] entries for children, rather than the exe name, to make a follow/no-follow decision --child-silent-after-fork=no|yes omit child output between fork & exec? [no] + --vgdb=no|yes|full activate gdbserver? [yes] + full is slower but provides precise watchpoint/step + --vgdb-error=<number> invoke gdbserver after <number> errors [999999999] --track-fds=no|yes track open file descriptors? [no] --time-stamp=no|yes add timestamps to log messages? [no] --log-fd=<number> log messages to file descriptor [2=stderr] @@ -60,6 +63,9 @@ usage: valgrind [options] prog-and-args and use it to print better error messages in tools that make use of it (Memcheck, Helgrind, DRD) [no] + --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000] + --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no] + --vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe] --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes] --sim-hints=hint1,hint2,... known hints: lax-ioctls, enable-outer [none] diff --git a/none/tests/valgrind_cpp_test.cpp b/none/tests/valgrind_cpp_test.cpp index f4edade7..41a522b5 100644 --- a/none/tests/valgrind_cpp_test.cpp +++ b/none/tests/valgrind_cpp_test.cpp @@ -9,9 +9,9 @@ #include "pub_tool_libcbase.h" #include "pub_tool_mallocfree.h" #include "pub_tool_libcprint.h" +#include "pub_tool_vki.h" #include "pub_tool_libcfile.h" #include "pub_tool_libcproc.h" -#include "pub_tool_vki.h" #include "pub_tool_threadstate.h" #include "pub_tool_errormgr.h" #include "pub_tool_options.h" diff --git a/tests/vg_regtest.in b/tests/vg_regtest.in index adf3b9aa..68e31388 100755 --- a/tests/vg_regtest.in +++ b/tests/vg_regtest.in @@ -55,12 +55,21 @@ # multiple are allowed) # - stdout_filter: <filter to run stdout through> (default: none) # - stderr_filter: <filter to run stderr through> (default: ./filter_stderr) +# +# - progB: <prog to run in parallel with prog> (default: none) +# - argsB: <args for progB> (default: none) +# - stdinB: <input file for progB> (default: none) +# - stdoutB_filter: <filter progB stdout through> (default: none) +# - stderrB_filter: <filter progB stderr through> (default: ./filter_stderr) +# # - prereq: <prerequisite command> (default: none) # - post: <post-test check command> (default: none) # - cleanup: <post-test cleanup cmd> (default: none) # +# If prog or probB is a relative path, it will be prefix with the test directory. # Note that filters are necessary for stderr results to filter out things that # always change, eg. process id numbers. +# Note that if a progB is specified, it is started in background (before prog). # # Expected stdout (filtered) is kept in <test>.stdout.exp* (can be more # than one expected output). It can be missing if it would be empty. Expected @@ -68,8 +77,13 @@ # one stderr.exp* file. Any .exp* file that ends in '~' or '#' is ignored; # this is because Emacs creates temporary files of these names. # +# Expected output for progB is handled similarly, except that +# expected stdout and stderr for progB are in <test>.stdoutB.exp* +# and <test>.stderrB.exp*. +# # If results don't match, the output can be found in <test>.std<strm>.out, # and the diff between expected and actual in <test>.std<strm>.diff*. +# (for progB, in <test>.std<strm>2.out and <test>.std<strm>2.diff*). # # The prerequisite command, if present, works like this: # - if it returns 0 the test is run @@ -112,6 +126,11 @@ my $prog; # test prog my $args; # test prog args my $stdout_filter; # filter program to run stdout results file through my $stderr_filter; # filter program to run stderr results file through +my $progB; # Same but for progB +my $argsB; # +my $stdoutB_filter; # +my $stderrB_filter; # +my $stdinB; # Input file for progB my $prereq; # prerequisite test to satisfy before running test my $post; # check command after running test my $cleanup; # cleanup command to run @@ -119,7 +138,9 @@ my $cleanup; # cleanup command to run my @failures; # List of failed tests my $num_tests_done = 0; -my %num_failures = (stderr => 0, stdout => 0, post => 0); +my %num_failures = (stderr => 0, stdout => 0, + stderrB => 0, stdoutB => 0, + post => 0); # Default valgrind to use is this build tree's (uninstalled) one my $valgrind = "./coregrind/valgrind"; @@ -204,12 +225,16 @@ sub read_vgtest_file($) my ($f) = @_; # Defaults. - ($vgopts, $prog, $args) = ("", undef, ""); - ($stdout_filter, $stderr_filter) = (undef, undef); - ($prereq, $post, $cleanup) = (undef, undef, undef); + ($vgopts, $prog, $args) = ("", undef, ""); + ($stdout_filter, $stderr_filter) = (undef, undef); + ($progB, $argsB, $stdinB) = (undef, "", undef); + ($stdoutB_filter, $stderrB_filter) = (undef, undef); + ($prereq, $post, $cleanup) = (undef, undef, undef); # Every test directory must have a "filter_stderr" $stderr_filter = validate_program(".", $default_stderr_filter, 1, 1); + $stderrB_filter = validate_program(".", $default_stderr_filter, 1, 1); + open(INPUTFILE, "< $f") || die "File $f not openable\n"; @@ -228,6 +253,16 @@ sub read_vgtest_file($) $stdout_filter = validate_program(".", $1, 1, 1); } elsif ($line =~ /^\s*stderr_filter:\s*(.*)$/) { $stderr_filter = validate_program(".", $1, 1, 1); + } elsif ($line =~ /^\s*progB:\s*(.*)$/) { + $progB = validate_program(".", $1, 0, 0); + } elsif ($line =~ /^\s*argsB:\s*(.*)$/) { + $argsB = $1; + } elsif ($line =~ /^\s*stdinB:\s*(.*)$/) { + $stdinB = $1; + } elsif ($line =~ /^\s*stdoutB_filter:\s*(.*)$/) { + $stdoutB_filter = validate_program(".", $1, 1, 1); + } elsif ($line =~ /^\s*stderrB_filter:\s*(.*)$/) { + $stderrB_filter = validate_program(".", $1, 1, 1); } elsif ($line =~ /^\s*prereq:\s*(.*)$/) { $prereq = $1; } elsif ($line =~ /^\s*post:\s*(.*)$/) { @@ -333,8 +368,27 @@ sub do_one_test($$) } } - printf("%-16s valgrind $extraopts $vgopts $prog $args\n", "$name:"); + if (defined $progB) { + # If there is a progB, let's start it in background: + printf("%-16s valgrind $extraopts $vgopts $prog $args (progB: $progB $argsB)\n", + "$name:"); + # progB.done used to detect child has finished. See below. + # Note: redirection of stdout and stderr is before $progB to allow argsB + # to e.g. redirect stdoutB to stderrB + if (defined $stdinB) { + mysystem("(rm -f progB.done;" + . " < $stdinB > $name.stdoutB.out 2> $name.stderrB.out $progB $argsB;" + . "touch progB.done) &"); + } else { + mysystem("(rm -f progB.done;" + . " > $name.stdoutB.out 2> $name.stderrB.out $progB $argsB;" + . "touch progB.done) &"); + } + } else { + printf("%-16s valgrind $extraopts $vgopts $prog $args\n", "$name:"); + } + # Pass the appropriate --tool option for the directory (can be overridden # by an "args:" line, though). Set both VALGRIND_LIB and # VALGRIND_LIB_INNER in case this Valgrind was configured with @@ -363,6 +417,38 @@ sub do_one_test($$) (0 != scalar @stderr_exps) or die "Could not find `$name.stderr.exp*'\n"; do_diffs($fullname, $name, "stderr", \@stderr_exps); + if (defined $progB) { + # wait for the child to be finished + # tried things such as: + # wait; + # $SIG{CHLD} = sub { wait }; + # but nothing worked: + # e.g. running mssnapshot.vgtest in a loop failed from time to time + # due to some missing output (not yet written?). + # So, we search progB.done during max 100 times 100 millisecond. + my $count; + for ($count = 1; $count <= 100; $count++) { + (-f "progB.done") or select(undef, undef, undef, 0.100); + } + # Filter stdout + if (defined $stdoutB_filter) { + mysystem("$stdoutB_filter < $name.stdoutB.out > $tmp"); + rename($tmp, "$name.stdoutB.out"); + } + # Find all the .stdoutB.exp files. If none, use /dev/null. + my @stdoutB_exps = <$name.stdoutB.exp*>; + @stdoutB_exps = ( "/dev/null" ) if (0 == scalar @stdoutB_exps); + do_diffs($fullname, $name, "stdoutB", \@stdoutB_exps); + + # Filter stderr + mysystem("$stderrB_filter < $name.stderrB.out > $tmp"); + rename($tmp, "$name.stderrB.out"); + # Find all the .stderrB.exp files. At least one must exist. + my @stderrB_exps = <$name.stderrB.exp*>; + (0 != scalar @stderrB_exps) or die "Could not find `$name.stderrB.exp*'\n"; + do_diffs($fullname, $name, "stderrB", \@stderrB_exps); + } + # Maybe do post-test check if (defined $post) { if (mysystem("$post > $name.post.out") != 0) { @@ -449,10 +535,13 @@ sub summarise_results my $x = ( $num_tests_done == 1 ? "test" : "tests" ); printf("\n== %d test%s, %d stderr failure%s, %d stdout failure%s, " + . "%d stderrB failure%s, %d stdoutB failure%s, " . "%d post failure%s ==\n", $num_tests_done, plural($num_tests_done), $num_failures{"stderr"}, plural($num_failures{"stderr"}), $num_failures{"stdout"}, plural($num_failures{"stdout"}), + $num_failures{"stderrB"}, plural($num_failures{"stderrB"}), + $num_failures{"stdoutB"}, plural($num_failures{"stdoutB"}), $num_failures{"post"}, plural($num_failures{"post"})); foreach my $failure (@failures) { @@ -508,6 +597,8 @@ if ($ENV{"EXTRA_REGTEST_OPTS"}) { if (0 == $num_failures{"stdout"} && 0 == $num_failures{"stderr"} && + 0 == $num_failures{"stdoutB"} && + 0 == $num_failures{"stderrB"} && 0 == $num_failures{"post"}) { exit 0; } else { |