This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH V4 7/9] Simple testsuite for DTrace USDT probes.


On Monday, February 02 2015, Jose E. Marchesi wrote:

> This patch adds some simple tests testing the support for DTrace USDT
> probes.  The testsuite will be skipped as unsupported in case the user
> does not have DTrace installed on her system.  The tests included in the
> test suite test breakpointing on DTrace probes, enabling and disabling
> probes, printing of probe arguments of several types and also
> breakpointing on several probes with the same name.

Thanks again for this awesome testcase.  I don't have any more comments
to make about the patch itself; I personally liked your way of solving
the transform problem, though I have a preference to see those changes
in a different patch (i.e., not in the DTrace series).  Do you think you
can do that?  It should be really easy to extract this part from your
patch; and then, you'd only have to worry about the dtrace-side of it.

I can't give you an approval for the transform.m4 thing, but for the
rest, you have an OK.

Thanks,

> gdb/ChangeLog:
>
> 2015-02-02  Jose E. Marchesi  <jose.marchesi@oracle.com>
>
> 	* Makefile.in (aclocal_m4_deps): Added transform.m4.
> 	* acinclude.m4: sinclude transform.m4.
> 	* transform.m4 (GDB_AC_TRANSFORM): New macro.
> 	New file.
> 	* configure.ac: Use GDB_AC_TRANSFORM.
>
> gdb/testsuite/ChangeLog:
>
> 2015-02-02  Jose E. Marchesi  <jose.marchesi@oracle.com>
>
> 	* lib/dtrace.exp: New file.
> 	* gdb.base/dtrace-probe.exp: Likewise.
> 	* gdb.base/dtrace-probe.d: Likewise.
> 	* gdb.base/dtrace-probe.c: Likewise.
> 	* lib/pdtrace.in: Likewise.
> 	* configure.ac: Output variables with the transformed names of
> 	the strip, readelf, as and nm tools.  AC_SUBST lib/pdtrace.in.
> 	* configure: Regenerated.
> 	* aclocal.m4: sinclude ../transform.m4.
> ---
>  gdb/ChangeLog                           |    8 +
>  gdb/Makefile.in                         |    1 +
>  gdb/acinclude.m4                        |    3 +
>  gdb/configure                           |   24 +-
>  gdb/configure.ac                        |   14 +-
>  gdb/testsuite/ChangeLog                 |   12 +
>  gdb/testsuite/aclocal.m4                |    1 +
>  gdb/testsuite/configure                 |   62 ++
>  gdb/testsuite/configure.ac              |    9 +
>  gdb/testsuite/gdb.base/dtrace-probe.c   |   38 ++
>  gdb/testsuite/gdb.base/dtrace-probe.d   |   21 +
>  gdb/testsuite/gdb.base/dtrace-probe.exp |  106 ++++
>  gdb/testsuite/lib/dtrace.exp            |   71 +++
>  gdb/testsuite/lib/pdtrace.in            | 1033 +++++++++++++++++++++++++++++++
>  14 files changed, 1381 insertions(+), 22 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/dtrace-probe.c
>  create mode 100644 gdb/testsuite/gdb.base/dtrace-probe.d
>  create mode 100644 gdb/testsuite/gdb.base/dtrace-probe.exp
>  create mode 100644 gdb/testsuite/lib/dtrace.exp
>  create mode 100755 gdb/testsuite/lib/pdtrace.in
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index e933b45..c376f44 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -1527,6 +1527,7 @@ aclocal_m4_deps = \
>  	configure.ac \
>  	acx_configure_dir.m4 \
>  	libmcheck.m4 \
> +	transform.m4 \
>  	../bfd/bfd.m4 \
>  	../config/acinclude.m4 \
>  	../config/plugins.m4 \
> diff --git a/gdb/acinclude.m4 b/gdb/acinclude.m4
> index 6f71486..1f0b574 100644
> --- a/gdb/acinclude.m4
> +++ b/gdb/acinclude.m4
> @@ -9,6 +9,9 @@ sinclude(acx_configure_dir.m4)
>  # This gets GDB_AC_LIBMCHECK.
>  sinclude(libmcheck.m4)
>  
> +# This gets GDB_AC_TRANSFORM.
> +sinclude(transform.m4)
> +
>  dnl gdb/configure.in uses BFD_NEED_DECLARATION, so get its definition.
>  sinclude(../bfd/bfd.m4)
>  
> diff --git a/gdb/configure b/gdb/configure
> index 30a54d2..bb9697d 100755
> --- a/gdb/configure
> +++ b/gdb/configure
> @@ -15141,17 +15141,21 @@ ac_config_links="$ac_config_links $ac_config_links_1"
>  $as_echo "#define GDB_DEFAULT_HOST_CHARSET \"UTF-8\"" >>confdefs.h
>  
>  
> -# Undo the $ec_script escaping suitable for Makefile.
> -transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> -GDB_TRANSFORM_NAME=`echo gdb | sed -e "$transform"`
> -if test "x$GDB_TRANSFORM_NAME" = x; then
> -  GDB_TRANSFORM_NAME=gdb
> -fi
>  
> -GCORE_TRANSFORM_NAME=`echo gcore | sed -e "$transform"`
> -if test "x$GCORE_TRANSFORM_NAME" = x; then
> -  GCORE_TRANSFORM_NAME=gcore
> -fi
> +  gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> +  GDB_TRANSFORM_NAME=`echo gdb | sed -e "$gdb_ac_transform"`
> +  if test "x$GDB_TRANSFORM_NAME" = x; then
> +     GDB_TRANSFORM_NAME=gdb
> +  fi
> +
> +
> +
> +  gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> +  GCORE_TRANSFORM_NAME=`echo gcore | sed -e "$gdb_ac_transform"`
> +  if test "x$GCORE_TRANSFORM_NAME" = x; then
> +     GCORE_TRANSFORM_NAME=gcore
> +  fi
> +
>  
>  ac_config_files="$ac_config_files gcore"
>  
> diff --git a/gdb/configure.ac b/gdb/configure.ac
> index 39fcef2..90bf71c 100644
> --- a/gdb/configure.ac
> +++ b/gdb/configure.ac
> @@ -2444,18 +2444,8 @@ dnl  At the moment, we just assume it's UTF-8.
>  AC_DEFINE(GDB_DEFAULT_HOST_CHARSET, "UTF-8",
>            [Define to be a string naming the default host character set.])
>  
> -# Undo the $ec_script escaping suitable for Makefile.
> -transform=`echo "$program_transform_name" | sed -e 's/[\\$][\\$]/\\$/g'`
> -GDB_TRANSFORM_NAME=`echo gdb | sed -e "$transform"`
> -if test "x$GDB_TRANSFORM_NAME" = x; then
> -  GDB_TRANSFORM_NAME=gdb
> -fi
> -AC_SUBST(GDB_TRANSFORM_NAME)
> -GCORE_TRANSFORM_NAME=`echo gcore | sed -e "$transform"`
> -if test "x$GCORE_TRANSFORM_NAME" = x; then
> -  GCORE_TRANSFORM_NAME=gcore
> -fi
> -AC_SUBST(GCORE_TRANSFORM_NAME)
> +GDB_AC_TRANSFORM([gdb], [GDB_TRANSFORM_NAME])
> +GDB_AC_TRANSFORM([gcore], [GCORE_TRANSFORM_NAME])
>  AC_CONFIG_FILES([gcore], [chmod +x gcore])
>  
>  AC_OUTPUT(Makefile gdb-gdb.gdb doc/Makefile data-directory/Makefile,
> diff --git a/gdb/testsuite/aclocal.m4 b/gdb/testsuite/aclocal.m4
> index 2934db2..d40c3a9 100644
> --- a/gdb/testsuite/aclocal.m4
> +++ b/gdb/testsuite/aclocal.m4
> @@ -1,5 +1,6 @@
>  sinclude(../../config/acx.m4)
>  sinclude(../../config/override.m4)
> +sinclude(../transform.m4)
>  
>  # AM_CONDITIONAL                                            -*- Autoconf -*-
>  
> diff --git a/gdb/testsuite/configure b/gdb/testsuite/configure
> index ca033c3..f36a1bd 100755
> --- a/gdb/testsuite/configure
> +++ b/gdb/testsuite/configure
> @@ -591,6 +591,10 @@ ac_includes_default="\
>  
>  ac_subst_vars='LTLIBOBJS
>  LIBOBJS
> +NM_TRANSFORM_NAME
> +GAS_TRANSFORM_NAME
> +READELF_TRANSFORM_NAME
> +STRIP_TRANSFORM_NAME
>  EXTRA_RULES
>  EGREP
>  GREP
> @@ -1272,6 +1276,11 @@ _ACEOF
>  
>    cat <<\_ACEOF
>  
> +Program names:
> +  --program-prefix=PREFIX            prepend PREFIX to installed program names
> +  --program-suffix=SUFFIX            append SUFFIX to installed program names
> +  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
> +
>  System types:
>    --build=BUILD     configure for building on BUILD [guessed]
>    --host=HOST       cross-compile to build programs to run on HOST [BUILD]
> @@ -3458,6 +3467,53 @@ if test "${build}" = "${host}" -a "${host}" = "${target}"; then
>  fi
>  
>  
> +# Transform the name of some programs and generate the lib/pdtrace
> +# test tool.
> +test "$program_prefix" != NONE &&
> +  program_transform_name="s&^&$program_prefix&;$program_transform_name"
> +# Use a double $ so make ignores it.
> +test "$program_suffix" != NONE &&
> +  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
> +# Double any \ or $.
> +# By default was `s,x,x', remove it if useless.
> +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
> +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
> +
> +
> +  gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> +  STRIP_TRANSFORM_NAME=`echo strip | sed -e "$gdb_ac_transform"`
> +  if test "xSTRIP_TRANSFORM_NAME" = x; then
> +     STRIP_TRANSFORM_NAME=strip
> +  fi
> +
> +
> +
> +  gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> +  READELF_TRANSFORM_NAME=`echo readelf | sed -e "$gdb_ac_transform"`
> +  if test "xREADELF_TRANSFORM_NAME" = x; then
> +     READELF_TRANSFORM_NAME=readelf
> +  fi
> +
> +
> +
> +  gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> +  GAS_TRANSFORM_NAME=`echo as | sed -e "$gdb_ac_transform"`
> +  if test "xGAS_TRANSFORM_NAME" = x; then
> +     GAS_TRANSFORM_NAME=as
> +  fi
> +
> +
> +
> +  gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
> +  NM_TRANSFORM_NAME=`echo nm | sed -e "$gdb_ac_transform"`
> +  if test "xNM_TRANSFORM_NAME" = x; then
> +     NM_TRANSFORM_NAME=nm
> +  fi
> +
> +
> +ac_config_files="$ac_config_files lib/pdtrace"
> +
> +
>  ac_config_files="$ac_config_files Makefile gdb.ada/Makefile gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.btrace/Makefile gdb.cell/Makefile gdb.compile/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile gdb.dlang/Makefile gdb.fortran/Makefile gdb.gdb/Makefile gdb.go/Makefile gdb.server/Makefile gdb.java/Makefile gdb.hp/Makefile gdb.hp/gdb.objdbg/Makefile gdb.hp/gdb.base-hp/Makefile gdb.hp/gdb.aCC/Makefile gdb.hp/gdb.compat/Makefile gdb.hp/gdb.defects/Makefile gdb.guile/Makefile gdb.linespec/Makefile gdb.mi/Makefile gdb.modula2/Makefile gdb.multi/Makefile gdb.objc/Makefile gdb.opencl/Makefile gdb.opt/Makefile gdb.pascal/Makefile gdb.perf/Makefile gdb.python/Makefile gdb.reverse/Makefile gdb.stabs/Makefile gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile"
>  
>  cat >confcache <<\_ACEOF
> @@ -4158,6 +4214,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
>  for ac_config_target in $ac_config_targets
>  do
>    case $ac_config_target in
> +    "lib/pdtrace") CONFIG_FILES="$CONFIG_FILES lib/pdtrace" ;;
>      "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
>      "gdb.ada/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.ada/Makefile" ;;
>      "gdb.arch/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.arch/Makefile" ;;
> @@ -4599,6 +4656,11 @@ which seems to be undefined.  Please make sure it is defined." >&2;}
>  
>    esac
>  
> +
> +  case $ac_file$ac_mode in
> +    "lib/pdtrace":F) chmod +x lib/pdtrace ;;
> +
> +  esac
>  done # for ac_tag
>  
>  
> diff --git a/gdb/testsuite/configure.ac b/gdb/testsuite/configure.ac
> index 5037723..9de9fcc 100644
> --- a/gdb/testsuite/configure.ac
> +++ b/gdb/testsuite/configure.ac
> @@ -96,6 +96,15 @@ if test "${build}" = "${host}" -a "${host}" = "${target}"; then
>  fi
>  AC_SUBST(EXTRA_RULES)
>  
> +# Transform the name of some programs and generate the lib/pdtrace
> +# test tool.
> +AC_ARG_PROGRAM
> +GDB_AC_TRANSFORM(strip, STRIP_TRANSFORM_NAME)
> +GDB_AC_TRANSFORM(readelf, READELF_TRANSFORM_NAME)
> +GDB_AC_TRANSFORM(as, GAS_TRANSFORM_NAME)
> +GDB_AC_TRANSFORM(nm, NM_TRANSFORM_NAME)
> +AC_CONFIG_FILES([lib/pdtrace], [chmod +x lib/pdtrace])
> +
>  AC_OUTPUT([Makefile \
>    gdb.ada/Makefile \
>    gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.btrace/Makefile \
> diff --git a/gdb/testsuite/gdb.base/dtrace-probe.c b/gdb/testsuite/gdb.base/dtrace-probe.c
> new file mode 100644
> index 0000000..29933ad
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/dtrace-probe.c
> @@ -0,0 +1,38 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2014, 2015 Free Software Foundation, Inc.
> +
> +   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 3 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, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "dtrace-probe.h"
> +
> +int
> +main ()
> +{
> +  char *name = "application";
> +
> +  TEST_TWO_LOCATIONS ();
> +  
> +  int i = 0;
> +  while (i < 10)
> +    {
> +      i++;
> +      if (TEST_PROGRESS_COUNTER_ENABLED ())
> +	TEST_PROGRESS_COUNTER (name, i);
> +      else
> +	TEST_TWO_LOCATIONS ();
> +    }
> +      
> +  return 0; /* last break here */
> +}
> diff --git a/gdb/testsuite/gdb.base/dtrace-probe.d b/gdb/testsuite/gdb.base/dtrace-probe.d
> new file mode 100644
> index 0000000..6bcbf34
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/dtrace-probe.d
> @@ -0,0 +1,21 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2014, 2015 Free Software Foundation, Inc.
> +
> +   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 3 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, see <http://www.gnu.org/licenses/>.  */
> +
> +provider test {
> +  probe progress__counter (char *, int);
> +  probe two__locations  ();
> +};
> diff --git a/gdb/testsuite/gdb.base/dtrace-probe.exp b/gdb/testsuite/gdb.base/dtrace-probe.exp
> new file mode 100644
> index 0000000..e42e948
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/dtrace-probe.exp
> @@ -0,0 +1,106 @@
> +# Copyright (C) 2014, 2015 Free Software Foundation, Inc.
> +
> +# 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 3 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, see <http://www.gnu.org/licenses/>.
> +
> +load_lib "dtrace.exp"
> +
> +# Run the tests.
> +# This returns -1 on failure to compile or start, 0 otherwise.
> +proc dtrace_test {} {
> +    global testfile hex srcfile binfile
> +
> +    standard_testfile
> +    
> +    if {[dtrace_build_usdt_test_program] == -1} {
> +        untested "could not compile test program"
> +        return -1
> +    }
> +
> +    clean_restart ${binfile}
> +    
> +    if ![runto_main] {
> +        return -1
> +    }
> +
> +    gdb_test "print \$_probe_argc" "No probe at PC $hex" \
> +        "check argument not at probe point"
> +
> +    # Test the 'info probes' command.
> +    gdb_test "info probes dtrace" \
> +        "test *progress-counter *$hex +no.*test *two-locations *$hex +always.*test *two-locations *$hex +always.*" \
> +        "info probes dtrace"
> +
> +    # Disabling the probe test:two-locations shall have no effect,
> +    # since no is-enabled probes are defined for it in the object
> +    # file.
> +
> +    gdb_test "disable probe test two-locations" \
> +	"Probe test:two-locations cannot be disabled.*" \
> +	"disable probe test two-locations"
> +
> +    # On the other hand, the probe test:progress-counter can be
> +    # enabled and then disabled again.
> +
> +    gdb_test "enable probe test progress-counter" \
> +	"Probe test:progress-counter enabled.*" \
> +	"enable probe test progress-counter"
> +
> +    gdb_test "disable probe test progress-counter" \
> +	"Probe test:progress-counter disabled.*" \
> +	"disable probe test progress-counter"
> +
> +    # Since test:progress-counter is disabled we can run to the second
> +    # instance of the test:two-locations probe.
> +
> +    if {![runto "-probe-dtrace test:two-locations"]} {
> +	fail "run to the first test:two-locations probe point"
> +    }
> +    if {![runto "-probe-dtrace test:two-locations"]} {
> +	fail "run to the second test:two-locations probe point"
> +    }
> +
> +    # Go back to the breakpoint on main() and enable the
> +    # test:progress-counter probe.  Set a breakpoint on it and see
> +    # that it gets reached.
> +
> +    if ![runto_main] {
> +	return -1
> +    }
> +
> +    gdb_test "enable probe test progress-counter" \
> +	"Probe test:progress-counter enabled.*" \
> +	"enable probe test progress-counter"
> +
> +    gdb_test "break -probe-dtrace test:progress-counter" \
> +	".*Breakpoint \[0-9\]+ .*" "set breakpoint in test:progress-counter"
> +    gdb_continue_to_breakpoint "test:progress-counter"
> +
> +    # Test probe arguments.
> +    gdb_test "print \$_probe_argc" " = 2" \
> +        "print \$_probe_argc for probe progress-counter"
> +    gdb_test "print \$_probe_arg0" \
> +        " = $hex \"application\"" \
> +        "print \$_probe_arg0 for probe progress-counter"
> +    gdb_test "print \$_probe_arg1" " = 1" \
> +        "print \$_probe_arg1 for probe progress-counter"
> +
> +    # Set a breakpoint with multiple probe locations.
> +    gdb_test "break -pdtrace test:two-locations" \
> +        "Breakpoint \[0-9\]+ at $hex.*2 locations.*" \
> +        "set multi-location probe breakpoint (probe two-locations)"
> +
> +    return 0
> +}
> +
> +dtrace_test
> diff --git a/gdb/testsuite/lib/dtrace.exp b/gdb/testsuite/lib/dtrace.exp
> new file mode 100644
> index 0000000..e323b08
> --- /dev/null
> +++ b/gdb/testsuite/lib/dtrace.exp
> @@ -0,0 +1,71 @@
> +# Copyright 2014, 2015 Free Software Foundation, Inc.
> +#
> +# 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 3 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, see <http://www.gnu.org/licenses/>.
> +
> +# Generate a test program containing DTrace USDT probes, whose sources
> +# are ${srcfile} and ${testfile}.d.  The sequence of commands used to
> +# generate the test program is:
> +#
> +# 1. Generate a header file from ${testfile}.d using dtrace -h.
> +# 2. Compile ${srcfile}.c.
> +# 3. Generate an object file containing a DOF program using dtrace -G.
> +# 4. Link everything together to get the test program.
> +#
> +# Note that if DTrace is not found in the host system then this
> +# function uses the pdtrace implementation, which is located at
> +# testsuite/lib/pdtrace.
> +#
> +# This function requires 'testfile', 'srcfile' and 'binfile' to be
> +# properly set.
> +#
> +# This function returns -1 on failure, 0 otherwise
> +proc dtrace_build_usdt_test_program {} {
> +    global testfile hex objdir srcdir srcfile subdir binfile
> +    
> +    # Make sure that dtrace is installed, it is the real one (not the
> +    # script installed by SystemTap, for example) and of the right
> +    # version (>= 0.4.0).  If it is not then use pdtrace instead.
> +    set dtrace "dtrace"
> +    set result [remote_exec host "$dtrace -V"]
> +    if {[lindex $result 0] != 0 || ![regexp {^dtrace: Sun D [0-9]\.[0-9]\.[0-9]} [lindex $result 1]]} {
> +	set dtrace "${objdir}/lib/pdtrace"
> +    }
> +    set dscript_file "${srcdir}/${subdir}/${testfile}.d"
> +
> +    # 1. Generate a header file from testprogram.d using dtrace -h.
> +    set out_header_file [standard_output_file "${testfile}.h"]
> +    set result [remote_exec host "$dtrace -h -s $dscript_file -o $out_header_file"]
> +    verbose -log [lindex $result 1]
> +    if {[lindex $result 0] != 0} {
> +        return -1
> +    }
> +
> +    # 2. Compile testprogram.c.
> +    set options [list debug additional_flags=-I[file dirname $out_header_file]]
> +    if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}.o" object ${options}] != ""} {
> +        return -1
> +    }
> +
> +    # 3. Generate an object file containing a DOF program using dtrace -G.
> +    set result [remote_exec host "$dtrace -G -s $dscript_file -o ${binfile}-p.o ${binfile}.o"]
> +    verbose -log [lindex $result 1]
> +    if {[lindex $result 0] != 0} {
> +        return -1
> +    }
> +
> +    # 4. Link everything together to get the test program.
> +    if {[gdb_compile "${binfile}.o ${binfile}-p.o" ${binfile} executable {debug}] != ""} {
> +        return -1
> +    }
> +}
> diff --git a/gdb/testsuite/lib/pdtrace.in b/gdb/testsuite/lib/pdtrace.in
> new file mode 100755
> index 0000000..118b017
> --- /dev/null
> +++ b/gdb/testsuite/lib/pdtrace.in
> @@ -0,0 +1,1033 @@
> +#!/bin/sh
> +
> +# A Poor(but Free)'s Man dtrace
> +#
> +# Copyright (C) 2014, 2015 Free Software Foundation, Inc.
> +#
> +# Contributed by Oracle, Inc.
> +#
> +# This file is part of GDB.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 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, see
> +# <http://www.gnu.org/licenses/>.
> +
> +# DISCLAIMER DISCLAIMER DISCLAIMER
> +# This script is a test tool.  As such it is in no way intended to
> +# replace the "real" dtrace command for any practical purpose, apart
> +# from testing the DTrace USDT probes support in GDB.
> +
> +# that said...
> +#
> +# pdtrace is a limited dtrace program, implementing a subset of its
> +# functionality:
> +#
> +# - The generation of an ELF file containing an embedded dtrace
> +#   program.  Equivalent to dtrace -G.
> +#
> +# - The generation of a header file with definitions for static
> +#   probes.  Equivalent to dtrace -h.
> +#
> +# This allows to generate DTrace static probes without having to use
> +# the user-level DTrace components.  The generated objects are 100%
> +# compatible with DTrace and can be traced by the dtrace kernel module
> +# like if they were generated by dtrace.
> +#
> +# Some of the known limitations of this implementation are:
> +# - The input d-script must describe one provider, and only one.
> +# - The "probe " directives in the d-file must not include argument
> +#   names, just the types.  Thus something like `char *' is valid, but
> +#   `char *name' is not.
> +# - The command line options must precede other arguments, since the
> +#   script uses the (more) portable getopts.
> +# - Each probe header in the d-script must be contained in
> +#   a single line.
> +# - strip -K removes the debugging information from the input object
> +#   file.
> +# - The supported target platforms are i[3456]86 and x86_64.
> +#
> +# Please keep this code as portable as possible.  Restrict yourself to
> +# POSIX sh.
> +
> +# This script uses the following external programs, defined in
> +# variables.  Some of them are substituted by autoconf.
> +
> +TR=tr
> +NM=@NM_TRANSFORM_NAME@
> +EGREP=egrep
> +SED=sed
> +CUT=cut
> +READELF=@READELF_TRANSFORM_NAME@
> +SORT=sort
> +EXPR=expr
> +WC=wc
> +UNIQ=uniq
> +HEAD=head
> +SEQ=seq
> +AS=@GAS_TRANSFORM_NAME@
> +STRIP=@STRIP_TRANSFORM_NAME@
> +TRUE=true
> +
> +# Sizes for several DOF structures, in bytes.
> +#
> +# See linux/dtrace/dof.h for the definition of the referred
> +# structures.
> +
> +dof_hdrsize=64      # sizeof(dtrace_dof_hdr)
> +dof_secsize=32      # sizeof(dtrace_dof_sect)
> +dof_probesize=48    # sizeof(dtrace_dof_probe)
> +dof_providersize=44 # sizeof(dtrace_dof_provider)
> +
> +# Types for the several DOF sections.
> +#
> +# See linux/dtrace/dof_defines.h for a complete list of section types
> +# along with their values.
> +
> +dof_sect_type_strtab=8
> +dof_sect_type_provider=15
> +dof_sect_type_probes=16
> +dof_sect_type_prargs=17
> +dof_sect_type_proffs=18
> +dof_sect_type_prenoffs=26
> +
> +### Functions
> +
> +# Write a message to the standard error output and exit with an error
> +# status.
> +#
> +# Arguments:
> +#   $1 error message.
> +
> +f_panic()
> +{
> +    echo "error: $1" 1>&2; exit 1
> +}
> +
> +# Write a usage message to the standard output and exit with an error
> +# status.
> +
> +f_usage()
> +{
> +    printf "Usage: pdtrace [-32|-64] [-GhV] [-o output] [-s script] [ args ... ]\n\n"
> +
> +    printf "\t-32 generate 32-bit ELF files\n"
> +    printf "\t-64 generate 64-bit ELF files\n\n"
> +
> +    printf "\t-G  generate an ELF file containing embedded dtrace program\n"
> +    printf "\t-h  generate a header file with definitions for static probes\n"
> +    printf "\t-o  set output file\n"
> +    printf "\t-s  handle probes according to the specified D script\n"
> +    printf "\t-V  report the DTrace API version implemented by the tool\n"
> +    exit 2
> +}
> +
> +# Write a version message to the standard output and exit with a
> +# successful status.
> +
> +f_version()
> +{
> +    echo "pdtrace: Sun D 1.6.3"
> +    exit
> +}
> +
> +# Add a new record to a list and return it.
> +#
> +# Arguments:
> +# $1 is the list.
> +# $2 is the new record
> +
> +f_add_record()
> +{
> +    rec=$1
> +    test -n "$rec" && \
> +        { rec=$(printf %s\\n "$rec"; echo x); rec=${rec%x}; }
> +    printf %s "$rec$2"
> +}
> +
> +# Collect the providers and probes information from the input object
> +# file.
> +#
> +# This function sets the values of the following global variables.
> +# The values are structured in records, each record in a line.  The
> +# fields of each record are separated in some cases by white
> +# characters and in other cases by colon (:) characters.
> +#
> +# The type codes in the line format descriptors are:
> +# S: string, D: decimal number
> +#
> +# probes
> +#   Regular probes and is-enabled probes.
> +#   TYPE(S) PROVIDER(S) NAME(S) OFFSET(D) BASE(D) BASE_SYM(S)
> +# base_probes
> +#   Base probes, i.e. probes sharing provider, name and container.
> +#   PROVIDER(S) NAME(S) BASE(D) BASE_SYM(S)
> +# providers
> +#   List of providers.
> +#   PROVIDER(S)
> +# All the offsets are expressed in bytes.
> +#
> +# Input globals:
> +#  objfile
> +# Output globals:
> +#  probes, base_probes, providers
> +
> +probes=
> +base_probes=
> +providers=
> +probes_args=
> +
> +f_collect_probes()
> +{
> +    # Probe points are function calls to undefined functions featuring
> +    # distinct names for both normal probes and is-enabled probes.
> +    PROBE_REGEX="(__dtrace_([a-zA-Z_]+)___([a-zA-Z_]+))"
> +    EPROBE_REGEX="(__dtraceenabled_([a-zA-Z_]+)___([a-zA-Z_]+))"
> +
> +    while read type symbol provider name; do
> +          test -z "$type" && f_panic "No probe points found in $objfile"
> +
> +          provider=$(printf %s $provider | $TR -s _)
> +          name=$(printf %s $name | $TR -s _)
> +
> +          # Search the object file for relocations defined for the
> +          # probe symbols.  Then calculate the base address of the
> +          # probe (along with the symbol associated with that base
> +          # address) and the offset of the probe point.
> +          for offset in $($READELF -W -r $objfile | $EGREP $symbol | $CUT -d' ' -f1)
> +          do
> +              # Figure out the base address for the probe.  This is
> +              # done finding the function name in the text section of
> +              # the object file located above the probed point.  But
> +              # note that the relocation is for the address operand of
> +              # the call instruction, so we have to subtract 1 to find
> +              # the real probed point.
> +              offset=$((0x$offset - 1))
> +
> +              # The addresses of is-enabled probes must point to the
> +              # first NOP instruction in their patched instructions
> +              # sequences, so modify them (see f_patch_objfile for the
> +              # instruction sequences).
> +              if test "$type" = "e"; then
> +                  if test "$objbits" -eq "32"; then
> +                      offset=$((offset + 2))
> +                  else # 64 bits
> +                      offset=$((offset + 3))
> +                  fi
> +              fi
> +              
> +              # Determine the base address of the probe and its
> +              # corresponding function name.
> +              funcs=$($NM -td $objfile | $EGREP "^[0-9]+ T " \
> +                      | $CUT -d' ' -f1,3 | $SORT -n -r | $TR ' ' :)
> +              for fun in $funcs; do
> +                  func_off=$(printf %s $fun | $CUT -d: -f1)
> +                  func_sym=$(printf %s $fun | $CUT -d: -f2)
> +                  # Note that `expr' is used to remove leading zeros
> +                  # to avoid FUNC_OFF to be interpreted as an octal
> +                  # number in arithmetic contexts.
> +                  test "$func_off" -le "$offset" && \
> +                      { base=$($EXPR $func_off + 0); break; }
> +              done
> +              test -n "$base" || \
> +                f_panic "could not find base address for probe at $objfile($o)"
> +
> +              # Emit the record for the probe.
> +              probes=$(f_add_record "$probes" \
> +                                    "$type $provider $name $(($offset - $base)) $base $func_sym")
> +          done
> +      done <<EOF
> +$($NM $objfile | $EGREP " U $PROBE_REGEX" \
> +            | $SED -E -e "s/.*$PROBE_REGEX.*/p \1 \2 \3/";
> +     $NM $objfile | $EGREP " U $EPROBE_REGEX" \
> +         | $SED -E -e "s/.*$EPROBE_REGEX.*/e \1 \2 \3/")
> +EOF
> +
> +    # Build the list of providers and of base probes from the probes.
> +    while read type provider name offset base base_sym; do
> +        providers=$(f_add_record "$providers" "$provider")
> +        base_probes=$(f_add_record "$base_probes" "$provider $name $base $base_sym")
> +    done <<EOF
> +$probes
> +EOF
> +    providers=$(printf %s\\n "$providers" | $SORT | $UNIQ)
> +    base_probes=$(printf %s\\n "$base_probes" | $SORT | $UNIQ)
> +}
> +
> +# Collect the argument counts and type strings for all the probes
> +# described in the `probes' global variable.  This is done by
> +# inspecting the d-script file provided by the user.
> +#
> +# This function sets the values of the following global variables.
> +# The values are structured in records, each record in a line.  The
> +# fields of each record are separated in some cases by white
> +# characters and in other cases by colon (:) characters.
> +#
> +# The type codes in the line format descriptors are:
> +# S: string, D: decimal number
> +#
> +# probes_args
> +#   Probes arguments.
> +#   PROVIDER(S):NAME(S):NARGS(D):ARG1(S):ARG2(S):...:ARGn(S)
> +#
> +# Input globals:
> +#  probes
> +# Output globals:
> +#  probes_args
> +# Arguments:
> +#   $1 is the d-script file from which to extract the arguments
> +#      information.
> +
> +f_collect_probes_args()
> +{
> +    dscript=$1
> +    while read type provider name offset base base_sym; do
> +        # Process normal probes only.  Is-enabled probes are not
> +        # described in the d-script file and they don't receive any
> +        # argument.
> +        test "$type" = "p" || continue
> +        
> +        # Names are mangled in d-script files to make it possible to
> +        # have underscore characters as part of the provider name and
> +        # probe name.
> +        m_provider=$(printf %s $provider | $SED -e 's/_/__/g')
> +        m_name=$(printf %s $name | $SED -e 's/_/__/g')
> +        
> +        # Ignore this probe if the d-script file does not describe its
> +        # provider.
> +        $EGREP -q "provider +$m_provider" $dscript || continue
> +        
> +        # Look for the line containing the description of the probe.
> +        # If we can't find it then ignore this probe.
> +        line=$($EGREP "^ *probe +$m_name *\(.*\);" $dscript)
> +        test -n "$line" || continue
> +        
> +        # Ok, extract the argument types from the probe prototype.
> +        # This is fragile as hell as it requires the prototype to be
> +        # in a single line.
> +        args=""; nargs=0; line=$(printf %s "$line" | $SED -e 's/.*(\(.*\)).*/\1/')
> +        set -f; IFS=,
> +        for arg in $line; do
> +            args="$args:$arg"
> +            nargs=$((nargs + 1))
> +        done
> +        set +f; unset IFS
> +
> +        # Emit the record for the probe arguments.
> +        probes_args=$(f_add_record "$probes_args" "$provider:$name:$nargs$args")
> +    done <<EOF
> +$probes
> +EOF
> +}
> +
> +# Functions to manipulate the global BCOUNT.
> +
> +BCOUNT=0
> +
> +f_incr_bcount()
> +{
> +    BCOUNT=$((BCOUNT + $1))
> +}
> +
> +f_align_bcount()
> +{
> +    test $((BCOUNT % $1)) -eq 0 || BCOUNT=$((BCOUNT + ($1 - (BCOUNT % $1))))
> +}
> +
> +# Generate a line of assembly code and add it to the asmprogram global
> +# variable.
> +#
> +# Arguments:
> +#   $1 string to generate in a line.
> +
> +asmprogram=
> +
> +f_gen_asm()
> +{
> +    line=$(printf "\t$1")
> +    asmprogram=$(f_add_record "$asmprogram" "$line")
> +}
> +
> +# Helper function to generate the assembly code of a DOF section
> +# header.
> +#
> +# This function is used by `f_gen_dof_program'.
> +#
> +# Arguments:
> +#   $1 is the name of the described section.
> +#   $2 is the type of the described section.
> +#   $3 is the alignment of the described section.
> +#   $4 is the number of entities stored in the described section.
> +#   $5 is the offset in the DOF program of the described section.
> +#   $6 is the size of the described section, in bytes.
> +
> +f_gen_dof_sect_header()
> +{
> +    f_gen_asm ""
> +    f_gen_asm "/* dtrace_dof_sect for the $1 section.  */"
> +    f_gen_asm ".balign 8"
> +    f_gen_asm ".4byte $2\t/* uint32_t dofs_type  */"
> +    f_gen_asm ".4byte $3\t/* uint32_t dofs_align  */"
> +    # The DOF_SECF_LOAD flag is 1 => loadable section.
> +    f_gen_asm ".4byte 1\t/* uint32_t dofs_flags  */"
> +    f_gen_asm ".4byte $4\t/* uint32_t dofs_entsize  */"
> +    f_gen_asm ".8byte $5\t/* uint64_t dofs_offset  */"
> +    f_gen_asm ".8byte $6\t/* uint64_t dofs_size  */"
> +}
> +
> +# Generate a DOF program and assembly it in the output file.
> +#
> +# The DOF program generated by this function has the following
> +# structure:
> +#
> +# HEADER
> +# STRTAB OFFTAB EOFFTAB [PROBES PROVIDER]...
> +# STRTAB_SECT OFFTAB_SECT EOFFTAB_SECT ARGTAB_SECT [PROBES_SECT PROVIDER_SECT]...
> +#
> +# Input globals:
> +#   probes, base_probes, providers, probes_args, BCOUNT
> +
> +f_gen_dof_program()
> +{   
> +    ###### Variables used to cache information needed later.
> +    
> +    # Number of section headers in the generated DOF program.
> +    dof_secnum=0
> +    # Offset of section headers in the generated DOF program, in bytes.
> +    dof_secoff=0
> +
> +    # Sizes of the STRTAB, OFFTAB and EOFFTAB sections, in bytes.
> +    strtab_size=0
> +    offtab_size=0
> +    eofftab_size=0
> +    
> +    # Offsets of the STRTAB, OFFTAB EOFFTAB and PROBES sections in the
> +    # generated DOF program.  In bytes.
> +    strtab_offset=0
> +    offtab_offset=0
> +    eofftab_offset=0
> +    argtab_offset=0
> +    probes_offset=0
> +    
> +    # Indexes of the section headers of the STRTAB, OFFTAB, EOFFTAB and
> +    # PROBES sections in the sections array.
> +    strtab_sect_index=0
> +    offtab_sect_index=0
> +    eofftab_sect_index=0
> +    argtab_sect_index=0
> +    probes_sect_index=0
> +
> +    # First offsets and eoffsets of the base-probes.
> +    # Lines: PROVIDER(S) NAME(S) BASE(D) (DOF_OFFSET(D)|DOF_EOFFSET(D))
> +    probes_dof_offsets=
> +    probes_dof_eoffsets=
> +    
> +    # Offsets in the STRTAB section for the first type of base probes.
> +    # Record per line: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
> +    probes_dof_types=
> +
> +
> +    # Offsets of the provider names in the provider's STRTAB section.
> +    # Lines: PROVIDER(S) OFFSET(D)
> +    providers_dof_names=
> +
> +    # Offsets of the base-probe names in the provider's STRTAB section.
> +    # Lines: PROVIDER(S) NAME(S) BASE(D) OFFSET(D)
> +    probes_dof_names=
> +    
> +    # Offsets of the provider sections in the DOF program.
> +    # Lines: PROVIDER(S) OFFSET(D)
> +    providers_offsets=
> +
> +    ###### Generation phase.
> +    
> +    # The header of the DOF program contains a `struct
> +    # dtrace_dof_hdr'.  Record its size, but it is written at the end
> +    # of the function.
> +    f_incr_bcount $dof_hdrsize; f_align_bcount 8
> +
> +    # The STRTAB section immediately follows the header.  It contains
> +    # the following set of packed null-terminated strings:
> +    #
> +    # [PROVIDER [BASE_PROBE_NAME [BASE_PROBE_ARG_TYPE...]]...]...
> +    strtab_offset=$BCOUNT
> +    strtab_sect_index=$dof_secnum
> +    dof_secnum=$((dof_secnum + 1))
> +    f_gen_asm ""
> +    f_gen_asm "/* The STRTAB section.  */"
> +    f_gen_asm ".balign 8"
> +    # Add the provider names.
> +    off=0
> +    while read provider; do
> +        strtab_size=$(($strtab_size + ${#prov} + 1))
> +        # Note the funny mangling...
> +        f_gen_asm ".asciz \"$(printf %s $provider | $TR _ -)\""
> +        providers_dof_names=$(f_add_record "$providers_dof_names" \
> +                                           "$provider $off")
> +        off=$(($off + ${#provider} + 1))
> +
> +        # Add the base-probe names.
> +        while read p_provider name base base_sym; do
> +            test "$p_provider" = "$provider" || continue
> +            # And yes, more funny mangling...
> +            f_gen_asm ".asciz \"$(printf %s $name | $TR _ -)\""
> +            probes_dof_names=$(f_add_record "$probes_dof_names" \
> +                                            "$p_provider $name $base $off")
> +            off=$(($off + ${#name} + 1))
> +            while read args; do
> +                a_provider=$(printf %s "$args" | $CUT -d: -f1)
> +                a_name=$(printf %s "$args" | $CUT -d: -f2)
> +                test "$a_provider" = "$p_provider" \
> +                    && test "$a_name" = "$name" \
> +                    || continue
> +
> +                probes_dof_types=$(f_add_record "$probes_dof_types" \
> +                                                "$a_provider $name $base $off")
> +                nargs=$(printf %s "$args" | $CUT -d: -f3)
> +                for n in $($SEQ $nargs); do
> +                    arg=$(printf %s "$args" | $CUT -d: -f$(($n + 3)))
> +                    f_gen_asm ".asciz \"${arg}\""
> +                    off=$(($off + ${#arg} + 1))
> +                done                
> +            done <<EOF
> +$probes_args
> +EOF
> +        done <<EOF
> +$base_probes
> +EOF
> +    done <<EOF
> +$providers
> +EOF
> +    strtab_size=$off
> +    f_incr_bcount $strtab_size; f_align_bcount 8
> +
> +    # The OFFTAB section contains a set of 32bit words, one per
> +    # defined regular probe.
> +    offtab_offset=$BCOUNT
> +    offtab_sect_index=$dof_secnum
> +    dof_secnum=$((dof_secnum + 1))
> +    f_gen_asm ""
> +    f_gen_asm "/* The OFFTAB section.  */"
> +    f_gen_asm ".balign 8"
> +    off=0
> +    while read type provider name offset base base_sym; do
> +        test "$type" = "p" || continue
> +        f_gen_asm ".4byte $offset\t/* probe ${provider}:${name}  */"
> +        probes_dof_offsets=$(f_add_record "$probes_dof_offsets" \
> +                                          "$provider $name $base $off")
> +        off=$(($off + 4))
> +    done <<EOF
> +$probes
> +EOF
> +    offtab_size=$off
> +    f_incr_bcount $offtab_size; f_align_bcount 8
> +
> +    # The EOFFTAB section contains a set of 32bit words, one per
> +    # defined is-enabled probe.
> +    eofftab_offset=$BCOUNT
> +    eofftab_sect_index=$dof_secnum
> +    dof_secnum=$((dof_secnum + 1))
> +    f_gen_asm ""
> +    f_gen_asm "/* The EOFFTAB section.  */"
> +    f_gen_asm ".balign 8"
> +    off=0
> +    while read type provider name offset base base_sym; do
> +        test "$type" = "e" || continue
> +        f_gen_asm ".4byte $offset\t/* is-enabled probe ${provider}:${name}  */"
> +        probes_dof_eoffsets=$(f_add_record "$probes_dof_eoffsets" \
> +                                           "$provider $name $base $off")
> +        off=$(($off + 4))
> +    done <<EOF
> +$probes
> +EOF
> +    eofftab_size=$off
> +    f_incr_bcount $eofftab_size; f_align_bcount 8
> +
> +    # The ARGTAB section is empty, but nonetheless has a section
> +    # header, so record its section index here.
> +    argtab_offset=0
> +    argtab_sect_index=$dof_secnum
> +    dof_secnum=$((dof_secnum + 1))
> +
> +    # Generate a pair of sections PROBES and PROVIDER for each
> +    # provider.
> +    while read prov; do
> +        # The PROBES section contains an array of `struct
> +        # dtrace_dof_probe'.
> +        #
> +        # A `dtrace_dof_probe' entry characterizes the collection of
> +        # probes and is-enabled probes sharing the same provider, name and
> +        # base address.
> +        probes_sect_index=$dof_secnum
> +        dof_secnum=$((dof_secnum + 1))
> +        probes_offset=$BCOUNT        
> +        num_base_probes=$(printf %s\\n "$base_probes" | $WC -l)
> +        while read provider name base base_sym; do
> +            name_offset=$(printf %s\\n "$probes_dof_names" \
> +                          | $EGREP "^$provider $name " | $CUT -d' ' -f4)
> +
> +            num_offsets=$(printf %s\\n "$probes_dof_offsets" \
> +                          | $EGREP "^$provider $name [0-9]+ " | $WC -l)
> +            
> +            first_offset=0
> +            test "$num_offsets" -gt 0 && \
> +              first_offset=$(printf %s\\n "$probes_dof_offsets" \
> +                             | $EGREP "^$provider $name " | $CUT -d' ' -f4 | $HEAD -1)
> +
> +            num_eoffsets=$(printf %s\\n "$probes_dof_eoffsets" \
> +                           | $EGREP "^$provider $name [0-9]+ " | $WC -l)
> +            first_eoffset=0
> +            test "$num_eoffsets" -gt 0 && \
> +              first_eoffset=$(printf %s "$probes_dof_eoffsets" \
> +                              | $EGREP "^$provider $name " | $CUT -d' ' -f4 | $HEAD -1)
> +
> +            num_args=$(printf %s "$probes_args" \
> +                       | $EGREP "^$provider:$name:" | $CUT -d: -f3 | $HEAD -1)
> + 
> +            first_type=$(printf %s "$probes_dof_types" \
> +                         | $EGREP "^$provider $name $base " | $CUT -d' ' -f4 | $HEAD -1)
> +
> +            reloctype=R_X86_64_GLOB_DAT
> +            test "$objbits" = "32" && reloctype=R_386_32
> +            
> +            f_gen_asm ""
> +            f_gen_asm "/* dtrace_dof_probe for ${provider}:${name} at ${base_sym}  */"
> +            f_gen_asm ".balign 8"
> +            f_gen_asm ".reloc ., $reloctype, $base_sym + 0"
> +            f_gen_asm ".8byte ${base}\t/* uint64_t dofpr_addr  */"
> +            f_gen_asm ".4byte 0\t/* uint32_t dofpr_func  */"
> +            f_gen_asm ".4byte $name_offset\t/* uint32_t dofpr_name   */"
> +            f_gen_asm ".4byte $first_type\t/* uint32_t dofpr_nargv  */"
> +            f_gen_asm ".4byte 0\t/* uint32_t dofpr_xargv  */"
> +            f_gen_asm ".4byte 0\t/* uint32_t dofpr_argidx */"
> +            f_gen_asm ".4byte $(($first_offset/4))\t/* uint32_t dofpr_offidx  */"
> +            f_gen_asm ".byte  $num_args\t/* uint8_t dofpr_nargc  */"
> +            f_gen_asm ".byte  0\t/* uint8_t dofpr_xargc  */"
> +            f_gen_asm ".2byte $num_offsets\t/* uint16_t dofpr_noffs  */"
> +            f_gen_asm ".4byte $(($first_eoffset/4))\t/* uint32_t dofpr_enoffidx  */"
> +            f_gen_asm ".2byte $num_eoffsets\t/* uint16_t dofpr_nenoffs  */"
> +            f_gen_asm ".2byte 0\t/* uint16_t dofpr_pad1  */"
> +            f_gen_asm ".4byte 0\t/* uint16_t dofpr_pad2  */"
> +
> +            f_incr_bcount "$dof_probesize"
> +        done <<EOF
> +$base_probes
> +EOF
> +
> +        # The PROVIDER section contains a `struct dtrace_dof_provider'
> +        # instance describing the provider for the probes above.
> +        dof_secnum=$((dof_secnum + 1))
> +        providers_offsets=$(f_add_record "$providers_offsets" \
> +                                         "$prov $BCOUNT")
> +        # The dtrace_dof_provider.
> +        provider_name_offset=$(printf %s "$providers_dof_names" \
> +                                      | $EGREP "^$prov " | $CUT -d' ' -f2)
> +
> +        f_gen_asm ""
> +        f_gen_asm "/* dtrace_dof_provider for $prov  */"
> +        f_gen_asm ".balign 8"
> +        # Links to several DOF sections.
> +        f_gen_asm ".4byte $strtab_sect_index\t/* uint32_t dofpv_strtab  */"
> +        f_gen_asm ".4byte $probes_sect_index\t/* uint32_t dofpv_probes  */"
> +        f_gen_asm ".4byte $argtab_sect_index\t/* uint32_t dofpv_prargs  */"
> +        f_gen_asm ".4byte $offtab_sect_index\t/* uint32_t dofpv_proffs  */"
> +        # Offset of the provider name into the STRTAB section.
> +        f_gen_asm ".4byte $provider_name_offset\t/* uint32_t dofpv_name  */"
> +        # The rest of fields can be 0 for our modest purposes :)
> +        f_gen_asm ".4byte 0\t/* uint32_t dofpv_provattr  */"
> +        f_gen_asm ".4byte 0\t/* uint32_t dofpv_modattr  */"
> +        f_gen_asm ".4byte 0\t/* uint32_t dofpv_funcattr  */"
> +        f_gen_asm ".4byte 0\t/* uint32_t dofpv_nameattr  */"
> +        f_gen_asm ".4byte 0\t/* uint32_t dofpv_argsattr  */"
> +        # But not this one, of course...
> +        f_gen_asm ".4byte $eofftab_sect_index\t/* uint32_t dofpv_prenoffs  */"
> +
> +        f_incr_bcount $dof_providersize
> +    done<<EOF
> +$providers
> +EOF
> +    f_align_bcount 8
> +
> +    # The section headers follow, one per section defined above.
> +    dof_secoff=$BCOUNT
> +
> +    f_gen_dof_sect_header STRTAB \
> +                          $dof_sect_type_strtab \
> +                          1 1 $strtab_offset $strtab_size
> +    f_incr_bcount $dof_secsize; f_align_bcount 8
> +
> +    f_gen_dof_sect_header OFFTAB \
> +                          $dof_sect_type_proffs \
> +                          4 4 $offtab_offset $offtab_size
> +    f_incr_bcount $dof_secsize; f_align_bcount 8
> +
> +    f_gen_dof_sect_header EOFFTAB \
> +                          $dof_sect_type_prenoffs \
> +                          4 4 $eofftab_offset $eofftab_size
> +    f_incr_bcount $dof_secsize; f_align_bcount 8
> +
> +    f_gen_dof_sect_header ARGTAB \
> +                          $dof_sect_type_prargs \
> +                          4 1 $argtab_offset 0
> +    f_incr_bcount $dof_secsize; f_align_bcount 8
> +    
> +    while read provider; do
> +        provider_offset=$(printf %s "$providers_offsets" \
> +                          | $EGREP "^$provider " | $CUT -d' ' -f2)
> +        num_base_probes=$(printf %s\\n "$base_probes" | $WC -l)
> +
> +        f_gen_dof_sect_header "$provider probes" \
> +                              $dof_sect_type_probes \
> +                              8 $dof_probesize $probes_offset \
> +                              $((num_base_probes * dof_probesize))
> +        f_incr_bcount $dof_secsize; f_align_bcount 8
> +
> +        f_gen_dof_sect_header "$provider provider" \
> +                              $dof_sect_type_provider \
> +                              8 1 $provider_offset $dof_providersize
> +        f_incr_bcount $dof_secsize; f_align_bcount 8
> +    done <<EOF
> +$providers
> +EOF
> +
> +    # Finally, cook the header.
> +    asmbody="$asmprogram"
> +    asmprogram=""
> +    f_gen_asm "/* File generated by pdtrace.  */"
> +    f_gen_asm ""
> +
> +    f_gen_asm ".section .SUNW_dof,\"a\",\"progbits\""
> +    f_gen_asm ".globl __SUNW_dof"
> +    f_gen_asm ".hidden __SUNW_dof"
> +    f_gen_asm ".size __SUNW_dof, ${BCOUNT}"
> +    f_gen_asm ".type __SUNW_dof, @object"
> +    f_gen_asm "__SUNW_dof:"
> +
> +    f_gen_asm ""
> +    f_gen_asm "/* dtrace_dof_hdr */"
> +    f_gen_asm ".balign 8"
> +    f_gen_asm ".byte  0x7f, 'D, 'O, 'F\t/* dofh_ident[0..3] */"
> +    f_gen_asm ".byte  2\t\t/* model: 1=ILP32, 2=LP64 */"
> +    f_gen_asm ".byte  1\t\t/* encoding: 1: little-endian, 2: big-endian */"
> +    f_gen_asm ".byte  2\t\t/* DOF version: 1 or 2.  Latest is 2 */"
> +    f_gen_asm ".byte  2\t\t/* DIF version: 1 or 2.  Latest is 2 */"
> +    f_gen_asm ".byte  8\t\t/* number of DIF integer registers */"
> +    f_gen_asm ".byte  8\t\t/* number of DIF tuple registers */"
> +    f_gen_asm ".byte  0, 0\t\t/* dofh_ident[10..11] */"
> +    f_gen_asm ".4byte 0\t\t/* dofh_ident[12..15] */"
> +    f_gen_asm ".4byte 0\t/* uint32_t dofh_flags  */"  # See Limitations above.
> +    f_gen_asm ".4byte ${dof_hdrsize}\t/* uint32_t dofh_hdrsize  */"
> +    f_gen_asm ".4byte ${dof_secsize}\t/* uint32_t dofh_secsize */"
> +    f_gen_asm ".4byte ${dof_secnum}\t/* uint32_t dofh_secnum  */"
> +    f_gen_asm ".8byte ${dof_secoff}\t/* uint64_t dofh_secoff  */"
> +    f_gen_asm ".8byte ${BCOUNT}\t/* uint64_t dofh_loadsz  */"
> +    f_gen_asm ".8byte ${BCOUNT}\t/* uint64_t dofh_filesz  */"
> +    f_gen_asm ".8byte 0\t/* uint64_t dofh_pad  */"
> +    f_gen_asm ""
> +
> +    # Ok, now assembly the program in OFILE
> +    echo "$asmprogram$asmbody" | $AS -$objbits -o $ofile
> +
> +    # Next step is to change the sh_type of the ".SUNW_dof" section
> +    # headers to 0x6ffffff4 (SHT_SUNW_dof).
> +    #
> +    # Note that this code relies in the fact that readelf will list
> +    # the sections ordered in the same order than the section headers
> +    # in the section header table of the file.
> +    elfinfo=$($READELF -a $ofile)
> +
> +    # Mind the endianness.
> +    if printf %s "$elfinfo" | $EGREP -q "little endian"; then
> +        sht_sunw_dof=$(printf %s%s%s%s \\364 \\377 \\377 \\157)
> +    else
> +        sht_sunw_dof=$(printf %s%s%s%s \\157 \\377 \\377 \\364)
> +    fi
> +
> +    shdr_start=$(printf %s "$elfinfo" \
> +                | $EGREP "^[ \t]*Start of section headers:" \
> +                | $SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
> +    test -n "$shdr_start" \
> +        || f_panic "could not extract the start of shdr from $ofile"
> +
> +    shdr_num_entries=$(printf %s "$elfinfo" \
> +                       | $EGREP "^[ \t]*Size of section headers:" \
> +                       | $SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
> +    test -n "$shdr_num_entries" \
> +         || f_panic "could not extract the number of shdr entries from $ofile"
> +
> +    shdr_entry_size=$(printf %s "$elfinfo" \
> +                      | $EGREP "^[ \t]*Size of section headers:" \
> +                      | $SED -E -e 's/.*headers:[ \t]*([0-9]+).*/\1/')
> +    test -n "$shdr_entry_size" \
> +         || f_panic "could not fetch the size of section headers from $ofile"
> +
> +    while read line; do
> +        data=$(printf %s "$line" \
> +               | $SED -E -e 's/.*\[(.*)\][ \t]+([a-zA-Z_.]+).*/\1:\2/')
> +        num=$(printf %s "$data" | $CUT -d: -f1)
> +        name=$(printf %s "$data" | $CUT -d: -f2)
> +        if test "$name" = ".SUNW_dof"; then
> +            # Patch the new sh_type in the proper entry of the section
> +            # header table.
> +            printf "$sht_sunw_dof" \
> +                   | dd of=$ofile conv=notrunc count=4 ibs=1 bs=1 \
> +                        seek=$((shdr_start + (shdr_entry_size * num) + 4)) \
> +                        2> /dev/null
> +            break
> +        fi
> +    done <<EOF
> +$(printf %s "$elfinfo" | $EGREP "^[ \t]*\[[0-9 ]+\].*[A-Z]+.*PROGBITS")
> +EOF
> +
> +}
> +
> +# Patch the probed points in the given object file, replacing the
> +# function calls with NOPs.
> +#
> +# The probed points in the input object files are function calls.
> +# This function replaces these function calls by some other
> +# instruction sequences.  Which replacement to use depends on several
> +# factors, as documented below.
> +#
> +# Arguments:
> +#  $1 is the object file to patch.
> +
> +f_patch_objfile()
> +{
> +    objfile=$1
> +    
> +    # Several x86_64 instruction opcodes, in octal.
> +    x86_op_nop=$(printf \\220)
> +    x86_op_ret=$(printf \\303)
> +    x86_op_call=$(printf \\350)
> +    x86_op_jmp32=$(printf \\351)
> +    x86_op_rex_rax=$(printf \\110)
> +    x86_op_xor_eax_0=$(printf \\063)
> +    x86_op_xor_eax_1=$(printf \\300)
> +    
> +    # Figure out the file offset of the text section in the object
> +    # file.
> +    text_off=0x$(objdump -j .text -h $objfile \
> +                 | grep \.text | $TR -s ' ' | $CUT -d' ' -f 7)
> +
> +    while read type provider name offset base base_sym; do
> +        # Calculate the offset of the probed point in the object file.
> +        # Note that the `offset' of is-enabled probes is tweaked in
> +        # `f_collect_probes" to point ahead the patching point.
> +        probe_off=$((text_off + base + offset))
> +        if test "$type" = "e"; then
> +            if test "$objbits" -eq "32"; then
> +                probe_off=$((probe_off - 2))
> +            else # 64 bits
> +                probe_off=$((probe_off - 3))
> +            fi
> +        fi
> +
> +        # The probed point can be either a CALL instruction or a JMP
> +        # instruction (a tail call).  This has an impact on the
> +        # patching sequence.  Fetch the first byte at the probed point
> +        # and do the right thing.
> +        nopret="$x86_op_nop"
> +        byte=$(dd if=$objfile count=1 ibs=1 bs=1 skip=$probe_off 2> /dev/null)
> +        test "$byte" = "$x86_op_jmp32" && nopret="$x86_op_ret"
> +
> +        # Determine the patching sequence.  It depends on the type of
> +        # probe at hand (regular or is-enabled) and also if
> +        # manipulating a 32bit or 64bit binary.
> +        patchseq=
> +        case $type in
> +            p) patchseq=$(printf %s%s%s%s%s \
> +                                 "$nopret" \
> +                                 "$x86_op_nop" \
> +                                 "$x86_op_nop" \
> +                                 "$x86_op_nop" \
> +                                 "$x86_op_nop")
> +               ;;
> +            e) test "$objbits" -eq 64 && \
> +                 patchseq=$(printf %s%s%s%s%s \
> +                                   "$x86_op_rex_rax" \
> +                                   "$x86_op_xor_eax_0" \
> +                                   "$x86_op_xor_eax_1" \
> +                                   "$nopret" \
> +                                   "$x86_op_nop")
> +               test "$objbits" -eq 32 && \
> +                 patchseq=$(printf %s%s%s%s%s \
> +                                   "$x86_op_xor_eax_0" \
> +                                   "$x86_op_xor_eax_1" \
> +                                   "$nopret" \
> +                                   "$x86_op_nop" \
> +                                   "$x86_op_nop")
> +               ;;
> +            *) f_panic "internal error: wrong probe type $type";;
> +        esac
> +
> +        # Patch!
> +        printf %s "$patchseq" \
> +               | dd of=$objfile conv=notrunc count=5 ibs=1 bs=1 seek=$probe_off 2> /dev/null
> +    done <<EOF
> +$probes
> +EOF
> +    
> +    # Finally, we have to remove the __dtrace_* and __dtraceenabled_*
> +    # symbols from the object file, along with their respective
> +    # relocations.
> +    #
> +    # Note that the most obvious call:
> +    #   strip -v -N whatever -w foo.o
> +    # will not work:
> +    #   strip: not stripping symbol `whatever' because it is named in a relocation
> +    #
> +    # Fortunately using `-K !whatever' instead tricks strip to do the
> +    # right thing, but this is black magic and may eventually stop
> +    # working...
> +    $STRIP -K '!__dtrace_*' -w $objfile
> +    $STRIP -K '!__dtraceenabled_*' -w $objfile
> +}
> +
> +# Read the input .d file and print a header file with macros to
> +# invoke the probes defined in it.
> +
> +f_gen_header_file()
> +{
> +    guard=$(basename $ofile | $TR - _ | $CUT -d. -f1 | $TR a-z A-Z)
> +    printf "/*\n * Generated by pdtrace.\n */\n\n"
> +
> +    printf "#ifndef _${guard}_H\n"
> +    printf "#define _${guard}_H\n\n"
> +
> +    printf "#include <unistd.h>\n"
> +    printf "#include <inttypes.h>\n"
> +    printf \\n\\n
> +
> +    printf "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
> +
> +    printf "#define _DTRACE_VERSION 1\n\n"
> +
> +    provider=$(cat $dfile | $EGREP "^ *provider +([a-zA-Z_]+)" \
> +               | $SED -E -e 's/^ *provider +([a-zA-Z]+).*/\1/')
> +    test -z "$provider" \
> +        && f_panic "unable to parse the provider name from $dfile."
> +    u_provider=$(printf %s "$provider" | $TR a-z A-Z | $TR -s _)
> +    
> +    cat $dfile | $EGREP "^ *probe +[a-zA-Z_]+ *\(.*\);" | \
> +        while read line; do
> +            # Extract the probe name.
> +            name=$(printf %s "$line" \
> +                   | $SED -E -e 's/^ *probe +([a-zA-Z_]+).*/\1/')
> +            u_name=$(printf %s "$name" | $TR a-z A-Z | $TR -s _)
> +
> +            # Generate an arg1,arg2,...,argN line for the probe.
> +            args=""; nargs=0; aline=$(printf %s "$line" | $SED -e 's/.*(\(.*\)).*/\1/')
> +            set -f; IFS=,
> +            for arg in $aline; do
> +                args="${args}arg${nargs},"
> +                nargs=$((nargs + 1))
> +            done
> +            set +f; unset IFS
> +            args=${args%,}
> +
> +            echo "#if _DTRACE_VERSION"
> +            echo ""
> +            
> +            # Emit the macros for the probe.
> +            echo "#define ${u_provider}_${u_name}($args) \\"
> +            echo "   __dtrace_${provider}___${name}($args)"
> +            echo "#define ${u_provider}_${u_name}_ENABLED() \\"
> +            echo "   __dtraceenabled_${provider}___${name}()"
> +
> +            # Emit the extern definitions for the probe dummy
> +            # functions.
> +            echo ""
> +            printf %s\\n "$line" \
> +                | $SED -E -e "s/^ *probe +/extern void __dtrace_${provider}___/"
> +            echo "extern int __dtraceenabled_${provider}___${name}(void);"
> +
> +
> +            printf "\n#else\n"
> +
> +            # Emit empty macros for the probe
> +            echo "#define ${u_provider}_${u_name}($args)"
> +            echo "#define ${u_provider}_${u_name}_ENABLED() (0)"
> +
> +            printf "\n#endif /* _DTRACE_VERSION */\n"
> +        done
> +
> +    printf "#ifdef __cplusplus\n}\n#endif\n\n"
> +    printf "#endif /* _${guard}_H */\n"
> +}
> +
> +### Main program.
> +
> +# Process command line arguments.
> +
> +test "$#" -eq "0" && f_usage
> +
> +genelf=0
> +genheader=0
> +objbits=64
> +ofile=
> +dfile=
> +while getopts VG3264hs:o: name; do
> +    case $name in
> +        V) f_version;;
> +        s) dfile="$OPTARG";
> +           test -f "$dfile" || f_panic "cannot read $dfile";;
> +        o) ofile="$OPTARG";;
> +        G) genelf=1;;
> +        h) genheader=1;;
> +        # Note the trick to support -32
> +        3) objbits=666;;
> +        2) test "$objbits" -eq 666 || f_usage; objbits=32;;
> +        # Likewise for -64
> +        6) objbits=777;;
> +        4) test "$objbits" -eq 777 || f_usage; objbits=64;;
> +        ?) f_usage;;
> +    esac
> +done
> +shift $(($OPTIND - 1))
> +
> +test "$objbits" -eq "32" || test "$objbits" -eq "64" \
> +    || f_usage
> +
> +test $((genelf + genheader)) -gt 1 && \
> +    { echo "Please use either -G or -h."; f_usage; }
> +
> +test -n "$dfile" || { echo "Please specify a .d file with -s."; exit 2; }
> +
> +if test "$genelf" -gt 0; then
> +    # In this mode there must be a remaining argument: the name of the
> +    # object file to inspect for probed points.
> +    test "$#" -ne "1" && f_usage
> +    test -f "$1" || f_panic "cannot read $1"
> +    objfile=$1
> +
> +    # Collect probe information from the input object file and the
> +    # d-script.
> +    f_collect_probes $objfile    
> +    f_collect_probes_args $dfile
> +
> +    # Generate the assembly code and assemble the DOF program in
> +    # OFILE.  Then patch OBJFILE to remove the dummy probe calls.
> +    f_gen_dof_program
> +    f_patch_objfile $objfile
> +fi
> +
> +if test "$genheader" -gt 0; then
> +    test -n "$ofile" || { echo "Please specify an output file with -o."; exit 2; }
> +    
> +    # In this mode no extra arguments shall be present.
> +    test "$#" -ne "0" && f_usage
> +
> +    f_gen_header_file > $ofile
> +fi
> +
> +# pdtrace ends here.
> -- 
> 1.7.10.4

-- 
Sergio
GPG key ID: 0x65FC5E36
Please send encrypted e-mail if possible
http://sergiodj.net/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]