diff options
-rw-r--r-- | ChangeLog | 20 | ||||
-rwxr-xr-x | configure | 476 | ||||
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | elaborate.h | 61 | ||||
-rw-r--r-- | main.cxx | 20 | ||||
-rw-r--r-- | parse.cxx | 211 | ||||
-rw-r--r-- | parse.h | 18 | ||||
-rw-r--r-- | session.h | 104 | ||||
-rw-r--r-- | stap.1.in | 47 | ||||
-rw-r--r-- | staptree.h | 15 | ||||
-rwxr-xr-x | testsuite/parseok/fourteen.stp | 10 |
11 files changed, 874 insertions, 116 deletions
@@ -1,3 +1,23 @@ +2005-11-01 Frank Ch. Eigler <fche@elastic.org> + + PR 1425. + * configure.ac: Look for rpm-devel headers and libs. + * configure: Regenerated. + * session.h: New file to contain systemtap_session decl. + * staptree.h: Likewise evict statistics_decl. + * elaborate.h: Corresponding changes. + * main.cxx (usage): Elaborate. Re-enable "-r RELEASE" option. + * parse.cxx (parser): Add systemtap_session& field. Update users. + (scan_pp, eval_pp_conditional): New routines for preprocessing. + (peek, next): Call it. + (lexer::scan): Lex the preprocessor operators. + (parser::parse): Include an extra level of exception catching + for parse errors that occur during recovery. + * parse.h: Corresponding changes. + (parse_error): Allow explicit token parameter. + * stap.1.in: Document preprocessing. + * testsuite/parseok/fourteen.stp: New test. + 2005-10-31 Roland McGrath <roland@redhat.com> * systemtap.spec.in, configure.ac: Version 0.4.2 cooked. @@ -310,7 +310,7 @@ ac_includes_default="\ # include <unistd.h> #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LN_S CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CPP EGREP U ANSI2KNR RANLIB ac_ct_RANLIB stap_LIBS DATE LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LN_S CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CPP EGREP U ANSI2KNR RANLIB ac_ct_RANLIB stap_LIBS CXXCPP DATE LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -779,6 +779,10 @@ ac_env_CPP_set=${CPP+set} ac_env_CPP_value=$CPP ac_cv_env_CPP_set=${CPP+set} ac_cv_env_CPP_value=$CPP +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP # # Report the --help message. @@ -871,6 +875,7 @@ Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags CPP C preprocessor + CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -5071,6 +5076,8 @@ fi +CPPFLAGS="$CPPFLAGS -I/usr/include/rpm" + save_LIBS="$LIBS" @@ -5157,6 +5164,472 @@ stap_LIBS="$LIBS" LIBS="$SAVE_LIBS" +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + +for ac_header in rpmlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------- ## +## Report this to systemtap@sources.redhat.com ## +## ------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +else + + { { echo "$as_me:$LINENO: error: systemtap requires rpmlib.h" >&5 +echo "$as_me: error: systemtap requires rpmlib.h" >&2;} + { (exit 1); exit 1; }; } +fi + +done + + +echo "$as_me:$LINENO: checking for rpmvercmp in -lrpm" >&5 +echo $ECHO_N "checking for rpmvercmp in -lrpm... $ECHO_C" >&6 +if test "${ac_cv_lib_rpm_rpmvercmp+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrpm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char rpmvercmp (); +int +main () +{ +rpmvercmp (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_rpm_rpmvercmp=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_rpm_rpmvercmp=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_rpm_rpmvercmp" >&5 +echo "${ECHO_T}$ac_cv_lib_rpm_rpmvercmp" >&6 +if test $ac_cv_lib_rpm_rpmvercmp = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRPM 1 +_ACEOF + + LIBS="-lrpm $LIBS" + +else + + { { echo "$as_me:$LINENO: error: systemtap requires librpm" >&5 +echo "$as_me: error: systemtap requires librpm" >&2;} + { (exit 1); exit 1; }; } +fi + + date=`date +%Y-%m-%d` cat >>confdefs.h <<_ACEOF @@ -5889,6 +6362,7 @@ s,@ANSI2KNR@,$ANSI2KNR,;t t s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@stap_LIBS@,$stap_LIBS,;t t +s,@CXXCPP@,$CXXCPP,;t t s,@DATE@,$DATE,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t diff --git a/configure.ac b/configure.ac index 88e68f06..3d7d81ef 100644 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,8 @@ AC_PROG_INSTALL AC_PROG_MAKE_SET AC_SUBST(CFLAGS) AC_SUBST(CXXFLAGS) +CPPFLAGS="$CPPFLAGS -I/usr/include/rpm" +AC_SUBST(CPPFLAGS) dnl Need libdwfl-capable recent elfutils from Fedora save_LIBS="$LIBS" @@ -30,6 +32,12 @@ stap_LIBS="$LIBS" LIBS="$SAVE_LIBS" AC_SUBST(stap_LIBS) +dnl Need rpmlib for rpmvercmp +AC_CHECK_HEADERS([rpmlib.h],,[ + AC_MSG_ERROR([systemtap requires rpmlib.h])]) +AC_CHECK_LIB([rpm], [rpmvercmp],,[ + AC_MSG_ERROR([systemtap requires librpm])]) + dnl Plop in the build (configure) date date=`date +%Y-%m-%d` AC_DEFINE_UNQUOTED(DATE, "$date", [Configuration/build date]) diff --git a/elaborate.h b/elaborate.h index 0abc04e0..df021076 100644 --- a/elaborate.h +++ b/elaborate.h @@ -9,6 +9,7 @@ #ifndef ELABORATE_H #define ELABORATE_H +#include "session.h" #include "staptree.h" #include "parse.h" #include <string> @@ -19,7 +20,6 @@ // ------------------------------------------------------------------------ -struct systemtap_session; struct derived_probe; struct match_node; @@ -191,64 +191,7 @@ match_node // ------------------------------------------------------------------------ - -class unparser; -class translator_output; - -struct systemtap_session -{ - systemtap_session (); - - // command line args - std::vector<std::string> include_path; - std::vector<std::string> macros; - std::vector<std::string> args; - std::string kernel_release; - std::string runtime_path; - std::string module_name; - std::string output_file; - std::string cmd; - int target_pid; - int last_pass; - bool test_mode; - bool verbose; - bool keep_tmpdir; - bool guru_mode; - bool bulk_mode; - int buffer_size; - - // temporary directory for module builds etc. - // hazardous - it is "rm -rf"'d at exit - std::string tmpdir; - std::string translated_source; // C source code - - match_node* pattern_root; - void register_library_aliases(); - - // parse trees for the various script files - stapfile* user_file; - std::vector<stapfile*> library_files; - - // resolved globals/functions/probes for the run as a whole - std::vector<stapfile*> files; - std::vector<vardecl*> globals; - std::vector<functiondecl*> functions; - std::vector<derived_probe*> probes; - std::vector<embeddedcode*> embeds; - std::map<std::string, statistic_decl> stat_decls; - - // module-referencing file handles - std::map<std::string,int> module_fds; - - // unparser data - translator_output* op; - unparser* up; - - unsigned num_errors; - // void print_error (const parse_error& e); - void print_error (const semantic_error& e); -}; - +/* struct systemtap_session moved to session.h */ int semantic_pass (systemtap_session& s); void derive_probes (systemtap_session& s, @@ -63,8 +63,10 @@ usage (systemtap_session& s) // << " -t test mode" << (s.test_mode ? " [set]" : "") << endl << " -g guru mode" << (s.guru_mode ? " [set]" : "") << endl << " -b bulk (relayfs) mode" << (s.bulk_mode ? " [set]" : "") << endl - << " -s NUM buffer size in megabytes" << endl - << " -p NUM stop after pass NUM 1-5" << endl + << " -s NUM buffer size in megabytes, instead of " + << s.buffer_size << endl + << " -p NUM stop after pass NUM 1-5, instead of " + << s.last_pass << endl << " (parse, elaborate, translate, compile, run)" << endl << " -I DIR look in DIR for additional .stp script files"; if (s.include_path.size() == 0) @@ -77,11 +79,11 @@ usage (systemtap_session& s) << " -D NM=VAL emit macro definition into generated C code" << endl << " -R DIR look in DIR for runtime, instead of" << endl << " " << s.runtime_path << endl - // << " -r RELEASE use kernel RELEASE, instead of" << endl - // << " " << s.kernel_release << endl + << " -r RELEASE use kernel RELEASE, instead of " + << s.kernel_release << endl << " -m MODULE set probe module name, instead of " << s.module_name << endl - << " -o FILE send output to file instead of stdout" << endl + << " -o FILE send output to file, instead of stdout" << endl << " -c CMD start the probes, run CMD, and exit when it finishes" << endl << " -x PID sets target() to PID" << endl @@ -289,13 +291,13 @@ main (int argc, char * const argv []) // XXX: pass args vector, so parser (or lexer?) can substitute // $1..$NN with actual arguments if (script_file == "-") - s.user_file = parser::parse (cin, s.guru_mode); + s.user_file = parser::parse (s, cin, s.guru_mode); else if (script_file != "") - s.user_file = parser::parse (script_file, s.guru_mode); + s.user_file = parser::parse (s, script_file, s.guru_mode); else { istringstream ii (cmdline_script); - s.user_file = parser::parse (ii, s.guru_mode); + s.user_file = parser::parse (s, ii, s.guru_mode); } if (s.user_file == 0) // syntax errors already printed @@ -338,7 +340,7 @@ main (int argc, char * const argv []) for (unsigned j=0; j<globbuf.gl_pathc; j++) { // privilege only for /usr/share/systemtap? - stapfile* f = parser::parse (globbuf.gl_pathv[j], true); + stapfile* f = parser::parse (s, globbuf.gl_pathv[j], true); if (f == 0) rc ++; else @@ -9,6 +9,7 @@ #include "config.h" #include "staptree.h" #include "parse.h" +#include "session.h" #include <iostream> #include <fstream> #include <cctype> @@ -16,6 +17,7 @@ #include <cerrno> #include <climits> #include <sstream> +#include "rpmlib.h" // for rpmvercmp using namespace std; @@ -23,13 +25,15 @@ using namespace std; -parser::parser (istream& i, bool p): +parser::parser (systemtap_session& s, istream& i, bool p): + session (s), input_name ("<input>"), free_input (0), input (i, input_name), privileged (p), last_t (0), next_t (0), num_errors (0) { } -parser::parser (const string& fn, bool p): +parser::parser (systemtap_session& s, const string& fn, bool p): + session (s), input_name (fn), free_input (new ifstream (input_name.c_str(), ios::in)), input (* free_input, input_name), privileged (p), last_t (0), next_t (0), num_errors (0) @@ -42,17 +46,17 @@ parser::~parser() stapfile* -parser::parse (std::istream& i, bool pr) +parser::parse (systemtap_session& s, std::istream& i, bool pr) { - parser p (i, pr); + parser p (s, i, pr); return p.parse (); } stapfile* -parser::parse (const std::string& n, bool pr) +parser::parse (systemtap_session& s, const std::string& n, bool pr) { - parser p (n, pr); + parser p (s, n, pr); return p.parse (); } @@ -101,11 +105,18 @@ parser::print_error (const parse_error &pe) { cerr << "parse error: " << pe.what () << endl; - const token* t = last_t; - if (t) - cerr << "\tsaw: " << *t << endl; + if (pe.tok) + { + cerr << "\tat: " << *pe.tok << endl; + } else - cerr << "\tsaw: " << input_name << " EOF" << endl; + { + const token* t = last_t; + if (t) + cerr << "\tsaw: " << *t << endl; + else + cerr << "\tsaw: " << input_name << " EOF" << endl; + } // XXX: make it possible to print the last input line, // so as to line up an arrow with the specific error column @@ -121,11 +132,148 @@ parser::last () } +// Here, we perform on-the-fly preprocessing. +// The basic form is %( CONDITION %? THEN-TOKENS %: ELSE-TOKENS %) +// where CONDITION is "kernel_v[r]" COMPARISON-OP "version-string", and +// the %: ELSE-TOKENS part is optional. +// +// e.g. %( kernel_v > "2.5" %? "foo" %: "baz" %) +// +// Up to an entire %( ... %) expression is processed by a single call +// to this function. Tokens included by any nested conditions are +// enqueued in a private vector. + +bool eval_pp_conditional (systemtap_session& s, + const token* l, const token* op, const token* r) +{ + if (! (l->type == tok_identifier && (l->content == "kernel_v" || + l->content == "kernel_vr"))) + throw parse_error ("expected 'kernel_v' or 'kernel_vr'", l); + string target_kernel_vr = s.kernel_release; + string target_kernel_v = target_kernel_vr; + // cut off any release code suffix + string::size_type dr = target_kernel_vr.rfind ('-'); + if (dr > 0 && dr != string::npos) + target_kernel_v = target_kernel_vr.substr (0, dr); + + if (! (r->type == tok_string)) + throw parse_error ("expected string literal", r); + string query_kernel_vr = r->content; + + // collect acceptable rpmvercmp results. + int rvc_ok1, rvc_ok2; + if (op->type == tok_operator && op->content == "<=") + { rvc_ok1 = -1; rvc_ok2 = 0; } + else if (op->type == tok_operator && op->content == ">=") + { rvc_ok1 = 1; rvc_ok2 = 0; } + else if (op->type == tok_operator && op->content == "<") + { rvc_ok1 = -1; rvc_ok2 = -1; } + else if (op->type == tok_operator && op->content == ">") + { rvc_ok1 = 1; rvc_ok2 = 1; } + else if (op->type == tok_operator && op->content == "==") + { rvc_ok1 = 0; rvc_ok2 = 0; } + else if (op->type == tok_operator && op->content == "!=") + { rvc_ok1 = -1; rvc_ok2 = 1; } + else + throw parse_error ("expected comparison operator", op); + + int rvc_result = rpmvercmp ((l->content == "kernel_vr" ? + target_kernel_vr.c_str() : + target_kernel_v.c_str()), + query_kernel_vr.c_str()); + return (rvc_result == rvc_ok1 || rvc_result == rvc_ok2); +} + + +const token* +parser::scan_pp () +{ + while (true) + { + if (enqueued_pp.size() > 0) + { + const token* t = enqueued_pp[0]; + enqueued_pp.erase (enqueued_pp.begin()); + return t; + } + + const token* t = input.scan (); // NB: not recursive! + if (t == 0) // EOF + return t; + + if (! (t->type == tok_operator && t->content == "%(")) // ordinary token + return t; + + // We have a %( - it's time to throw a preprocessing party! + + const token *l, *op, *r; + l = input.scan (); // NB: not recursive, though perhaps could be + op = input.scan (); + r = input.scan (); + if (l == 0 || op == 0 || r == 0) + throw parse_error ("incomplete condition after '%('", t); + // NB: consider generalizing to consume all tokens until %?, and + // passing that as a vector to an evaluator. + + bool result = eval_pp_conditional (session, l, op, r); + + const token *m = input.scan (); // NB: not recursive + if (! (m && m->type == tok_operator && m->content == "%?")) + throw parse_error ("expected '%?' marker for conditional", t); + + vector<const token*> my_enqueued_pp; + + while (true) // consume THEN tokens + { + m = scan_pp (); // NB: recursive + if (m == 0) + throw parse_error ("missing THEN tokens for conditional", t); + + if (m->type == tok_operator && (m->content == "%:" || // ELSE + m->content == "%)")) // END + break; + // enqueue token + if (result) + my_enqueued_pp.push_back (m); + // continue + } + + if (m && m->type == tok_operator && m->content == "%:") // ELSE + while (true) + { + m = scan_pp (); // NB: recursive + if (m == 0) + throw parse_error ("missing ELSE tokens for conditional", t); + + if (m->type == tok_operator && m->content == "%)") // END + break; + // enqueue token + if (! result) + my_enqueued_pp.push_back (m); + // continue + } + + // NB: we transcribe the retained tokens here, and not inside + // the THEN/ELSE while loops. If it were done there, each loop + // would become infinite (each iteration consuming an ordinary + // token the previous one just pushed there). Guess how I + // figured that out. + enqueued_pp.insert (enqueued_pp.end(), + my_enqueued_pp.begin(), + my_enqueued_pp.end()); + + // Go back to outermost while(true) loop. We hope that at least + // some THEN or ELSE tokens were enqueued. If not, around we go + // again, until EOF. + } +} + + const token* parser::next () { if (! next_t) - next_t = input.scan (); + next_t = scan_pp (); if (! next_t) throw parse_error ("unexpected end-of-file"); @@ -140,9 +288,7 @@ const token* parser::peek () { if (! next_t) - next_t = input.scan (); - - // cerr << "{" << (next_t ? next_t->content : "null") << "}"; + next_t = scan_pp (); // don't advance by zeroing next_t last_t = next_t; @@ -474,7 +620,12 @@ lexer::scan () s2 == "--" || s2 == "->" || s2 == "<<" || - s2 == ">>") + s2 == ">>" || + // preprocessor tokens + s2 == "%(" || + s2 == "%?" || + s2 == "%:" || + s2 == "%)") { n->content = s2; input_get (); // swallow other character @@ -529,17 +680,25 @@ parser::parse () catch (parse_error& pe) { print_error (pe); - // Quietly swallow all tokens until the next '}'. - while (1) - { - const token* t = peek (); - if (! t) - break; - next (); - if (t->type == tok_operator && t->content == "}") - break; - } - } + try + { + // Quietly swallow all tokens until the next '}'. + while (1) + { + const token* t = peek (); + if (! t) + break; + next (); + if (t->type == tok_operator && t->content == "}") + break; + } + } + catch (parse_error& pe2) + { + // parse error during recovery ... ugh + print_error (pe2); + } + } } if (empty) @@ -46,7 +46,10 @@ std::ostream& operator << (std::ostream& o, const token& t); struct parse_error: public std::runtime_error { - parse_error (const std::string& msg): runtime_error (msg) {} + const token* tok; + parse_error (const std::string& msg): runtime_error (msg), tok (0) {} + parse_error (const std::string& msg, const token* t): runtime_error (msg), + tok (t) {} }; @@ -70,21 +73,26 @@ private: class parser { public: - parser (std::istream& i, bool p); - parser (const std::string& n, bool p); + parser (systemtap_session& s, std::istream& i, bool p); + parser (systemtap_session& s, const std::string& n, bool p); ~parser (); stapfile* parse (); - static stapfile* parse (std::istream& i, bool privileged); - static stapfile* parse (const std::string& n, bool privileged); + static stapfile* parse (systemtap_session& s, std::istream& i, bool privileged); + static stapfile* parse (systemtap_session& s, const std::string& n, bool privileged); private: + systemtap_session& session; std::string input_name; std::istream* free_input; lexer input; bool privileged; + // preprocessing subordinate + std::vector<const token*> enqueued_pp; + const token* scan_pp (); + // scanning state const token* last (); const token* next (); diff --git a/session.h b/session.h new file mode 100644 index 00000000..666d8ce2 --- /dev/null +++ b/session.h @@ -0,0 +1,104 @@ +// -*- C++ -*- +// Copyright (C) 2005 Red Hat Inc. +// +// This file is part of systemtap, and is free software. You can +// redistribute it and/or modify it under the terms of the GNU General +// Public License (GPL); either version 2, or (at your option) any +// later version. + +#ifndef SESSION_H +#define SESSION_H + +#include <string> +#include <vector> +#include <iostream> +#include <sstream> +#include <map> + + +// forward decls for all referenced systemtap types +struct match_node; +struct stapfile; +struct vardecl; +struct functiondecl; +struct derived_probe; +struct embeddedcode; +struct translator_output; +struct unparser; +struct semantic_error; + + +// XXX: a generalized form of this descriptor could be associated with +// a vardecl instead of out here at the systemtap_session level. +struct statistic_decl +{ + statistic_decl() + : type(none), + logarithmic_buckets(0), + linear_low(0), linear_high(0), linear_step(0) + {} + enum { none, linear, logarithmic } type; + int64_t logarithmic_buckets; + int64_t linear_low; + int64_t linear_high; + int64_t linear_step; +}; + + +struct systemtap_session +{ + systemtap_session (); + + // command line args + std::vector<std::string> include_path; + std::vector<std::string> macros; + std::vector<std::string> args; + std::string kernel_release; + std::string runtime_path; + std::string module_name; + std::string output_file; + std::string cmd; + int target_pid; + int last_pass; + bool test_mode; + bool verbose; + bool keep_tmpdir; + bool guru_mode; + bool bulk_mode; + int buffer_size; + + // temporary directory for module builds etc. + // hazardous - it is "rm -rf"'d at exit + std::string tmpdir; + std::string translated_source; // C source code + + match_node* pattern_root; + void register_library_aliases(); + + // parse trees for the various script files + stapfile* user_file; + std::vector<stapfile*> library_files; + + // resolved globals/functions/probes for the run as a whole + std::vector<stapfile*> files; + std::vector<vardecl*> globals; + std::vector<functiondecl*> functions; + std::vector<derived_probe*> probes; + std::vector<embeddedcode*> embeds; + std::map<std::string, statistic_decl> stat_decls; + // XXX: vector<*> instead please? + + // module-referencing file handles + std::map<std::string,int> module_fds; + + // unparser data + translator_output* op; + unparser* up; + + unsigned num_errors; + // void print_error (const parse_error& e); + void print_error (const semantic_error& e); +}; + + +#endif // SESSION_H @@ -70,7 +70,6 @@ The systemtap translator supports the following options. Any other option prints a list of supported options. .\" undocumented for now: .\" -t test mode -.\" -r RELEASE .TP .B \-v Verbose mode. Produces more informative output. @@ -108,12 +107,15 @@ Add the given directory to the tapset search directory. See the description of pass 2 for details. .TP .BI \-D " NAME=VALUE" -Add the given preprocessor directive to the module Makefile. These can +Add the given C preprocessor directive to the module Makefile. These can be used to override limit parameters described below. .TP .BI \-R " DIR" Look for the systemtap runtime sources in the given directory. .TP +.BI \-r " RELEASE" +Build for given kernel release instead of currently running one. +.TP .BI \-m " MODULE" Use the given name for the generated kernel object module, instead of a unique randomized name. @@ -151,7 +153,46 @@ the usual C escape codes with backslashes), or integers (in decimal, hexadecimal, or octal, using the same notation as in C). All strings are limited in length to some reasonable value (a few hundred bytes). Integers are 64-bit signed quantities, although the parser also accepts -(and wraps around) values above positive 2**63. +(and wraps around) values above positive 2**63. +.PP +A simple conditional preprocessing stage is run as a part of parsing. +The general form is similar to the +.RB cond " ? " exp1 " : " exp2 +ternary operator: +.SAMPLE +.BR %( " CONDITION " %? " TRUE-TOKENS " %) +.BR %( " CONDITION " %? " TRUE-TOKENS " %: " FALSE-TOKENS " %) +.ESAMPLE +The CONDITION is a very limited expression consisting of three +parts. The first part is the identifier +.BR kernel_vr " or " kernel_v +to refer to the kernel version number, with ("2.6.13-1.322FC3smp") or +without ("2.6.13") the release code suffix. +The second part is one of the six standard numeric comparison operators +.BR < ", " <= ", " == ", " != ", " > ", and " >= . +The third part is a string literal that contains an RPM-style +version-release value. The condition is deemed satisfied if the +version of the target kernel (as optionally overridden by the +.BR \-r +option) compares to the given version string. The comparison is +performed by the RPM library function +.BR rpmvercmp . +The TRUE-TOKENS and FALSE-TOKENS are zero or more general parser +tokens (possibly including nested preprocessor conditionals), and are +pasted into the input stream if the condition is true or false. For +example, the following code induces a parse error unless the target +kernel version is newer than 2.6.5: +.SAMPLE +%( kernel_v <= "2.6.5" %? **ERROR** %) # invalid token sequence +.ESAMPLE +The following code might adapt to hypothetical kernel version drift: +.SAMPLE +probe kernel.function ( + %( kernel_v <= "2.6.12" %? "__mm_do_fault" %: + %( kernel_vr == "2.6.13-1.8273FC3smp" %? "do_page_fault" %: + UNSUPPORTED %) %) +) { /* ... */ } +.ESAMPLE .SS VARIABLES Identifiers for variables and functions are an alphanumeric sequence, @@ -9,6 +9,7 @@ #ifndef STAPTREE_H #define STAPTREE_H +#include "session.h" #include <map> #include <stack> #include <string> @@ -38,19 +39,7 @@ struct semantic_error: public std::runtime_error // ------------------------------------------------------------------------ -struct statistic_decl -{ - statistic_decl() - : type(none), - logarithmic_buckets(0), - linear_low(0), linear_high(0), linear_step(0) - {} - enum { none, linear, logarithmic } type; - int64_t logarithmic_buckets; - int64_t linear_low; - int64_t linear_high; - int64_t linear_step; -}; +/* struct statistic_decl moved to session.h */ // ------------------------------------------------------------------------ diff --git a/testsuite/parseok/fourteen.stp b/testsuite/parseok/fourteen.stp new file mode 100755 index 00000000..e68aa4fe --- /dev/null +++ b/testsuite/parseok/fourteen.stp @@ -0,0 +1,10 @@ +#! stap -p1 + +global +%( kernel_v > "2.6" %? /* and */ + %( kernel_vr != "2.9.77-2873NOTHING" %? /* and */ + %( kernel_v <= "3.5" %? /* and */ + %( kernel_vr == "2.3.5-2.43.54.2" %? "FAIL1" %: PASS %) + %: "FAIL2" %) + %: "FAIL3" %) +%: "FAIL4" %) |