diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/testsuite/ChangeLog | 11 | ||||
-rwxr-xr-x | gdb/testsuite/configure | 51 | ||||
-rw-r--r-- | gdb/testsuite/configure.ac | 26 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dtrace-probe.c | 38 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dtrace-probe.d | 21 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dtrace-probe.exp | 106 | ||||
-rw-r--r-- | gdb/testsuite/lib/dtrace.exp | 71 | ||||
-rwxr-xr-x | gdb/testsuite/lib/pdtrace.in | 1033 |
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. |