From patchwork Tue Feb 17 16:18:15 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Jose E. Marchesi" X-Patchwork-Id: 5132 Received: (qmail 19925 invoked by alias); 17 Feb 2015 16:13:02 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 19832 invoked by uid 89); 17 Feb 2015 16:13:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.5 required=5.0 tests=AWL, BAYES_00, LIKELY_SPAM_BODY, SPF_PASS, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=no version=3.3.2 X-HELO: userp1040.oracle.com Received: from userp1040.oracle.com (HELO userp1040.oracle.com) (156.151.31.81) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-SHA encrypted) ESMTPS; Tue, 17 Feb 2015 16:12:53 +0000 Received: from aserv0021.oracle.com (aserv0021.oracle.com [141.146.126.233]) by userp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id t1HGCoLd025860 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 17 Feb 2015 16:12:50 GMT Received: from userz7022.oracle.com (userz7022.oracle.com [156.151.31.86]) by aserv0021.oracle.com (8.13.8/8.13.8) with ESMTP id t1HGCnML028033 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 17 Feb 2015 16:12:49 GMT Received: from abhmp0019.oracle.com (abhmp0019.oracle.com [141.146.116.25]) by userz7022.oracle.com (8.14.5+Sun/8.14.4) with ESMTP id t1HGCmuE006150 for ; Tue, 17 Feb 2015 16:12:48 GMT Received: from localhost.localdomain (/10.175.193.114) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 17 Feb 2015 08:12:48 -0800 From: "Jose E. Marchesi" To: gdb-patches@sourceware.org Subject: [COMMITTED V5 7/9] Simple testsuite for DTrace USDT probes. Date: Tue, 17 Feb 2015 17:18:15 +0100 Message-Id: <1424189897-32457-8-git-send-email-jose.marchesi@oracle.com> In-Reply-To: <1424189897-32457-1-git-send-email-jose.marchesi@oracle.com> References: <1424189897-32457-1-git-send-email-jose.marchesi@oracle.com> X-IsSubscribed: yes This patch adds some simple tests testing the support for DTrace USDT probes. The testsuite will be skipped as unsupported in case the user does not have DTrace installed on her system. The tests included in the test suite test breakpointing on DTrace probes, enabling and disabling probes, printing of probe arguments of several types and also breakpointing on several probes with the same name. gdb/ChangeLog: 2015-02-17 Jose E. Marchesi * 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. --- gdb/testsuite/ChangeLog | 11 + gdb/testsuite/configure | 51 ++ gdb/testsuite/configure.ac | 26 + gdb/testsuite/gdb.base/dtrace-probe.c | 38 ++ gdb/testsuite/gdb.base/dtrace-probe.d | 21 + gdb/testsuite/gdb.base/dtrace-probe.exp | 106 ++++ gdb/testsuite/lib/dtrace.exp | 71 +++ gdb/testsuite/lib/pdtrace.in | 1033 +++++++++++++++++++++++++++++++ 8 files changed, 1357 insertions(+) create mode 100644 gdb/testsuite/gdb.base/dtrace-probe.c create mode 100644 gdb/testsuite/gdb.base/dtrace-probe.d create mode 100644 gdb/testsuite/gdb.base/dtrace-probe.exp create mode 100644 gdb/testsuite/lib/dtrace.exp create mode 100755 gdb/testsuite/lib/pdtrace.in diff --git a/gdb/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 + * 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 + * 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 . */ + +#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 . */ + +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 . + +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 . + +# 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 +# . + +# 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 < 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 < /dev/null + break + fi + done < /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 <\n" + printf "#include \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.