aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/testsuite/ChangeLog11
-rwxr-xr-xgdb/testsuite/configure51
-rw-r--r--gdb/testsuite/configure.ac26
-rw-r--r--gdb/testsuite/gdb.base/dtrace-probe.c38
-rw-r--r--gdb/testsuite/gdb.base/dtrace-probe.d21
-rw-r--r--gdb/testsuite/gdb.base/dtrace-probe.exp106
-rw-r--r--gdb/testsuite/lib/dtrace.exp71
-rwxr-xr-xgdb/testsuite/lib/pdtrace.in1033
8 files changed, 1357 insertions, 0 deletions
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index b26baad..1e36b45 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,16 @@
2015-02-17 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.
+
+2015-02-17 Jose E. Marchesi <jose.marchesi@oracle.com>
+
* gdb.base/stap-probe.exp (stap_test): Remove "SystemTap" from
expected message when trying to access $_probe_* convenience
variables while not on a probe.
diff --git a/gdb/testsuite/configure b/gdb/testsuite/configure
index ca033c3..b593cd3 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,42 @@ 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"`
+
+transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
+STRIP_TRANSFORM_NAME=`echo strip | sed -e "$transform"`
+if test "x$STRIP_TRANSFORM_NAME" = x; then
+ STRIP_TRANSFORM_NAME=strip
+fi
+
+READELF_TRANSFORM_NAME=`echo readelf | sed -e "$transform"`
+if test "x$READELF_TRANSFORM_NAME" = x; then
+ READELF_TRANSFORM_NAME=readelf
+fi
+
+GAS_TRANSFORM_NAME=`echo as | sed -e "$transform"`
+if test "x$GAS_TRANSFORM_NAME" = x; then
+ GAS_TRANSFORM_NAME=as
+fi
+
+NM_TRANSFORM_NAME=`echo nm | sed -e "$transform"`
+if test "x$NM_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 +4203,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 +4645,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..eed0191 100644
--- a/gdb/testsuite/configure.ac
+++ b/gdb/testsuite/configure.ac
@@ -96,6 +96,32 @@ 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
+transform=`echo "$program_transform_name" | sed -e 's/[\\$][\\$]/\\$/g'`
+STRIP_TRANSFORM_NAME=`echo strip | sed -e "$transform"`
+if test "x$STRIP_TRANSFORM_NAME" = x; then
+ STRIP_TRANSFORM_NAME=strip
+fi
+AC_SUBST(STRIP_TRANSFORM_NAME)
+READELF_TRANSFORM_NAME=`echo readelf | sed -e "$transform"`
+if test "x$READELF_TRANSFORM_NAME" = x; then
+ READELF_TRANSFORM_NAME=readelf
+fi
+AC_SUBST(READELF_TRANSFORM_NAME)
+GAS_TRANSFORM_NAME=`echo as | sed -e "$transform"`
+if test "x$GAS_TRANSFORM_NAME" = x; then
+ GAS_TRANSFORM_NAME=as
+fi
+AC_SUBST(GAS_TRANSFORM_NAME)
+NM_TRANSFORM_NAME=`echo nm | sed -e "$transform"`
+if test "x$NM_TRANSFORM_NAME" = x; then
+ NM_TRANSFORM_NAME=nm
+fi
+AC_SUBST(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.