diff options
author | fche <fche> | 2005-05-21 01:35:34 +0000 |
---|---|---|
committer | fche <fche> | 2005-05-21 01:35:34 +0000 |
commit | 2b066ec1b8801b08052a68282ce34ef9c425ae8f (patch) | |
tree | d0b8aadc2521e2fbf1adde2d330bd7a941587087 | |
parent | a199030a268b007580b57a83b511f97bbb65996f (diff) | |
download | systemtap-steved-2b066ec1b8801b08052a68282ce34ef9c425ae8f.tar.gz systemtap-steved-2b066ec1b8801b08052a68282ce34ef9c425ae8f.tar.xz systemtap-steved-2b066ec1b8801b08052a68282ce34ef9c425ae8f.zip |
* at long last, a more full-bodied snapshot
2005-05-20 Frank Ch. Eigler <fche@redhat.com>
Many changes throughout. Partial sketch of translation output.
* elaborate.*: Elaboration pass.
* translate.*: Translation pass.
* staptree.*: Simplified for visitor concept.
* main.cxx: Translator mainline.
* *test.cxx: Removed.
* testsuite/*: Some new tests, some changed for newer syntax.
50 files changed, 3467 insertions, 1140 deletions
diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 00000000..c6be8e47 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +autom4te.* cscope*out @@ -1,3 +1,13 @@ +2005-05-20 Frank Ch. Eigler <fche@redhat.com> + + Many changes throughout. Partial sketch of translation output. + * elaborate.*: Elaboration pass. + * translate.*: Translation pass. + * staptree.*: Simplified for visitor concept. + * main.cxx: Translator mainline. + * *test.cxx: Removed. + * testsuite/*: Some new tests, some changed for newer syntax. + 2005-05-05 Frank Ch. Eigler <fche@redhat.com> * parse.cxx (parse): Add helper methods. diff --git a/Makefile.am b/Makefile.am index ff1651c4..d1ea8e1a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,9 +4,8 @@ AM_MAKEFLAGS = 'CXXFLAGS=$(CXXFLAGS)' 'LDFLAGS=$(LDFLAGS)' bin_PROGRAMS = -noinst_PROGRAMS = parsetest semtest -parsetest_SOURCES = parse.cxx staptree.cxx parsetest.cxx -semtest_SOURCES = parse.cxx staptree.cxx semtest.cxx +noinst_PROGRAMS = stap +stap_SOURCES = parse.cxx staptree.cxx elaborate.cxx translate.cxx main.cxx AM_CXXFLAGS = -Wall # Get extra libs as needed diff --git a/Makefile.in b/Makefile.in index 2b995761..483f3188 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,7 +16,7 @@ # Makefile.am --- automake input file for systemtap -SOURCES = $(parsetest_SOURCES) $(semtest_SOURCES) +SOURCES = $(stap_SOURCES) srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -39,7 +39,7 @@ NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : bin_PROGRAMS = -noinst_PROGRAMS = parsetest$(EXEEXT) semtest$(EXEEXT) +noinst_PROGRAMS = stap$(EXEEXT) subdir = . DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/config.in \ @@ -57,16 +57,11 @@ CONFIG_CLEAN_FILES = am__installdirs = "$(DESTDIR)$(bindir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) -am_parsetest_OBJECTS = parse.$(OBJEXT) staptree.$(OBJEXT) \ - parsetest.$(OBJEXT) -parsetest_OBJECTS = $(am_parsetest_OBJECTS) -parsetest_LDADD = $(LDADD) -parsetest_DEPENDENCIES = -am_semtest_OBJECTS = parse.$(OBJEXT) staptree.$(OBJEXT) \ - semtest.$(OBJEXT) -semtest_OBJECTS = $(am_semtest_OBJECTS) -semtest_LDADD = $(LDADD) -semtest_DEPENDENCIES = +am_stap_OBJECTS = parse.$(OBJEXT) staptree.$(OBJEXT) \ + elaborate.$(OBJEXT) translate.$(OBJEXT) main.$(OBJEXT) +stap_OBJECTS = $(am_stap_OBJECTS) +stap_LDADD = $(LDADD) +stap_DEPENDENCIES = DEFAULT_INCLUDES = -I. -I$(srcdir) -I. depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -75,8 +70,8 @@ CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ -SOURCES = $(parsetest_SOURCES) $(semtest_SOURCES) -DIST_SOURCES = $(parsetest_SOURCES) $(semtest_SOURCES) +SOURCES = $(stap_SOURCES) +DIST_SOURCES = $(stap_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -104,6 +99,7 @@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ +CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -177,8 +173,7 @@ sharedstatedir = @sharedstatedir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ AM_MAKEFLAGS = 'CXXFLAGS=$(CXXFLAGS)' 'LDFLAGS=$(LDFLAGS)' -parsetest_SOURCES = parse.cxx staptree.cxx parsetest.cxx -semtest_SOURCES = parse.cxx staptree.cxx semtest.cxx +stap_SOURCES = parse.cxx staptree.cxx elaborate.cxx translate.cxx main.cxx AM_CXXFLAGS = -Wall # Get extra libs as needed @@ -275,12 +270,9 @@ clean-binPROGRAMS: clean-noinstPROGRAMS: -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) -parsetest$(EXEEXT): $(parsetest_OBJECTS) $(parsetest_DEPENDENCIES) - @rm -f parsetest$(EXEEXT) - $(CXXLINK) $(parsetest_LDFLAGS) $(parsetest_OBJECTS) $(parsetest_LDADD) $(LIBS) -semtest$(EXEEXT): $(semtest_OBJECTS) $(semtest_DEPENDENCIES) - @rm -f semtest$(EXEEXT) - $(CXXLINK) $(semtest_LDFLAGS) $(semtest_OBJECTS) $(semtest_LDADD) $(LIBS) +stap$(EXEEXT): $(stap_OBJECTS) $(stap_DEPENDENCIES) + @rm -f stap$(EXEEXT) + $(CXXLINK) $(stap_LDFLAGS) $(stap_OBJECTS) $(stap_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -288,10 +280,11 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elaborate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parsetest.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/semtest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staptree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/translate.Po@am__quote@ .cxx.o: @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ @@ -1,12 +1,14 @@ -systemtap prototype #3.1 +systemtap prototype #3.2 - demonstrates partial parsing of hypothetical systemtap script language using hand-written simpe LL(1) recursive-descent parser and similar little lexer: parse.cxx, parse.h -- semantic analysis in stapfile.cxx, driven from semtest.cxx +- semantic analysis in elaborate.cxx, driven from main.cxx - examples under testsuite - "probe", "global", "function" top-level constructs parsed no provider-oriented syntax provided yet +- some tapset library auto-inclusion supported - use autotools-style configure; make; make check -- to see parse tree dump, use stdin: echo 'SCRIPT FRAGMENT' | ./parsetest -- to see type inference results, use stdin: echo 'SCRIPT FRAGMENT' | ./semtest +- to see parse tree: stap -p1 -e 'SCRIPT FRAGMENT' +- to see semantic/type analysis results: stap -p2 -e 'SCRIPT FRAGMENT' +- to see translation of subset of constructs: stap -e 'SCRIPT FRAGMENT' @@ -1,8 +1,20 @@ /* config.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the <elfutils/libdw.h> header file. */ +#undef HAVE_ELFUTILS_LIBDW_H + /* Define to 1 if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the `dw' library (-ldw). */ +#undef HAVE_LIBDW + +/* Define to 1 if you have the `elf' library (-lelf). */ +#undef HAVE_LIBELF + +/* Define to 1 if you have the <libelf.h> header file. */ +#undef HAVE_LIBELF_H + /* Define to 1 if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H @@ -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 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 CXXCPP 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. @@ -5072,6 +5077,538 @@ 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 +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 libelf.h elfutils/libdw.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 + +fi + +done + + +echo "$as_me:$LINENO: checking for dwarf_begin in -ldw" >&5 +echo $ECHO_N "checking for dwarf_begin in -ldw... $ECHO_C" >&6 +if test "${ac_cv_lib_dw_dwarf_begin+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldw $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 dwarf_begin (); +int +main () +{ +dwarf_begin (); + ; + 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_dw_dwarf_begin=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dw_dwarf_begin=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_dw_dwarf_begin" >&5 +echo "${ECHO_T}$ac_cv_lib_dw_dwarf_begin" >&6 +if test $ac_cv_lib_dw_dwarf_begin = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBDW 1 +_ACEOF + + LIBS="-ldw $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for elf_begin in -lelf" >&5 +echo $ECHO_N "checking for elf_begin in -lelf... $ECHO_C" >&6 +if test "${ac_cv_lib_elf_elf_begin+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lelf $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 elf_begin (); +int +main () +{ +elf_begin (); + ; + 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_elf_elf_begin=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_elf_elf_begin=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_elf_elf_begin" >&5 +echo "${ECHO_T}$ac_cv_lib_elf_elf_begin" >&6 +if test $ac_cv_lib_elf_elf_begin = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBELF 1 +_ACEOF + + LIBS="-lelf $LIBS" + +fi + + ac_config_headers="$ac_config_headers config.h:config.in" ac_config_files="$ac_config_files Makefile" @@ -5788,6 +6325,7 @@ s,@U@,$U,;t t s,@ANSI2KNR@,$ANSI2KNR,;t t s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@CXXCPP@,$CXXCPP,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF diff --git a/configure.ac b/configure.ac index 0ffaa19f..2c609720 100644 --- a/configure.ac +++ b/configure.ac @@ -22,6 +22,11 @@ AC_PROG_MAKE_SET AC_SUBST(CFLAGS) AC_SUBST(CXXFLAGS) +dnl Need elfutils 0.102+ from <drepper@redhat.com> +AC_CHECK_HEADERS([libelf.h elfutils/libdw.h]) +AC_CHECK_LIB(dw, dwarf_begin) +AC_CHECK_LIB(elf, elf_begin) + AC_CONFIG_HEADERS([config.h:config.in]) AC_CONFIG_FILES(Makefile) AC_OUTPUT diff --git a/cscope.files b/cscope.files new file mode 100644 index 00000000..c59657ed --- /dev/null +++ b/cscope.files @@ -0,0 +1,10 @@ +-q +elaborate.cxx +elaborate.h +main.cxx +parse.cxx +parse.h +staptree.cxx +staptree.h +translate.cxx +translate.h diff --git a/elaborate.cxx b/elaborate.cxx new file mode 100644 index 00000000..0f308c68 --- /dev/null +++ b/elaborate.cxx @@ -0,0 +1,1019 @@ +// elaboration functions +// Copyright 2005 Red Hat Inc. +// GPL + + +#include "config.h" +#include "elaborate.h" +#include "parse.h" + +extern "C" { +#include <sys/utsname.h> +} + +#include <fstream> +#include <algorithm> + +#if 0 +#ifdef HAVE_ELFUTILS_LIBDW_H +#include <elfutils/libdw.h> +#else +#error "need <elfutils/libdw.h>" +#endif +#endif + +using namespace std; + + +// ------------------------------------------------------------------------ + + +derived_probe::derived_probe (probe *p): + base (p) +{ + this->locations = p->locations; + this->tok = p->tok; + this->body = p->body; + this->locals = p->locals; +} + + +derived_probe::derived_probe (probe *p, probe_point *l): + base (p) +{ + this->locations.push_back (l); + this->tok = p->tok; + this->body = p->body; + this->locals = p->locals; +} + + +// ------------------------------------------------------------------------ + + +static int semantic_pass_symbols (systemtap_session&); +static int semantic_pass_types (systemtap_session&); + + + +// Link up symbols to their declarations. Set the session's +// files/probes/functions/globals vectors from the transitively +// reached set of stapfiles in s.library_files, starting from +// s.user_file. Perform automatic tapset inclusion and XXX: probe +// alias expansion. +static int +semantic_pass_symbols (systemtap_session& s) +{ + symresolution_info sym (s); + + // NB: s.files can grow during this iteration, so size() can + // return gradually increasing numbers. + s.files.push_back (s.user_file); + for (unsigned i = 0; i < s.files.size(); i++) + { + stapfile* dome = s.files[i]; + + // Pass 1: add globals and functions to systemtap-session master list, + // so the find_* functions find them + + for (unsigned i=0; i<dome->globals.size(); i++) + s.globals.push_back (dome->globals[i]); + + for (unsigned i=0; i<dome->functions.size(); i++) + s.functions.push_back (dome->functions[i]); + + // Pass 2: process functions + + for (unsigned i=0; i<dome->functions.size(); i++) + { + functiondecl* fd = dome->functions[i]; + + try + { + sym.current_function = fd; + sym.current_probe = 0; + fd->body->visit (& sym); + } + catch (const semantic_error& e) + { + s.print_error (e); + } + } + + // Pass 3: process probes + + for (unsigned i=0; i<dome->probes.size(); i++) + { + probe* p = dome->probes [i]; + vector<derived_probe*> dps; + + try + { + // much magic happens here: probe alias expansion, + // provider identification + sym.derive_probes (p, dps); + } + catch (const semantic_error& e) + { + s.print_error (e); + // dps.erase (dps.begin(), dps.end()); + } + + for (unsigned j=0; j<dps.size(); j++) + { + derived_probe* dp = dps[j]; + s.probes.push_back (dp); + + try + { + sym.current_function = 0; + sym.current_probe = dp; + dp->body->visit (& sym); + } + catch (const semantic_error& e) + { + s.print_error (e); + } + } + } + } + + return s.num_errors; // all those print_error calls +} + + + +int +semantic_pass (systemtap_session& s) +{ + int rc = semantic_pass_symbols (s); + if (rc == 0) rc = semantic_pass_types (s); + return rc; +} + + +// ------------------------------------------------------------------------ + + +systemtap_session::systemtap_session (): + user_file (0), op (0), up (0), num_errors (0) +{ +} + + +void +systemtap_session::print_error (const semantic_error& e) +{ + cerr << "semantic error: " << e.what () << ": "; + if (e.tok1) cerr << *e.tok1; + cerr << e.msg2; + if (e.tok2) cerr << *e.tok2; + cerr << endl; + num_errors ++; +} + + +// ------------------------------------------------------------------------ +// semantic processing: symbol resolution + + +symresolution_info::symresolution_info (systemtap_session& s): + session (s), current_function (0), current_probe (0) +{ +} + + +void +symresolution_info::visit_block (block* e) +{ + for (unsigned i=0; i<e->statements.size(); i++) + { + try + { + e->statements[i]->visit (this); + } + catch (const semantic_error& e) + { + session.print_error (e); + } + } +} + + +void +symresolution_info::visit_symbol (symbol* e) +{ + if (e->referent) + return; + + vardecl* d = find_scalar (e->name); + if (d) + e->referent = d; + else + { + // new local + vardecl* v = new vardecl; + v->name = e->name; + v->tok = e->tok; + if (current_function) + current_function->locals.push_back (v); + else if (current_probe) + current_probe->locals.push_back (v); + else + throw semantic_error ("no current probe/function for unresolved scalar", e->tok); + e->referent = v; + } +} + + +void +symresolution_info::visit_arrayindex (arrayindex* e) +{ + for (unsigned i=0; i<e->indexes.size(); i++) + e->indexes[i]->visit (this); + + if (e->referent) + return; + + vardecl* d = find_array (e->base, e->indexes.size ()); + if (d) + e->referent = d; + else + throw semantic_error ("unresolved global array", e->tok); +} + + +void +symresolution_info::visit_functioncall (functioncall* e) +{ + for (unsigned i=0; i<e->args.size(); i++) + e->args[i]->visit (this); + + if (e->referent) + return; + + functiondecl* d = find_function (e->function, e->args.size ()); + if (d) + e->referent = d; + else + throw semantic_error ("unresolved function call", e->tok); +} + + +vardecl* +symresolution_info::find_scalar (const string& name) +{ + // search locals + vector<vardecl*>& locals = (current_function ? + current_function->locals : + current_probe->locals); + for (unsigned i=0; i<locals.size(); i++) + if (locals[i]->name == name) + // NB: no need to check arity here: locals always scalar + return locals[i]; + + // search function formal parameters (if any) + if (current_function) + for (unsigned i=0; i<current_function->formal_args.size(); i++) + if (current_function->formal_args[i]->name == name) + // NB: no need to check arity here: formal args always scalar + return current_function->formal_args[i]; + + // search globals + for (unsigned i=0; i<session.globals.size(); i++) + if (session.globals[i]->name == name) + if (session.globals[i]->arity <= 0) + { + session.globals[i]->set_arity (0); + return session.globals[i]; + } + + // search library globals + for (unsigned i=0; i<session.library_files.size(); i++) + { + stapfile* f = session.library_files[i]; + for (unsigned j=0; j<f->globals.size(); j++) + if (f->globals[j]->name == name && + f->globals[j]->index_types.size() == 0) + { + // put library into the queue if not already there + if (0) // (session.verbose_resolution) + cerr << " scalar " << name << " " + << "is defined from " << f->name << endl; + + if (find (session.files.begin(), session.files.end(), f) + == session.files.end()) + session.files.push_back (f); + // else .. print different message? + + return f->globals[j]; + } + } + + // search builtins that become locals + // XXX: need to invent a proper formalism for this + if (name == "$pid" || name == "$tid") + { + vardecl_builtin* vb = new vardecl_builtin; + vb->name = name; + vb->type = pe_long; + + // XXX: need a better way to synthesize tokens + token* t = new token; + t->type = tok_identifier; + t->content = name; + t->location.file = "<builtin>"; + vb->tok = t; + + locals.push_back (vb); + return vb; + } + + return 0; + // XXX: add checking for conflicting array or function +} + + +vardecl* +symresolution_info::find_array (const string& name, unsigned arity) +{ + // search processed globals + for (unsigned i=0; i<session.globals.size(); i++) + if (session.globals[i]->name == name) + if ((session.globals[i]->arity == (int) arity) || + session.globals[i]->arity < 0) + { + session.globals[i]->set_arity (arity); + return session.globals[i]; + } + + // search library globals + for (unsigned i=0; i<session.library_files.size(); i++) + { + stapfile* f = session.library_files[i]; + for (unsigned j=0; j<f->globals.size(); j++) + if (f->globals[j]->name == name && + f->globals[j]->index_types.size() == arity) + { + // put library into the queue if not already there + if (0) // (session.verbose_resolution) + cerr << " array " << name << " " + << "is defined from " << f->name << endl; + + if (find (session.files.begin(), session.files.end(), f) + == session.files.end()) + session.files.push_back (f); + // else .. print different message? + + return f->globals[j]; + } + } + + return 0; + // XXX: add checking for conflicting scalar or function +} + + +functiondecl* +symresolution_info::find_function (const string& name, unsigned arity) +{ + for (unsigned j = 0; j < session.functions.size(); j++) + { + functiondecl* fd = session.functions[j]; + if (fd->name == name && + fd->formal_args.size() == arity) + return fd; + } + + // search library globals + for (unsigned i=0; i<session.library_files.size(); i++) + { + stapfile* f = session.library_files[i]; + for (unsigned j=0; j<f->functions.size(); j++) + if (f->functions[j]->name == name && + f->functions[j]->formal_args.size() == arity) + { + // put library into the queue if not already there + if (0) // session.verbose_resolution + cerr << " function " << name << " " + << "is defined from " << f->name << endl; + + if (find (session.files.begin(), session.files.end(), f) + == session.files.end()) + session.files.push_back (f); + // else .. print different message? + + return f->functions[j]; + } + } + + return 0; + // XXX: add checking for conflicting variables +} + + +// ------------------------------------------------------------------------ +// type resolution + + +static int +semantic_pass_types (systemtap_session& s) +{ + int rc = 0; + + // next pass: type inference + unsigned iterations = 0; + typeresolution_info ti (s); + + ti.assert_resolvability = false; + // XXX: maybe convert to exception-based error signalling + while (1) + { + iterations ++; + // cerr << "Type resolution, iteration " << iterations << endl; + ti.num_newly_resolved = 0; + ti.num_still_unresolved = 0; + + for (unsigned j=0; j<s.functions.size(); j++) + { + functiondecl* fn = s.functions[j]; + ti.current_function = fn; + ti.t = pe_unknown; + fn->body->visit (& ti); + // NB: we don't have to assert a known type for + // functions here, to permit a "void" function. + // The translator phase will omit the "retvalue". + // + // if (fn->type == pe_unknown) + // ti.unresolved (fn->tok); + } + + for (unsigned j=0; j<s.probes.size(); j++) + { + derived_probe* pn = s.probes[j]; + ti.current_function = 0; + ti.t = pe_unknown; + pn->body->visit (& ti); + } + + for (unsigned j=0; j<s.globals.size(); j++) + { + vardecl* gd = s.globals[j]; + if (gd->type == pe_unknown) + ti.unresolved (gd->tok); + } + + if (ti.num_newly_resolved == 0) // converged + if (ti.num_still_unresolved == 0) + break; // successfully + else if (! ti.assert_resolvability) + ti.assert_resolvability = true; // last pass, with error msgs + else + { // unsuccessful conclusion + rc ++; + break; + } + } + + return rc + s.num_errors; +} + + +void +typeresolution_info::visit_literal_number (literal_number* e) +{ + assert (e->type == pe_long); + if ((t == e->type) || (t == pe_unknown)) + return; + + mismatch (e->tok, e->type, t); +} + + +void +typeresolution_info::visit_literal_string (literal_string* e) +{ + assert (e->type == pe_string); + if ((t == e->type) || (t == pe_unknown)) + return; + + mismatch (e->tok, e->type, t); +} + + +void +typeresolution_info::visit_logical_or_expr (logical_or_expr *e) +{ + visit_binary_expression (e); +} + + +void +typeresolution_info::visit_logical_and_expr (logical_and_expr *e) +{ + visit_binary_expression (e); +} + + +void +typeresolution_info::visit_comparison (comparison *e) +{ + visit_binary_expression (e); +} + + +void +typeresolution_info::visit_concatenation (concatenation *e) +{ + visit_binary_expression (e); +} + + +void +typeresolution_info::visit_exponentiation (exponentiation *e) +{ + visit_binary_expression (e); +} + + +void +typeresolution_info::visit_assignment (assignment *e) +{ + visit_binary_expression (e); +} + + +void +typeresolution_info::visit_binary_expression (binary_expression* e) +{ + if (e->op == "<<<") // stats aggregation + { + exp_type t1 = t; + t = pe_stats; + e->left->visit (this); + t = pe_long; + e->right->visit (this); + if (t1 == pe_stats || t1 == pe_string) + invalid (e->tok, t1); + else if (e->type == pe_unknown) + { + e->type = pe_long; + resolved (e->tok, e->type); + } + } + else if (e->op == ".") // string concatenation + { + exp_type t1 = t; + t = pe_string; + e->left->visit (this); + t = pe_string; + e->right->visit (this); + if (t1 == pe_long || t1 == pe_stats) + mismatch (e->tok, t1, pe_string); + else if (e->type == pe_unknown) + { + e->type = pe_string; + resolved (e->tok, e->type); + } + } + else if (e->op == "==" + || false) // XXX: other comparison operators + { + exp_type t1 = t; + t = pe_unknown; + e->left->visit (this); + t = pe_unknown; + e->right->visit (this); + if (t1 == pe_string || t1 == pe_stats) + mismatch (e->tok, t1, pe_long); + else if (e->type == pe_unknown) + { + e->type = pe_long; + resolved (e->tok, e->type); + } + } + else // general arithmetic operators? + { + // propagate e->type downward + exp_type sub_type = t; + if ((sub_type == pe_unknown) && (e->type != pe_unknown)) + sub_type = e->type; + t = sub_type; + e->left->visit (this); + t = sub_type; + e->right->visit (this); + + if ((sub_type == pe_unknown) && (e->type != pe_unknown)) + ; // already resolved + else if ((sub_type != pe_unknown) && (e->type == pe_unknown)) + { + e->type = sub_type; + resolved (e->tok, e->type); + } + else if ((sub_type == pe_unknown) && (e->left->type != pe_unknown)) + { + e->type = e->left->type; + resolved (e->tok, e->type); + } + else if ((sub_type == pe_unknown) && (e->right->type != pe_unknown)) + { + e->type = e->right->type; + resolved (e->tok, e->type); + } + else if (e->type != sub_type) + mismatch (e->tok, sub_type, e->type); + } +} + + +void +typeresolution_info::visit_pre_crement (pre_crement *e) +{ + visit_unary_expression (e); +} + + +void +typeresolution_info::visit_post_crement (post_crement *e) +{ + visit_unary_expression (e); +} + + +void +typeresolution_info::visit_unary_expression (unary_expression* e) +{ + // all unary operators only work on numerics + exp_type t1 = t; + t = pe_long; + e->operand->visit (this); + + if (t1 == pe_unknown && e->type != pe_unknown) + ; // already resolved + else if (t1 == pe_string || t1 == pe_stats) + mismatch (e->tok, t1, pe_long); + else if (e->type == pe_unknown) + { + e->type = pe_long; + resolved (e->tok, e->type); + } +} + + + +void +typeresolution_info::visit_ternary_expression (ternary_expression* e) +{ + exp_type sub_type = t; + + t = pe_long; + e->cond->visit (this); + + // Match ordinary binary_expression type inference for the true/false + // arms of the ternary expression. + + if (sub_type == pe_unknown && e->type != pe_unknown) + sub_type = e->type; + t = sub_type; + e->truevalue->visit (this); + t = sub_type; + e->falsevalue->visit (this); + + if ((sub_type == pe_unknown) && (e->type != pe_unknown)) + ; // already resolved + else if ((sub_type != pe_unknown) && (e->type == pe_unknown)) + { + e->type = sub_type; + resolved (e->tok, e->type); + } + else if ((sub_type == pe_unknown) && (e->truevalue->type != pe_unknown)) + { + e->type = e->truevalue->type; + resolved (e->tok, e->type); + } + else if ((sub_type == pe_unknown) && (e->falsevalue->type != pe_unknown)) + { + e->type = e->falsevalue->type; + resolved (e->tok, e->type); + } + else if (e->type != sub_type) + mismatch (e->tok, sub_type, e->type); +} + + +template <class Referrer, class Referent> +void resolve_2types (Referrer* referrer, Referent* referent, + typeresolution_info* r, exp_type t) +{ + exp_type& re_type = referrer->type; + const token* re_tok = referrer->tok; + exp_type& te_type = referent->type; + const token* te_tok = referent->tok; + + if (t != pe_unknown && re_type == t && re_type == te_type) + ; // do nothing: all three e->types in agreement + else if (t == pe_unknown && re_type != pe_unknown && re_type == te_type) + ; // do nothing: two known e->types in agreement + else if (re_type != pe_unknown && te_type != pe_unknown && re_type != te_type) + r->mismatch (re_tok, re_type, te_type); + else if (re_type != pe_unknown && t != pe_unknown && re_type != t) + r->mismatch (re_tok, re_type, t); + else if (te_type != pe_unknown && t != pe_unknown && te_type != t) + r->mismatch (te_tok, te_type, t); + else if (re_type == pe_unknown && t != pe_unknown) + { + // propagate from upstream + re_type = t; + r->resolved (re_tok, re_type); + // catch re_type/te_type mismatch later + } + else if (re_type == pe_unknown && te_type != pe_unknown) + { + // propagate from referent + re_type = te_type; + r->resolved (re_tok, re_type); + // catch re_type/t mismatch later + } + else if (re_type != pe_unknown && te_type == pe_unknown) + { + // propagate to referent + te_type = re_type; + r->resolved (te_tok, te_type); + // catch re_type/t mismatch later + } + else + r->unresolved (re_tok); +} + + +void +typeresolution_info::visit_symbol (symbol* e) +{ + assert (e->referent != 0); + + if (e->referent->arity > 0) + unresolved (e->tok); // symbol resolution should not permit this + // XXX: but consider "delete <array>;" and similar constructs + else + resolve_2types (e, e->referent, this, t); +} + + +void +typeresolution_info::visit_arrayindex (arrayindex* e) +{ + assert (e->referent != 0); + + resolve_2types (e, e->referent, this, t); + + // now resolve the array indexes + if (e->referent->index_types.size() == 0) + { + // redesignate referent as array + e->referent->index_types.resize (e->indexes.size()); + for (unsigned i=0; i<e->indexes.size(); i++) + e->referent->index_types[i] = pe_unknown; + // NB: we "fall through" to for loop + } + + if (e->indexes.size() != e->referent->index_types.size()) + unresolved (e->tok); // symbol resolution should prevent this + else for (unsigned i=0; i<e->indexes.size(); i++) + { + expression* ee = e->indexes[i]; + exp_type& ft = e->referent->index_types [i]; + t = ft; + ee->visit (this); + exp_type at = ee->type; + + if ((at == pe_string || at == pe_long) && ft == pe_unknown) + { + // propagate to formal type + ft = at; + resolved (e->referent->tok, ft); + // uses array decl as there is no token for "formal type" + } + if (at == pe_stats) + invalid (ee->tok, at); + if (ft == pe_stats) + invalid (ee->tok, ft); + if (at != pe_unknown && ft != pe_unknown && ft != at) + mismatch (e->tok, at, ft); + if (at == pe_unknown) + unresolved (ee->tok); + } +} + + +void +typeresolution_info::visit_functioncall (functioncall* e) +{ + assert (e->referent != 0); + + resolve_2types (e, e->referent, this, t); + + if (e->type == pe_stats) + invalid (e->tok, e->type); + + // XXX: but what about functions that return no value, + // and are used only as an expression-statement for side effects? + + // now resolve the function parameters + if (e->args.size() != e->referent->formal_args.size()) + unresolved (e->tok); // symbol resolution should prevent this + else for (unsigned i=0; i<e->args.size(); i++) + { + expression* ee = e->args[i]; + exp_type& ft = e->referent->formal_args[i]->type; + const token* fe_tok = e->referent->formal_args[i]->tok; + t = ft; + ee->visit (this); + exp_type at = ee->type; + + if (((at == pe_string) || (at == pe_long)) && ft == pe_unknown) + { + // propagate to formal arg + ft = at; + resolved (e->referent->formal_args[i]->tok, ft); + } + if (at == pe_stats) + invalid (e->tok, at); + if (ft == pe_stats) + invalid (fe_tok, ft); + if (at != pe_unknown && ft != pe_unknown && ft != at) + mismatch (e->tok, at, ft); + if (at == pe_unknown) + unresolved (e->tok); + } +} + + +void +typeresolution_info::visit_block (block* e) +{ + for (unsigned i=0; i<e->statements.size(); i++) + { + try + { + t = pe_unknown; + e->statements[i]->visit (this); + } + catch (const semantic_error& e) + { + session.print_error (e); + } + } +} + + +void +typeresolution_info::visit_if_statement (if_statement* e) +{ + t = pe_long; + e->condition->visit (this); + + t = pe_unknown; + e->thenblock->visit (this); + + if (e->elseblock) + { + t = pe_unknown; + e->elseblock->visit (this); + } +} + + +void +typeresolution_info::visit_for_loop (for_loop* e) +{ + t = pe_unknown; + e->init->visit (this); + t = pe_long; + e->cond->visit (this); + t = pe_unknown; + e->incr->visit (this); + t = pe_unknown; + e->block->visit (this); +} + + +void +typeresolution_info::visit_null_statement (null_statement* e) +{ +} + + +void +typeresolution_info::visit_expr_statement (expr_statement* e) +{ + t = pe_unknown; + e->value->visit (this); +} + + +void +typeresolution_info::visit_delete_statement (delete_statement* e) +{ + // XXX: not yet supported + unresolved (e->tok); +} + + +void +typeresolution_info::visit_array_in (array_in* e) +{ + // XXX: not yet supported + unresolved (e->tok); +} + + +void +typeresolution_info::visit_return_statement (return_statement* e) +{ + // This is like symbol, where the referent is + // the return value of the function. + + // XXX: need control flow semantic checking; until then: + if (current_function == 0) + { + unresolved (e->tok); + return; + } + + exp_type& e_type = current_function->type; + t = current_function->type; + e->value->visit (this); + + if (e_type != pe_unknown && e->value->type != pe_unknown + && e_type != e->value->type) + mismatch (current_function->tok, e_type, e->value->type); + if (e_type == pe_unknown && + (e->value->type == pe_long || e->value->type == pe_string)) + { + // propagate non-statistics from value + e_type = e->value->type; + resolved (current_function->tok, e->value->type); + } + if (e->value->type == pe_stats) + invalid (e->value->tok, e->value->type); +} + + +void +typeresolution_info::unresolved (const token* tok) +{ + num_still_unresolved ++; + + if (assert_resolvability) + { + cerr << "error: unresolved type for "; + if (tok) + cerr << *tok; + else + cerr << "a token"; + cerr << endl; + } +} + + +void +typeresolution_info::invalid (const token* tok, exp_type pe) +{ + num_still_unresolved ++; + + if (assert_resolvability) + { + cerr << "error: invalid type " << pe << " for "; + if (tok) + cerr << *tok; + else + cerr << "a token"; + cerr << endl; + } +} + + +void +typeresolution_info::mismatch (const token* tok, exp_type t1, exp_type t2) +{ + num_still_unresolved ++; + + if (assert_resolvability) + { + cerr << "error: type mismatch for "; + if (tok) + cerr << *tok; + else + cerr << "a token"; + cerr << ": " << t1 << " vs. " << t2 << endl; + } +} + + +void +typeresolution_info::resolved (const token* tok, exp_type t) +{ + num_newly_resolved ++; + // cerr << "resolved " << *e->tok << " type " << t << endl; +} + diff --git a/elaborate.h b/elaborate.h new file mode 100644 index 00000000..8375833a --- /dev/null +++ b/elaborate.h @@ -0,0 +1,161 @@ +// -*- C++ -*- +// Copyright 2005 Red Hat Inc. +// GPL + + +#ifndef ELABORATE_H +#define ELABORATE_H + +#include "staptree.h" +#include "parse.h" +#include <string> +#include <vector> + + +// ------------------------------------------------------------------------ + +struct systemtap_session; +struct derived_probe; +struct symresolution_info: public traversing_visitor +{ +protected: + systemtap_session& session; + +public: + functiondecl* current_function; + derived_probe* current_probe; + symresolution_info (systemtap_session& s); + + // XXX: instead in systemtap_session? + void derive_probes (probe *p, std::vector<derived_probe*>& dps); + +protected: + vardecl* find_scalar (const std::string& name); + vardecl* find_array (const std::string& name, unsigned arity); + functiondecl* find_function (const std::string& name, unsigned arity); + + void visit_block (block *s); + void visit_symbol (symbol* e); + void visit_arrayindex (arrayindex* e); + void visit_functioncall (functioncall* e); +}; + + +struct typeresolution_info: public visitor +{ + typeresolution_info (systemtap_session& s): session (s) {} + systemtap_session& session; + unsigned num_newly_resolved; + unsigned num_still_unresolved; + bool assert_resolvability; + functiondecl* current_function; + + void mismatch (const token* tok, exp_type t1, exp_type t2); + void unresolved (const token* tok); + void resolved (const token* tok, exp_type t); + void invalid (const token* tok, exp_type t); + + exp_type t; // implicit parameter for nested visit call; may clobber + + void visit_block (block *s); + void visit_null_statement (null_statement *s); + void visit_expr_statement (expr_statement *s); + void visit_if_statement (if_statement* s); + void visit_for_loop (for_loop* s); + void visit_return_statement (return_statement* s); + void visit_delete_statement (delete_statement* s); + void visit_literal_string (literal_string* e); + void visit_literal_number (literal_number* e); + void visit_binary_expression (binary_expression* e); + void visit_unary_expression (unary_expression* e); + void visit_pre_crement (pre_crement* e); + void visit_post_crement (post_crement* e); + void visit_logical_or_expr (logical_or_expr* e); + void visit_logical_and_expr (logical_and_expr* e); + void visit_array_in (array_in* e); + void visit_comparison (comparison* e); + void visit_concatenation (concatenation* e); + void visit_exponentiation (exponentiation* e); + void visit_ternary_expression (ternary_expression* e); + void visit_assignment (assignment* e); + void visit_symbol (symbol* e); + void visit_arrayindex (arrayindex* e); + void visit_functioncall (functioncall* e); +}; + + +// ------------------------------------------------------------------------ + + +// A derived_probe is a probe that has been elaborated by +// binding to a matching provider. The locations std::vector +// may be smaller or larger than the base probe, since a +// provider may transform it. + +class translator_output; + +struct derived_probe: public probe +{ + derived_probe (probe* b); + derived_probe (probe* b, probe_point* l); + probe* base; // the original parsed probe + + virtual ~derived_probe () {} + + virtual void emit_registrations (translator_output* o, + unsigned probeidx) = 0; + // (from within module_init): + // rc = ..... ENTRYFN ; + + virtual void emit_deregistrations (translator_output* o, + unsigned probeidx) = 0; + // (from within module_exit): + // rc = ..... ENTRYFN ; + + virtual void emit_probe_entries (translator_output* o, + unsigned probeidx) = 0; + // ... for all probe-points: + // ELABORATE_SPECIFIC_SIGNATURE ENTRYFN { + // /* allocate context */ + // /* copy parameters, initial state into context */ + // probe_NUMBER (context); + // /* deallocate context */ + // } +}; + + + +class unparser; +class translator_output; + +struct systemtap_session +{ + systemtap_session (); + + // parse trees for the various script files + stapfile* user_file; + std::vector<stapfile*> library_files; + + // configuration options + // bool verbose_resolution; + + // 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; + + // unparser data + translator_output* op; + unparser* up; + + unsigned num_errors; + // void print_error (const parse_error& e); + void print_error (const semantic_error& e); +}; + + +int semantic_pass (systemtap_session& s); + + +#endif // ELABORATE_H diff --git a/main.cxx b/main.cxx new file mode 100644 index 00000000..94026001 --- /dev/null +++ b/main.cxx @@ -0,0 +1,196 @@ +// systemtap translator driver +// Copyright 2005 Red Hat Inc. +// GPL + +#include "config.h" +#include "staptree.h" +#include "parse.h" +#include "elaborate.h" +#include "translate.h" + +#include <iostream> +#include <fstream> +#include <sstream> + +extern "C" { +#include <glob.h> +#include <unistd.h> +} + +using namespace std; + + + +void usage () +{ + cerr << "Usage: stap [options] FILE [ARGS ...] \tRun script files." << endl; + cerr << " or: stap [options] -e PROGRAM [ARGS ...]\tRun script." << endl; + cerr << endl; + cerr << "Arguments:" << endl; + cerr << " --\tNo more options after this" << endl; + cerr << " -p NUM\tStop after pass NUM 1-3" << endl; + cerr << " \t(parse, elaborate, translate)" << endl; + cerr << " -I DIR\tLook in DIR for additional .stp script files." << endl; + cerr << " -o FILE\tSend translator output to file instead of stdout." << endl; + // XXX: other options: + // -s: safe mode + // -d: dump safety-related external references + + exit (0); +} + + +int +main (int argc, char * const argv []) +{ + int last_pass = 3; // -p NUM + string cmdline_script; // -e PROGRAM + string script_file; // FILE + bool have_script = false; + string output_file; // -o FILE + vector<string> include_path; // -I DIR + vector<string> args; // ARGS + while (true) + { + int grc = getopt (argc, argv, "p:I:e:o:"); + if (grc < 0) + break; + switch (grc) + { + case 'p': + last_pass = atoi (optarg); + if (last_pass < 1 || last_pass > 3) + { + cerr << "Invalid pass number." << endl; + usage (); + } + break; + + case 'I': + include_path.push_back (string (optarg)); + break; + + case 'e': + if (have_script) + usage (); + cmdline_script = string (optarg); + have_script = true; + break; + + case 'o': + if (output_file != "") + usage (); + output_file = string (optarg); + break; + + case '?': + case 'h': + default: + usage (); + } + } + + for (int i = optind; i < argc; i++) + { + if (! have_script) + { + script_file = string (argv[i]); + have_script = true; + } + else + args.push_back (string (argv[i])); + } + + // need a user file + if (! have_script) + usage(); + + // arguments parsed; get down to business + + int rc = 0; + systemtap_session s; + if (output_file != "") + s.op = new translator_output (output_file); + else + s.op = new translator_output (cout); + + + // PASS 1a: PARSING USER SCRIPT + // XXX: pass args vector, so parser (or lexer?) can substitute + // $1..$NN with actual arguments + if (script_file != "") + s.user_file = parser::parse (script_file); + else + { + istringstream ii (cmdline_script); + s.user_file = parser::parse (ii); + } + if (s.user_file == 0) + // syntax errors already printed + rc ++; + + // PASS 1b: PARSING LIBRARY SCRIPTS + for (unsigned i=0; i<include_path.size(); i++) + { + glob_t globbuf; + string dir = include_path[i] + "/*.stp"; + int r = glob(dir.c_str (), 0, NULL, & globbuf); + if (r == GLOB_NOSPACE || r == GLOB_ABORTED) + rc ++; + // GLOB_NOMATCH is acceptable + + for (unsigned j=0; j<globbuf.gl_pathc; j++) + { + stapfile* f = parser::parse (globbuf.gl_pathv[j]); + if (f == 0) + rc ++; + else + s.library_files.push_back (f); + } + + globfree (& globbuf); + } + + if (last_pass == 1 && rc == 0) + { + s.op->line() << "# parse tree dump"; + s.user_file->print (s.op->newline()); + for (unsigned i=0; i<s.library_files.size(); i++) + s.library_files[i]->print (s.op->newline()); + } + + // PASS 2: ELABORATION + if (rc == 0 && last_pass > 1) + rc = semantic_pass (s); + if (last_pass == 2 && rc == 0) + { + s.op->line() << "# globals"; + for (unsigned i=0; i<s.globals.size(); i++) + { + vardecl* v = s.globals[i]; + v->printsig (s.op->newline()); + } + + s.op->newline() << "# functions"; + for (unsigned i=0; i<s.functions.size(); i++) + { + functiondecl* f = s.functions[i]; + f->printsig (s.op->newline()); + } + + s.op->newline() << "# probes"; + for (unsigned i=0; i<s.probes.size(); i++) + { + derived_probe* p = s.probes[i]; + p->printsig (s.op->newline()); + } + } + + // PASS 3: TRANSLATION + if (rc == 0 && last_pass > 2) + rc = translate_pass (s); + + delete s.op; + + return rc; +} @@ -2,12 +2,13 @@ // Copyright 2005 Red Hat Inc. // GPL -#include <iostream> +#include "config.h" #include "staptree.h" #include "parse.h" +#include <iostream> +#include <fstream> #include <cctype> #include <cstdlib> -#include <fstream> #include <cerrno> #include <climits> @@ -405,28 +406,38 @@ parser::parse_stmt_block () throw parse_error ("expected '{'"); pb->tok = t; + while (1) { try { - // handle empty blocks - t = peek (); - if (t && t->type == tok_operator && t->content == "}") - { - next (); - break; - } - + // handle empty blocks + t = peek (); + if (t && t->type == tok_operator && t->content == "}") + { + next (); + break; + } + pb->statements.push_back (parse_statement ()); // ';' is a statement separator in awk, not a terminator. // Note that ';' is also a possible null statement. t = peek (); - if (t && t->type == tok_operator && t->content == ";") + if (t && t->type == tok_operator && t->content == "}") + { + next (); + break; + } + else if (t && t->type == tok_operator && t->content == ";") { next (); continue; + // this also accepts semicolon as a terminator: + // { a=1; } } + else + throw parse_error ("expected ';' or '}'"); } catch (parse_error& pe) { @@ -496,10 +507,18 @@ parser::parse_global (vector <vardecl*>& globals) if (! (t->type == tok_identifier)) throw parse_error ("expected identifier"); - vardecl* d = new vardecl; - d->name = t->content; - d->tok = t; - globals.push_back (d); // XXX: check for duplicates + bool dupe = false; + for (unsigned i=0; i<globals.size(); i++) + if (globals[i]->name == t->content) + dupe = true; + + if (! dupe) + { + vardecl* d = new vardecl; + d->name = t->content; + d->tok = t; + globals.push_back (d); + } t = peek (); if (t && t->type == tok_operator && t->content == ",") @@ -565,6 +584,8 @@ parser::parse_probe_point () { probe_point* pl = new probe_point; + // XXX: add support for probe point aliases + // e.g. probe a.b = a.c = a.d = foo while (1) { const token* t = next (); @@ -596,13 +617,16 @@ parser::parse_probe_point () if (t && t->type == tok_operator && (t->content == "{" || t->content == ",")) break; + else if (t && t->type == tok_operator && + t->content == "(") + throw parse_error ("unexpected '.' or ',' or '{'"); } // fall through if (t && t->type == tok_operator && t->content == ".") next (); else - throw parse_error ("expected '.'"); + throw parse_error ("expected '.' or ',' or '(' or '{'"); } return pl; @@ -717,8 +741,7 @@ parser::parse_assignment () t->content == "+=" || false)) // XXX: add /= etc. { - if (op1->is_lvalue () == 0) - throw parse_error ("assignment not to lvalue"); + // NB: lvalueness is checked during translation / elaboration assignment* e = new assignment; e->left = op1; e->op = t->content; @@ -2,6 +2,9 @@ // Copyright 2005 Red Hat Inc. // GPL +#ifndef PARSE_H +#define PARSE_H + #include <string> #include <fstream> #include <iostream> @@ -20,6 +23,7 @@ struct source_loc enum token_type { tok_junk, tok_identifier, tok_operator, tok_string, tok_number + // XXX: add tok_keyword throughout }; @@ -87,7 +91,7 @@ private: // nonterminals probe* parse_probe (); probe_point* parse_probe_point (); literal* parse_literal (); - void parse_global (vector<vardecl*>&); + void parse_global (std::vector<vardecl*>&); functiondecl* parse_functiondecl (); block* parse_stmt_block (); statement* parse_statement (); @@ -111,3 +115,7 @@ private: // nonterminals expression* parse_symbol (); symbol* parse_symbol_plain (); }; + + + +#endif // PARSE_H diff --git a/parsetest.cxx b/parsetest.cxx deleted file mode 100644 index 6293a2f5..00000000 --- a/parsetest.cxx +++ /dev/null @@ -1,38 +0,0 @@ -// toy driver -// Copyright 2005 Red Hat Inc. -// GPL - - -#include "staptree.h" -#include "parse.h" -#include <iostream> - - -int main (int argc, char *argv []) -{ - int rc = 0; - - if (argc > 1) - { - for (int i = 1; i < argc; i ++) - { - parser p (argv[i]); - stapfile* f = p.parse (); - if (f) - f->print (cout); - else - rc = 1; - } - } - else - { - parser p (cin); - stapfile* f = p.parse (); - if (f) - f->print (cout); - else - rc = 1; - } - - return rc; -} diff --git a/semtest.cxx b/semtest.cxx deleted file mode 100644 index 09cd7576..00000000 --- a/semtest.cxx +++ /dev/null @@ -1,182 +0,0 @@ -// semantic analysis pass, beginnings of elaboration -// Copyright 2005 Red Hat Inc. -// GPL - -#include "staptree.h" -#include "parse.h" -#include <iostream> - - -int -semantic_pass_1 (vector<stapfile*>& files) -{ - int rc = 0; - - // link up symbols to their declarations - for (unsigned i=0; i<files.size(); i++) - { - stapfile* f = files[i]; - - // ... on functions - for (unsigned j=0; j<f->functions.size(); j++) - { - functiondecl* fn = f->functions[j]; - symresolution_info ri (fn->locals, f->globals, f->functions, fn); - - fn->body->resolve_symbols (ri); - if (ri.num_unresolved) - rc ++; - } - - // ... and on probes - for (unsigned j=0; j<f->probes.size(); j++) - { - probe* pn = f->probes[j]; - symresolution_info ri (pn->locals, f->globals, f->functions); - - pn->body->resolve_symbols (ri); - if (ri.num_unresolved) - rc ++; - } - } - - return rc; -} - - -int -semantic_pass_2 (vector<stapfile*>& files) -{ - int rc = 0; - - // next pass: type inference - unsigned iterations = 0; - typeresolution_info ti; - - ti.assert_resolvability = false; - while (1) - { - iterations ++; - // cerr << "Type resolution, iteration " << iterations << endl; - ti.num_newly_resolved = 0; - ti.num_still_unresolved = 0; - - for (unsigned i=0; i<files.size(); i++) - { - stapfile* f = files[i]; - - for (unsigned j=0; j<f->functions.size(); j++) - { - functiondecl* fn = f->functions[j]; - ti.current_function = fn; - fn->body->resolve_types (ti); - if (fn->type == pe_unknown) - ti.unresolved (fn->tok); - } - - for (unsigned j=0; j<f->probes.size(); j++) - { - probe* pn = f->probes[j]; - ti.current_function = 0; - pn->body->resolve_types (ti); - } - - for (unsigned j=0; j<f->globals.size(); j++) - { - vardecl* gd = f->globals[j]; - if (gd->type == pe_unknown) - ti.unresolved (gd->tok); - } - } - - if (ti.num_newly_resolved == 0) // converged - if (ti.num_still_unresolved == 0) - break; // successfully - else if (! ti.assert_resolvability) - ti.assert_resolvability = true; // last pass, with error msgs - else - { // unsuccessful conclusion - rc ++; - break; - } - } - - return rc; -} - - -int -main (int argc, char *argv []) -{ - int rc = 0; - - vector<stapfile*> files; - if (argc == 1) - { - stapfile* f = parser::parse (cin); - if (f) - files.push_back (f); - else - rc ++; - } - else for (int i = 1; i < argc; i ++) - { - stapfile* f = parser::parse (argv[i]); - if (f) - files.push_back (f); - else - rc ++; - } - - rc += semantic_pass_1 (files); - if (rc == 0) - rc += semantic_pass_2 (files); - - for (unsigned i=0; i<files.size(); i++) - { - stapfile* f = files[i]; - for (unsigned j=0; j<f->functions.size(); j++) - { - functiondecl* fn = f->functions[j]; - cerr << "Function "; - fn->printsig (cerr); - cerr << endl << "locals:" << endl; - for (unsigned k=0; k<fn->locals.size(); k++) - { - vardecl* fa = fn->locals[k]; - cerr << "\t"; - fa->printsig (cerr); - cerr << endl; - } - cerr << endl; - } - - for (unsigned j=0; j<f->probes.size(); j++) - { - probe* pn = f->probes[j]; - cerr << "Probe "; - pn->printsig (cerr); - cerr << "locals:" << endl; - for (unsigned k=0; k<pn->locals.size(); k++) - { - vardecl* fa = pn->locals[k]; - cerr << "\t"; - fa->printsig (cerr); - cerr << endl; - } - cerr << endl; - } - - cerr << "globals:" << endl; - for (unsigned k=0; k<f->globals.size(); k++) - { - vardecl* fa = f->globals[k]; - cerr << "\t"; - fa->printsig (cerr); - cerr << endl; - } - cerr << endl; - } - - return rc; -} diff --git a/staptree.cxx b/staptree.cxx index ab2ebb97..3c4fe435 100644 --- a/staptree.cxx +++ b/staptree.cxx @@ -2,12 +2,15 @@ // Copyright 2005 Red Hat Inc. // GPL +#include "config.h" #include "staptree.h" #include "parse.h" #include <iostream> #include <typeinfo> #include <cassert> +using namespace std; + expression::expression (): type (pe_unknown), tok (0) @@ -79,15 +82,19 @@ probe_point::component::component (): } -vardecl::vardecl () +vardecl::vardecl (): + arity (-1) { } -vardecl::vardecl (unsigned arity) +void +vardecl::set_arity (int a) { + assert (a >= 0); + arity = a; index_types.resize (arity); - for (unsigned i=0; i<arity; i++) + for (int i=0; i<arity; i++) index_types[i] = pe_unknown; } @@ -139,6 +146,7 @@ ostream& operator << (ostream& o, expression& k) void literal_string::print (ostream& o) { + // XXX: quote special chars o << '"' << value << '"'; } @@ -362,681 +370,6 @@ ostream& operator << (ostream& o, symboldecl& k) } -// ------------------------------------------------------------------------ -// semantic processing: symbol resolution - - -symresolution_info::symresolution_info (vector<vardecl*>& l, - vector<vardecl*>& g, - vector<functiondecl*>& f): - locals (l), globals (g), functions (f), current_function (0) -{ - num_unresolved = 0; -} - - -symresolution_info::symresolution_info (vector<vardecl*>& l, - vector<vardecl*>& g, - vector<functiondecl*>& f, - functiondecl* cf): - locals (l), globals (g), functions (f), current_function (cf) -{ - num_unresolved = 0; -} - - -void -literal::resolve_symbols (symresolution_info& r) -{ -} - - -void -binary_expression::resolve_symbols (symresolution_info& r) -{ - left->resolve_symbols (r); - right->resolve_symbols (r); -} - - -void -unary_expression::resolve_symbols (symresolution_info& r) -{ - operand->resolve_symbols (r); -} - - -void -ternary_expression::resolve_symbols (symresolution_info& r) -{ - cond->resolve_symbols (r); - truevalue->resolve_symbols (r); - falsevalue->resolve_symbols (r); -} - - -void -symbol::resolve_symbols (symresolution_info& r) -{ - if (referent) - return; - - vardecl* d = r.find_scalar (name); - if (d) - referent = d; - else - { - // new local - vardecl* v = new vardecl; - v->name = name; - v->tok = tok; - r.locals.push_back (v); - referent = v; - } -} - - -void -arrayindex::resolve_symbols (symresolution_info& r) -{ - for (unsigned i=0; i<indexes.size(); i++) - indexes[i]->resolve_symbols (r); - - if (referent) - return; - - vardecl* d = r.find_array (base, indexes); - if (d) - referent = d; - else - { - // new local - vardecl* v = new vardecl (indexes.size()); - v->name = base; - v->tok = tok; - r.locals.push_back (v); - referent = v; - } -} - - -void -functioncall::resolve_symbols (symresolution_info& r) -{ - for (unsigned i=0; i<args.size(); i++) - args[i]->resolve_symbols (r); - - if (referent) - return; - - functiondecl* d = r.find_function (function, args); - if (d) - referent = d; - else - r.unresolved (tok); -} - - -void -block::resolve_symbols (symresolution_info& r) -{ - for (unsigned i=0; i<statements.size(); i++) - statements[i]->resolve_symbols (r); -} - - -void -if_statement::resolve_symbols (symresolution_info& r) -{ - condition->resolve_symbols (r); - thenblock->resolve_symbols (r); - elseblock->resolve_symbols (r); -} - - -void -for_loop::resolve_symbols (symresolution_info& r) -{ - init->resolve_symbols (r); - cond->resolve_symbols (r); - incr->resolve_symbols (r); - block->resolve_symbols (r); -} - - -void -expr_statement::resolve_symbols (symresolution_info& r) -{ - value->resolve_symbols (r); -} - - -vardecl* -symresolution_info::find_scalar (const string& name) -{ - // search locals - for (unsigned i=0; i<locals.size(); i++) - if (locals[i]->name == name) - return locals[i]; - - // search builtins that become locals - // XXX: need to invent a proper formalism for this - if (name == "$pid" || name == "$tid") - { - vardecl_builtin* vb = new vardecl_builtin; - vb->name = name; - vb->type = pe_long; - - // XXX: need a better way to synthesize tokens - token* t = new token; - t->type = tok_identifier; - t->content = name; - t->location.file = "<builtin>"; - vb->tok = t; - - locals.push_back (vb); - return vb; - } - - // search function formal parameters (if any) - if (current_function) - { - for (unsigned i=0; i<current_function->formal_args.size(); i++) - if (current_function->formal_args [i]->name == name) - return current_function->formal_args [i]; - } - - // search globals - for (unsigned i=0; i<globals.size(); i++) - if (globals[i]->name == name) - return globals[i]; - - return 0; - // XXX: add checking for conflicting array or function -} - - -vardecl* -symresolution_info::find_array (const string& name, - const vector<expression*>& indexes) -{ - // search locals - for (unsigned i=0; i<locals.size(); i++) - if (locals[i]->name == name) - return locals[i]; - - // search function formal parameters (if any) - if (current_function) - { - for (unsigned i=0; i<current_function->formal_args.size(); i++) - if (current_function->formal_args [i]->name == name) - return current_function->formal_args [i]; - } - - // search globals - for (unsigned i=0; i<globals.size(); i++) - if (globals[i]->name == name) - return globals[i]; - - return 0; - // XXX: add checking for conflicting scalar or function -} - - -functiondecl* -symresolution_info::find_function (const string& name, - const vector<expression*>& args) -{ - for (unsigned j = 0; j < functions.size(); j++) - { - functiondecl* fd = functions[j]; - if (fd->name == name) - return fd; - } - - return 0; - // XXX: add checking for conflicting variables -} - - - -void -symresolution_info::unresolved (const token* tok) -{ - num_unresolved ++; - - cerr << "error: unresolved symbol for "; - if (tok) - cerr << *tok; - else - cerr << "a token"; - cerr << endl; -} - - -// ------------------------------------------------------------------------ -// semantic processing: type resolution - - -void -literal::resolve_types (typeresolution_info& r, exp_type t) -{ - assert (type == pe_long || type == pe_string); - if ((t == type) || (t == pe_unknown)) - return; - - r.mismatch (tok, type, t); -} - - -void -binary_expression::resolve_types (typeresolution_info& r, exp_type t) -{ - if (op == "<<<") // stats aggregation - { - left->resolve_types (r, pe_stats); - right->resolve_types (r, pe_long); - if (t == pe_stats || t == pe_string) - r.mismatch (tok, t, pe_long); - else if (type == pe_unknown) - { - type = pe_long; - r.resolved (tok, type); - } - } - else if (op == ".") // string concatenation - { - left->resolve_types (r, pe_string); - right->resolve_types (r, pe_string); - if (t == pe_long || t == pe_stats) - r.mismatch (tok, t, pe_string); - else if (type == pe_unknown) - { - type = pe_string; - r.resolved (tok, type); - } - } - else if (op == "==" - || op == "in" // XXX: really a unary operator - || false) // XXX: other comparison operators - { - left->resolve_types (r, pe_unknown); - right->resolve_types (r, pe_unknown); - if (t == pe_string || t == pe_stats) - r.mismatch (tok, t, pe_long); - else if (type == pe_unknown) - { - type = pe_long; - r.resolved (tok, type); - } - } - else // general arithmetic operators? - { - // propagate type downward - exp_type subtype = t; - if ((t == pe_unknown) && (type != pe_unknown)) - subtype = type; - left->resolve_types (r, subtype); - right->resolve_types (r, subtype); - - if ((t == pe_unknown) && (type != pe_unknown)) - ; // already resolved - else if ((t != pe_unknown) && (type == pe_unknown)) - { - type = t; - r.resolved (tok, type); - } - else if ((t == pe_unknown) && (left->type != pe_unknown)) - { - type = left->type; - r.resolved (tok, type); - } - else if ((t == pe_unknown) && (right->type != pe_unknown)) - { - type = right->type; - r.resolved (tok, type); - } - else if (type != t) - r.mismatch (tok, t, type); - } -} - - -void -unary_expression::resolve_types (typeresolution_info& r, exp_type t) -{ - // all unary operators only work on numerics - - operand->resolve_types (r, pe_long); - - if (t == pe_unknown && type != pe_unknown) - ; // already resolved - else if (t == pe_string || t == pe_stats) - r.mismatch (tok, t, pe_long); - else if (type == pe_unknown) - { - type = pe_long; - r.resolved (tok, type); - } -} - - -void -ternary_expression::resolve_types (typeresolution_info& r, exp_type t) -{ - cond->resolve_types (r, pe_long); - truevalue->resolve_types (r, t); - falsevalue->resolve_types (r, t); -} - - -template <class Referrer, class Referent> -void resolve_2types (Referrer* referrer, Referent* referent, - typeresolution_info& r, exp_type t) -{ - exp_type& rtype = referrer->type; - const token* rtok = referrer->tok; - exp_type& ttype = referent->type; - const token* ttok = referent->tok; - - if (t != pe_unknown && rtype == t && rtype == ttype) - ; // do nothing: all three types in agreement - else if (t == pe_unknown && rtype != pe_unknown && rtype == ttype) - ; // do nothing: two known types in agreement - else if (rtype != pe_unknown && ttype != pe_unknown && rtype != ttype) - r.mismatch (rtok, rtype, ttype); - else if (rtype != pe_unknown && t != pe_unknown && rtype != t) - r.mismatch (rtok, rtype, t); - else if (ttype != pe_unknown && t != pe_unknown && ttype != t) - r.mismatch (ttok, ttype, t); - else if (rtype == pe_unknown && t != pe_unknown) - { - // propagate from upstream - rtype = t; - r.resolved (rtok, rtype); - // catch rtype/ttype mismatch later - } - else if (rtype == pe_unknown && ttype != pe_unknown) - { - // propagate from referent - rtype = ttype; - r.resolved (rtok, rtype); - // catch rtype/t mismatch later - } - else if (rtype != pe_unknown && ttype == pe_unknown) - { - // propagate to referent - ttype = rtype; - r.resolved (ttok, ttype); - // catch rtype/t mismatch later - } - else - r.unresolved (rtok); -} - - -void -symbol::resolve_types (typeresolution_info& r, exp_type t) -{ - assert (referent != 0); - - if (referent->index_types.size() > 0) - r.unresolved (tok); // array - else - resolve_2types (this, referent, r, t); -} - - -void -arrayindex::resolve_types (typeresolution_info& r, exp_type t) -{ - assert (referent != 0); - - resolve_2types (this, referent, r, t); - - // now resolve the array indexes - if (referent->index_types.size() == 0) - { - // designate referent as array - referent->index_types.resize (indexes.size()); - for (unsigned i=0; i<indexes.size(); i++) - referent->index_types[i] = pe_unknown; - // NB: we "fall through" to for loop - } - - if (indexes.size() != referent->index_types.size()) - r.unresolved (tok); - else for (unsigned i=0; i<indexes.size(); i++) - { - expression* e = indexes[i]; - e->resolve_types (r, referent->index_types[i]); - exp_type it = e->type; - referent->index_types[i] = it; - - if (it == pe_string || it == pe_long) - ; // do nothing - else if (it == pe_stats) - r.invalid (e->tok, it); - else // pe_unknown - r.unresolved (e->tok); - } -} - - -void -functioncall::resolve_types (typeresolution_info& r, exp_type t) -{ - assert (referent != 0); - - resolve_2types (this, referent, r, t); - - if (type == pe_stats) - r.mismatch (tok, pe_unknown, type); - - // XXX: but what about functions that return no value, - // and are used only as an expression-statement for side effects? - - // now resolve the function parameters - if (args.size() != referent->formal_args.size()) - r.unresolved (tok); - else for (unsigned i=0; i<args.size(); i++) - { - expression* e = args[i]; - exp_type& ft = referent->formal_args[i]->type; - const token* ftok = referent->formal_args[i]->tok; - e->resolve_types (r, ft); - exp_type at = e->type; - - if (((at == pe_string) || (at == pe_long)) && ft == pe_unknown) - { - // propagate to formal arg - ft = at; - r.resolved (referent->formal_args[i]->tok, ft); - } - if (at == pe_stats) - r.invalid (e->tok, at); - if (ft == pe_stats) - r.invalid (ftok, ft); - if (at != pe_unknown && ft != pe_unknown && ft != at) - r.mismatch (e->tok, at, ft); - if (at == pe_unknown) - r.unresolved (e->tok); - } -} - - -void -block::resolve_types (typeresolution_info& r) -{ - for (unsigned i=0; i<statements.size(); i++) - statements[i]->resolve_types (r); -} - - -void -if_statement::resolve_types (typeresolution_info& r) -{ - condition->resolve_types (r, pe_long); - thenblock->resolve_types (r); - elseblock->resolve_types (r); -} - - -void -for_loop::resolve_types (typeresolution_info& r) -{ - init->resolve_types (r, pe_unknown); - cond->resolve_types (r, pe_long); - incr->resolve_types (r, pe_unknown); - block->resolve_types (r); -} - - -void -expr_statement::resolve_types (typeresolution_info& r) -{ - value->resolve_types (r, pe_unknown); -} - - -void -return_statement::resolve_types (typeresolution_info& r) -{ - // This is like symbol::resolve_types, where the referent is - // the return value of the function. - - // XXX: need control flow semantic checking; until then: - if (r.current_function == 0) - { - r.unresolved (tok); - return; - } - - exp_type& type = r.current_function->type; - value->resolve_types (r, type); - - if (type != pe_unknown && value->type != pe_unknown - && type != value->type) - r.mismatch (r.current_function->tok, type, value->type); - if (type == pe_unknown && - (value->type == pe_long || value->type == pe_string)) - { - // propagate non-statistics from value - type = value->type; - r.resolved (r.current_function->tok, value->type); - } - if (value->type == pe_stats) - r.invalid (value->tok, value->type); -} - - -void -typeresolution_info::unresolved (const token* tok) -{ - num_still_unresolved ++; - - if (assert_resolvability) - { - cerr << "error: unresolved type for "; - if (tok) - cerr << *tok; - else - cerr << "a token"; - cerr << endl; - } -} - - -void -typeresolution_info::invalid (const token* tok, exp_type pe) -{ - num_still_unresolved ++; - - if (assert_resolvability) - { - cerr << "error: invalid type " << pe << " for "; - if (tok) - cerr << *tok; - else - cerr << "a token"; - cerr << endl; - } -} - - -void -typeresolution_info::mismatch (const token* tok, exp_type t1, exp_type t2) -{ - num_still_unresolved ++; - - if (assert_resolvability) - { - cerr << "error: type mismatch for "; - if (tok) - cerr << *tok; - else - cerr << "a token"; - cerr << ": " << t1 << " vs. " << t2 << endl; - } -} - - -void -typeresolution_info::resolved (const token* tok, exp_type t) -{ - num_newly_resolved ++; - // cerr << "resolved " << *tok << " type " << t << endl; -} - - -// ------------------------------------------------------------------------ -// semantic processing: lvalue checking: XXX: unneeded? - - -bool -assignment::is_lvalue () -{ - return left->is_lvalue (); -} - - -// ------------------------------------------------------------------------ -// unparser - - -translator_output::translator_output (ostream& f): - o (f), tablevel (0) -{ -} - - -ostream& -translator_output::newline (int indent) -{ - assert (indent > 0 || tablevel >= (unsigned)-indent); - tablevel += indent; - o << endl; - for (unsigned i=0; i<tablevel; i++) - o << " "; - return o; -} - - -void -translator_output::indent (int indent) -{ - assert (indent > 0 || tablevel >= (unsigned)-indent); - tablevel += indent; -} - - -ostream& -translator_output::line () -{ - return o; -} - - // ------------------------------------------------------------------------ // visitors @@ -1212,7 +545,8 @@ traversing_visitor::visit_if_statement (if_statement* s) { s->condition->visit (this); s->thenblock->visit (this); - s->elseblock->visit (this); + if (s->elseblock) + s->elseblock->visit (this); } void @@ -7,28 +7,24 @@ #include <string> #include <vector> -#include <map> #include <iostream> #include <stdexcept> -using namespace std; - - struct token; // parse.h struct semantic_error: public std::runtime_error { const token* tok1; - const string msg2; + const std::string msg2; const token* tok2; ~semantic_error () throw () {} - semantic_error (const string& msg): + semantic_error (const std::string& msg): runtime_error (msg), tok1 (0), tok2 (0) {} - semantic_error (const string& msg, const token* t1): + semantic_error (const std::string& msg, const token* t1): runtime_error (msg), tok1 (t1), tok2 (0) {} - semantic_error (const string& msg, const token* t1, - const string& m2, const token* t2): + semantic_error (const std::string& msg, const token* t1, + const std::string& m2, const token* t2): runtime_error (msg), tok1 (t1), msg2 (m2), tok2 (t2) {} }; @@ -42,11 +38,9 @@ enum exp_type pe_stats }; -ostream& operator << (ostream& o, const exp_type& e); +std::ostream& operator << (std::ostream& o, const exp_type& e); struct token; -struct symresolution_info; -struct typeresolution_info; struct visitor; struct expression @@ -55,38 +49,33 @@ struct expression const token* tok; expression (); virtual ~expression (); - virtual void print (ostream& o) = 0; - virtual void resolve_symbols (symresolution_info& r) = 0; - virtual void resolve_types (typeresolution_info& r, exp_type t) = 0; - virtual bool is_lvalue () = 0; // XXX: deprecate + virtual void print (std::ostream& o) = 0; virtual void visit (visitor* u) = 0; }; -ostream& operator << (ostream& o, expression& k); +std::ostream& operator << (std::ostream& o, expression& k); struct literal: public expression { - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return false; } }; struct literal_string: public literal { - string value; - literal_string (const string& v); - void print (ostream& o); + std::string value; + literal_string (const std::string& v); + void print (std::ostream& o); void visit (visitor* u); }; struct literal_number: public literal { + // XXX: s/long/long long/ throughout long value; literal_number (long v); - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); }; @@ -94,25 +83,19 @@ struct literal_number: public literal struct binary_expression: public expression { expression* left; - string op; + std::string op; expression* right; - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return false; } }; struct unary_expression: public expression { - string op; + std::string op; expression* operand; - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return false; } }; @@ -124,7 +107,7 @@ struct pre_crement: public unary_expression struct post_crement: public unary_expression { - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); }; @@ -170,17 +153,13 @@ struct ternary_expression: public expression expression* cond; expression* truevalue; expression* falsevalue; - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return false; } }; struct assignment: public binary_expression { - bool is_lvalue (); void visit (visitor* u); }; @@ -188,112 +167,64 @@ struct assignment: public binary_expression class vardecl; struct symbol: public expression { - string name; + std::string name; vardecl *referent; symbol (); - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return true; } }; struct arrayindex: public expression { - string base; - vector<expression*> indexes; + std::string base; + std::vector<expression*> indexes; vardecl *referent; arrayindex (); - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return true; } }; class functiondecl; struct functioncall: public expression { - string function; - vector<expression*> args; + std::string function; + std::vector<expression*> args; functiondecl *referent; functioncall (); - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r, exp_type t); - bool is_lvalue () { return false; } }; // ------------------------------------------------------------------------ -struct stapfile; -struct symboldecl; -struct symresolution_info -{ - vector<vardecl*>& locals; // includes incoming function parameters - vector<vardecl*>& globals; - vector<functiondecl*>& functions; - functiondecl* current_function; - - symresolution_info (vector<vardecl*>& l, - vector<vardecl*>& g, - vector<functiondecl*>& f, - functiondecl* cfun); - symresolution_info (vector<vardecl*>& l, - vector<vardecl*>& g, - vector<functiondecl*>& f); - - vardecl* find_scalar (const string& name); - vardecl* find_array (const string& name, const vector<expression*>&); - functiondecl* find_function (const string& name, const vector<expression*>&); - - void unresolved (const token* tok); - unsigned num_unresolved; -}; - - -struct typeresolution_info -{ - unsigned num_newly_resolved; - unsigned num_still_unresolved; - bool assert_resolvability; - functiondecl* current_function; - - void mismatch (const token* tok, exp_type t1, exp_type t2); - void unresolved (const token* tok); - void resolved (const token* tok, exp_type t); - void invalid (const token* tok, exp_type t); -}; - - struct symboldecl // unique object per (possibly implicit) // symbol declaration { const token* tok; - string name; + std::string name; exp_type type; symboldecl (); virtual ~symboldecl (); - virtual void print (ostream &o) = 0; - virtual void printsig (ostream &o) = 0; + virtual void print (std::ostream &o) = 0; + virtual void printsig (std::ostream &o) = 0; }; -ostream& operator << (ostream& o, symboldecl& k); +std::ostream& operator << (std::ostream& o, symboldecl& k); struct vardecl: public symboldecl { - void print (ostream& o); - void printsig (ostream& o); + void print (std::ostream& o); + void printsig (std::ostream& o); vardecl (); - vardecl (unsigned arity); - vector<exp_type> index_types; // for arrays only + void set_arity (int arity); + int arity; // -1: unknown; 0: scalar; >0: array + std::vector<exp_type> index_types; // for arrays only }; @@ -305,12 +236,12 @@ struct vardecl_builtin: public vardecl struct block; struct functiondecl: public symboldecl { - vector<vardecl*> formal_args; - vector<vardecl*> locals; + std::vector<vardecl*> formal_args; + std::vector<vardecl*> locals; block* body; functiondecl (); - void print (ostream& o); - void printsig (ostream& o); + void print (std::ostream& o); + void printsig (std::ostream& o); }; @@ -319,25 +250,21 @@ struct functiondecl: public symboldecl struct statement { - virtual void print (ostream& o) = 0; + virtual void print (std::ostream& o) = 0; virtual void visit (visitor* u) = 0; const token* tok; statement (); virtual ~statement (); - virtual void resolve_symbols (symresolution_info& r) = 0; - virtual void resolve_types (typeresolution_info& r) = 0; }; -ostream& operator << (ostream& o, statement& k); +std::ostream& operator << (std::ostream& o, statement& k); struct block: public statement { - vector<statement*> statements; - void print (ostream& o); + std::vector<statement*> statements; + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r); }; struct for_loop: public statement @@ -346,29 +273,23 @@ struct for_loop: public statement expression* cond; expression* incr; statement* block; - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r); }; struct null_statement: public statement { - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r) {} - void resolve_types (typeresolution_info& r) {} }; struct expr_statement: public statement { expression* value; // executed for side-effects - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r); }; @@ -377,24 +298,21 @@ struct if_statement: public statement expression* condition; statement* thenblock; statement* elseblock; - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_symbols (symresolution_info& r); - void resolve_types (typeresolution_info& r); }; struct return_statement: public expr_statement { - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); - void resolve_types (typeresolution_info& r); }; struct delete_statement: public expr_statement { - void print (ostream& o); + void print (std::ostream& o); void visit (visitor* u); }; @@ -402,11 +320,11 @@ struct delete_statement: public expr_statement struct probe; struct stapfile { - string name; - vector<probe*> probes; - vector<functiondecl*> functions; - vector<vardecl*> globals; - void print (ostream& o); + std::string name; + std::vector<probe*> probes; + std::vector<functiondecl*> functions; + std::vector<vardecl*> globals; + void print (std::ostream& o); }; @@ -414,48 +332,33 @@ struct probe_point { struct component // XXX: sort of a restricted functioncall { - string functor; + std::string functor; literal* arg; // optional component (); }; - vector<component*> components; + std::vector<component*> components; + // XXX: probe aliases const token* tok; // points to first component's functor - void print (ostream& o); + void print (std::ostream& o); probe_point (); }; -ostream& operator << (ostream& o, probe_point& k); +std::ostream& operator << (std::ostream& o, probe_point& k); struct probe { - vector<probe_point*> locations; + std::vector<probe_point*> locations; block* body; const token* tok; - vector<vardecl*> locals; + std::vector<vardecl*> locals; probe (); - void print (ostream& o); - void printsig (ostream &o); + void print (std::ostream& o); + void printsig (std::ostream &o); }; -// Output context for systemtap translation, intended to allow -// pretty-printing. -class translator_output -{ - ostream& o; - unsigned tablevel; - -public: - translator_output (ostream& file); - - ostream& newline (int indent = 0); - void indent (int indent = 0); - ostream& line(); -}; - - // An derived visitor instance is used to visit the entire // statement/expression tree. struct visitor diff --git a/testsuite/parseko/five.stp b/testsuite/parseko/five.stp index 1673a1e6..a80b6734 100755 --- a/testsuite/parseko/five.stp +++ b/testsuite/parseko/five.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p1 probe foo( { } diff --git a/testsuite/parseko/four.stp b/testsuite/parseko/four.stp index ad77239b..7bfccd02 100755 --- a/testsuite/parseko/four.stp +++ b/testsuite/parseko/four.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p1 probe foo { somethingawful->foo = 1; diff --git a/testsuite/parseko/one.stp b/testsuite/parseko/one.stp index 149f602e..0388a7e6 100755 --- a/testsuite/parseko/one.stp +++ b/testsuite/parseko/one.stp @@ -1,2 +1,2 @@ -#! parsetest +#! stap -p1 "not a probe" diff --git a/testsuite/parseko/six.stp b/testsuite/parseko/six.stp index 2e8c8dbe..f37cd034 100755 --- a/testsuite/parseko/six.stp +++ b/testsuite/parseko/six.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p1 probe foo { a = -9999999999999999999999999; diff --git a/testsuite/parseko/three.stp b/testsuite/parseko/three.stp index 4db7dd3f..be810e9d 100755 --- a/testsuite/parseko/three.stp +++ b/testsuite/parseko/three.stp @@ -1,5 +1,7 @@ -#! semtest +#! stap -p1 probe foo { - 1 + 2 = 3; # bad lvalue + a(1) +# need ; + b(2) } diff --git a/testsuite/parseko/two.stp b/testsuite/parseko/two.stp index e17024ff..74633df4 100755 --- a/testsuite/parseko/two.stp +++ b/testsuite/parseko/two.stp @@ -1,4 +1,4 @@ -#! parsetest +#! stap -p1 probe foo { a + } diff --git a/testsuite/parseok/five.stp b/testsuite/parseok/five.stp index a475807e..b7690943 100755 --- a/testsuite/parseok/five.stp +++ b/testsuite/parseok/five.stp @@ -1,4 +1,4 @@ -#! parsetest +#! stap -p1 probe lkst("process_contextswitch") {} probe syscall("name").return {} diff --git a/testsuite/parseok/four.stp b/testsuite/parseok/four.stp index d381b242..39c9ebd4 100755 --- a/testsuite/parseok/four.stp +++ b/testsuite/parseok/four.stp @@ -1,4 +1,4 @@ -#! parsetest +#! stap -p1 probe syscall ("foo").foo.bar , syscall ("bar"), syscall ("*").return { diff --git a/testsuite/parseok/one.stp b/testsuite/parseok/one.stp index 5b69767d..dab3b394 100755 --- a/testsuite/parseok/one.stp +++ b/testsuite/parseok/one.stp @@ -1,3 +1,3 @@ -#! parsetest +#! stap -p1 # test function k () { } diff --git a/testsuite/parseok/seven.stp b/testsuite/parseok/seven.stp new file mode 100755 index 00000000..8f692958 --- /dev/null +++ b/testsuite/parseok/seven.stp @@ -0,0 +1,26 @@ +#! stap -p1 + +probe one { if (1) {} } +probe two { if (2) {;} } +probe three { if (3) ; } +probe four { if (4) {} else ; } +probe five { if (5) ; else {} } +probe six { if (6) {} else {;} } +probe seven { ; } +probe eight { {} } +probe nine { {;} } +probe ten { a=1; } + +probe all +{ + if (1) {} ; + if (2) {;} ; + if (3) ; ; + if (4) {} else ; ; + if (5) ; else {} ; + if (6) {} else {;} ; + ; ; + {} ; + {;} ; + a=1 +} diff --git a/testsuite/parseok/six.stp b/testsuite/parseok/six.stp index 6fb9b764..bc16a336 100755 --- a/testsuite/parseok/six.stp +++ b/testsuite/parseok/six.stp @@ -1,4 +1,4 @@ -#! parsetest +#! stap -p1 probe one { diff --git a/testsuite/parseok/three.stp b/testsuite/parseok/three.stp index d2401877..1b31f6d8 100755 --- a/testsuite/parseok/three.stp +++ b/testsuite/parseok/three.stp @@ -1,4 +1,4 @@ -#! parsetest +#! stap -p1 probe syscall ("foo") { diff --git a/testsuite/parseok/two.stp b/testsuite/parseok/two.stp index fdcf335e..d18dd3e3 100755 --- a/testsuite/parseok/two.stp +++ b/testsuite/parseok/two.stp @@ -1,4 +1,4 @@ -#! parsetest +#! stap -p1 probe syscall (231) { diff --git a/testsuite/provok/one.stp b/testsuite/provok/one.stp new file mode 100755 index 00000000..e51ee28a --- /dev/null +++ b/testsuite/provok/one.stp @@ -0,0 +1,26 @@ +#! stap + +global ar1, ar2 + +function string (v) { return "" } # to become built-in + +function search (key) +{ + if (key in ar1) + { ar1[key] ++; return ar2[ar1[key]] } + else + return "no can do" +} + +probe syscall("zamboni") +{ + thread->ar2 = $tgid; + search ($pid) +} + +probe end +{ + for (key in ar2) + # if (key in ar2) + printk ("this: " . string (key) . " was " . string (ar2[key])) +} diff --git a/testsuite/semko/eight.stp b/testsuite/semko/eight.stp index 1bcb344a..7d297295 100755 --- a/testsuite/semko/eight.stp +++ b/testsuite/semko/eight.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 probe foo { stats << "string" # stats only collect numbers diff --git a/testsuite/semko/five.stp b/testsuite/semko/five.stp index 6887fced..ddbbdca5 100755 --- a/testsuite/semko/five.stp +++ b/testsuite/semko/five.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 function bar () { diff --git a/testsuite/semko/four.stp b/testsuite/semko/four.stp index e73cc88d..fd2b3928 100755 --- a/testsuite/semko/four.stp +++ b/testsuite/semko/four.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 global a, b; # types unknown diff --git a/testsuite/semko/nine.stp b/testsuite/semko/nine.stp index 7e864671..f9a704f1 100755 --- a/testsuite/semko/nine.stp +++ b/testsuite/semko/nine.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 probe foo { a[4] = 1; diff --git a/testsuite/semko/one.stp b/testsuite/semko/one.stp index 7519dfc6..324dee7c 100755 --- a/testsuite/semko/one.stp +++ b/testsuite/semko/one.stp @@ -1,8 +1,8 @@ -#! semtest +#! stap -p2 function stamp (syscall) { # no return expression => unknown function type } -probe syscall (read) { stamp ("read"); } +probe syscall.read { stamp ("read"); } diff --git a/testsuite/semko/seven.stp b/testsuite/semko/seven.stp index 7d987f77..ce36762a 100755 --- a/testsuite/semko/seven.stp +++ b/testsuite/semko/seven.stp @@ -1,5 +1,6 @@ -#! semtest +#! stap -p2 +global baz probe foo { baz[1] = 4; baz["1"] = 5; # inconsistent index types diff --git a/testsuite/semko/six.stp b/testsuite/semko/six.stp index aa7d10d7..89457fac 100755 --- a/testsuite/semko/six.stp +++ b/testsuite/semko/six.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 probe foo { bar[1] = 2; diff --git a/testsuite/semko/three.stp b/testsuite/semko/three.stp index bfdeec66..423dbcdf 100755 --- a/testsuite/semko/three.stp +++ b/testsuite/semko/three.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 probe foo { a << 2; diff --git a/testsuite/semko/two.stp b/testsuite/semko/two.stp index 39b77f6a..acf0a973 100755 --- a/testsuite/semko/two.stp +++ b/testsuite/semko/two.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 function zoo (p) { p << 5; return 0 } # passing stats as function arg diff --git a/testsuite/semok/five.stp b/testsuite/semok/five.stp index 2bb656b1..4e229f42 100755 --- a/testsuite/semok/five.stp +++ b/testsuite/semok/five.stp @@ -1,5 +1,6 @@ -#! semtest +#! stap -p2 +global array probe foo { array[1] = array[2] = 3; diff --git a/testsuite/semok/four.stp b/testsuite/semok/four.stp index eb87f3f9..978ec557 100755 --- a/testsuite/semok/four.stp +++ b/testsuite/semok/four.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 # these will ultimately be somehow associated with "providers" # and have a syntax of their own diff --git a/testsuite/semok/one.stp b/testsuite/semok/one.stp index 764bd476..909d8eac 100755 --- a/testsuite/semok/one.stp +++ b/testsuite/semok/one.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 # these will ultimately be somehow associated with "providers" # and have a syntax of their own diff --git a/testsuite/semok/seven.stp b/testsuite/semok/seven.stp new file mode 100755 index 00000000..8351c1e2 --- /dev/null +++ b/testsuite/semok/seven.stp @@ -0,0 +1,27 @@ +#! stap -p2 + +global ar1, ar2 + +function string (v) { num=v+0; return "stringify me" } # to become a built-in +function printk (s) { str=s.""; return 0 } # to become a built-in + +function search (key) +{ + if (1) # (key in ar1) + { ar1[key] ++; return ar2[ar1[key]] } + else + return "no can do" # implies ar2[]: string +} + +probe syscall("zamboni") +{ + thread->ar2 = string ($tgid); + search ($pid) +} + +probe end +{ + # for (key in ar2) + if (1) # (key in ar2) + printk ("this: " . string (key) . " was " . ar2[key]) +} diff --git a/testsuite/semok/six.stp b/testsuite/semok/six.stp index 72b7e065..ef14827c 100755 --- a/testsuite/semok/six.stp +++ b/testsuite/semok/six.stp @@ -1,4 +1,6 @@ -#! semtest +#! stap -p2 + +global bar, baz probe foo { diff --git a/testsuite/semok/three.stp b/testsuite/semok/three.stp index 100a5f2b..41d726a0 100755 --- a/testsuite/semok/three.stp +++ b/testsuite/semok/three.stp @@ -1,5 +1,6 @@ -#! semtest +#! stap -p2 +global b probe foo { a <<< 2; b[4] <<< 4; diff --git a/testsuite/semok/two.stp b/testsuite/semok/two.stp index 5a124474..e68d596e 100755 --- a/testsuite/semok/two.stp +++ b/testsuite/semok/two.stp @@ -1,4 +1,4 @@ -#! semtest +#! stap -p2 global bar, baz diff --git a/translate.cxx b/translate.cxx new file mode 100644 index 00000000..7e2669f4 --- /dev/null +++ b/translate.cxx @@ -0,0 +1,1154 @@ +// semantic analysis pass, beginnings of elaboration +// Copyright 2005 Red Hat Inc. +// GPL + +#include "config.h" +#include "staptree.h" +#include "elaborate.h" +#include "translate.h" +#include <iostream> + +using namespace std; + + +// ------------------------------------------------------------------------ +// toy provider/unparser pair + + +struct test_derived_probe: public derived_probe +{ + test_derived_probe (probe* p); + test_derived_probe (probe* p, probe_point* l); + + void emit_registrations (translator_output* o, unsigned i); + void emit_deregistrations (translator_output* o, unsigned i); + void emit_probe_entries (translator_output* o, unsigned i); +}; + + + +void +symresolution_info::derive_probes (probe *p, vector<derived_probe*>& dps) +{ + // XXX: there will be real ones coming later + for (unsigned i=0; i<p->locations.size(); i++) + dps.push_back (new test_derived_probe (p, p->locations[i])); +} + + + +// unique name generator +static unsigned tmpidxgen; + + +struct test_unparser: public unparser, public visitor +{ + systemtap_session* session; + translator_output* o; + + derived_probe* current_probe; + unsigned current_probenum; + functiondecl* current_function; + + test_unparser (systemtap_session *ss): session (ss), o (ss->op), + current_probe(0), current_function (0) {} + ~test_unparser () {} + + void emit_common_header (); + void emit_global (vardecl* v); + void emit_functionsig (functiondecl* v); + void emit_module_init (); + void emit_module_exit (); + void emit_function (functiondecl* v); + void emit_probe (derived_probe* v, unsigned i); + + // XXX: for use by loop/nesting constructs + // vector<string> loop_break_labels; + // vector<string> loop_continue_labels; + + string c_typename (exp_type e); + string c_varname (const string& e); + + void visit_block (block *s); + void visit_null_statement (null_statement *s); + void visit_expr_statement (expr_statement *s); + void visit_if_statement (if_statement* s); + void visit_for_loop (for_loop* s); + void visit_return_statement (return_statement* s); + void visit_delete_statement (delete_statement* s); + void visit_literal_string (literal_string* e); + void visit_literal_number (literal_number* e); + void visit_binary_expression (binary_expression* e); + void visit_unary_expression (unary_expression* e); + void visit_pre_crement (pre_crement* e); + void visit_post_crement (post_crement* e); + void visit_logical_or_expr (logical_or_expr* e); + void visit_logical_and_expr (logical_and_expr* e); + void visit_array_in (array_in* e); + void visit_comparison (comparison* e); + void visit_concatenation (concatenation* e); + void visit_exponentiation (exponentiation* e); + void visit_ternary_expression (ternary_expression* e); + void visit_assignment (assignment* e); + void visit_symbol (symbol* e); + void visit_arrayindex (arrayindex* e); + void visit_functioncall (functioncall* e); +}; + + + +// ------------------------------------------------------------------------ + + +// Perform inter-script dependency analysis [XXX: later], +// then provider elaboration and derived probe construction +// and finally semantic analysis +// on the reachable set of probes/functions from the user_file +int +resolution_pass (systemtap_session& s) +{ + int rc = 0; + + for (unsigned i=0; i<s.user_file->probes.size(); i++) + { + probe* p = s.user_file->probes[i]; + // XXX: should of course be based on each probe_point + derived_probe *dp = new test_derived_probe (p); + s.probes.push_back (dp); + } + + // XXX: merge functiondecls + // XXX: handle library files + // XXX: add builtin variables/functions + return rc; +} + + +// ------------------------------------------------------------------------ + + +translator_output::translator_output (ostream& f): + o2 (0), o (f), tablevel (0) +{ +} + + +translator_output::translator_output (const string& filename): + o2 (new ofstream (filename.c_str ())), o (*o2), tablevel (0) +{ +} + + +translator_output::~translator_output () +{ + delete o2; +} + + +ostream& +translator_output::newline (int indent) +{ + assert (indent > 0 || tablevel >= (unsigned)-indent); + tablevel += indent; + o << endl; + for (unsigned i=0; i<tablevel; i++) + o << " "; + return o; +} + + +void +translator_output::indent (int indent) +{ + assert (indent > 0 || tablevel >= (unsigned)-indent); + tablevel += indent; +} + + +ostream& +translator_output::line () +{ + return o; +} + + + +// ------------------------------------------------------------------------ + + +test_derived_probe::test_derived_probe (probe* p): derived_probe (p) +{ +} + + +test_derived_probe::test_derived_probe (probe* p, probe_point* l): + derived_probe (p, l) +{ +} + + +void +test_derived_probe::emit_registrations (translator_output* o, unsigned i) +{ + // XXX + o->newline() << "rc = 0; /* no registration for probe " << i << " */"; +} + +void +test_derived_probe::emit_deregistrations (translator_output* o, unsigned i) +{ + // XXX + o->newline() << "rc = 0; /* no deregistration for probe " << i << " */"; +} + + +void +test_derived_probe::emit_probe_entries (translator_output* o, unsigned j) +{ + for (unsigned i=0; i<locations.size(); i++) + { + probe_point *l = locations[i]; + o->newline() << "/* location " << i << ": " << *l << " */"; + o->newline() << "static void enter_" << j << "_" << i << " ()"; + o->newline() << "{"; + o->newline(1) << "struct context* c = & contexts [0];"; + // XXX: assert #0 is free; need locked search instead + o->newline() << "if (c->busy) { errorcount ++; return; }"; + o->newline() << "c->busy ++;"; + o->newline() << "c->actioncount = 0;"; + o->newline() << "c->nesting = 0;"; + // NB: locals are initialized by probe function itself + o->newline() << "probe_" << j << " (c);"; + o->newline() << "c->busy --;"; + o->newline(-1) << "}" << endl; + } +} + + +// ------------------------------------------------------------------------ + + +void +test_unparser::emit_common_header () +{ + o->newline() << "#include <string.h>"; + o->newline() << "#define NR_CPU 1"; + o->newline() << "#define MAXNESTING 30"; + o->newline() << "#define MAXCONCURRENCY NR_CPU"; + o->newline() << "#define MAXSTRINGLEN 128"; + o->newline() << "#define MAXACTION 1000"; + o->newline(); + o->newline() << "typedef char string_t[MAXSTRINGLEN];"; + o->newline() << "typedef struct { int a; } stats_t;"; + o->newline(); + o->newline() << "unsigned errorcount;"; + o->newline() << "struct context {"; + o->indent(1); + o->newline() << "unsigned busy;"; + o->newline() << "unsigned actioncount;"; + o->newline() << "unsigned nesting;"; + o->newline() << "union {"; + o->indent(1); + // XXX: this handles only scalars! + + for (unsigned i=0; i<session->probes.size(); i++) + { + o->newline() << "struct {"; + derived_probe* dp = session->probes[i]; + o->newline(1) << "/* local variables */"; + for (unsigned j=0; j<dp->locals.size(); j++) + { + vardecl* v = dp->locals[j]; + o->newline() << c_typename (v->type) << " " + << c_varname (v->name) << ";"; + } + o->newline(-1) << "} probe_" << i << ";"; + } + + for (unsigned i=0; i<session->functions.size(); i++) + { + o->newline() << "struct {"; + functiondecl* fd = session->functions[i]; + o->newline(1) << "/* local variables */"; + for (unsigned j=0; j<fd->locals.size(); j++) + { + vardecl* v = fd->locals[j]; + o->newline() << c_typename (v->type) << " " + << c_varname (v->name) << ";"; + } + o->newline() << "/* formal arguments */"; + for (unsigned j=0; j<fd->formal_args.size(); j++) + { + vardecl* v = fd->formal_args[j]; + o->newline() << c_typename (v->type) << " " + << c_varname (v->name) << ";"; + } + if (fd->type == pe_unknown) + o->newline() << "/* no return value */"; + else + { + o->newline() << "/* return value */"; + o->newline() << c_typename (fd->type) << " retvalue;"; + } + o->newline(-1) << "} function_" << c_varname (fd->name) << ";"; + } + + // XXX: must allocate context temporary pool + + o->newline(-1) << "} locals [MAXNESTING];"; + o->newline(-1) << "} contexts [MAXCONCURRENCY];" << endl; +} + + +void +test_unparser::emit_global (vardecl *v) +{ + o->newline() << "static " + << c_typename (v->type) + << " " + << "global_" << c_varname (v->name) + << ";"; + /* XXX + o->line() << "static DEFINE_RWLOCK(" + << c_varname (v->name) << "_lock" + << ");"; + */ +} + + +void +test_unparser::emit_functionsig (functiondecl* v) +{ + o->newline() << "static void function_" << v->name + << " (struct context *c);"; +} + + +void +test_unparser::emit_module_init () +{ + o->newline() << "static int STARTUP () {"; + o->newline(1) << "int anyrc = 0;"; + o->newline() << "int rc;"; + // XXX: initialize globals + for (unsigned i=0; i<session->probes.size(); i++) + { + session->probes[i]->emit_registrations (o, i); + o->newline() << "anyrc |= rc;"; + o->newline() << "if (rc) {"; + o->indent(1); + if (i > 0) + for (unsigned j=i; j>0; j--) + session->probes[j-1]->emit_deregistrations (o, j-1); + // XXX: ignore rc + o->newline() << "goto out;"; + o->newline(-1) << "}"; + } + o->newline(-1) << "out:"; + o->indent(1); + o->newline() << "return anyrc; /* if (anyrc) log badness */"; + o->newline(-1) << "}" << endl; +} + + +void +test_unparser::emit_module_exit () +{ + o->newline() << "static int SHUTDOWN () {"; + o->newline(1) << "int anyrc = 0;"; + o->newline() << "int rc;"; + for (unsigned i=0; i<session->probes.size(); i++) + { + session->probes[i]->emit_deregistrations (o, i); + o->newline() << "anyrc |= rc;"; + } + // XXX: uninitialize globals + o->newline() << "return anyrc; /* if (anyrc) log badness */"; + o->newline(-1) << "}" << endl; +} + + +void +test_unparser::emit_function (functiondecl* v) +{ + o->newline() << "static void function_" << c_varname (v->name) + << " (struct context* c) {"; + o->indent(1); + this->current_probe = 0; + this->current_probenum = 0; + this->current_function = v; + v->body->visit (this); + this->current_function = 0; + + o->newline(-1) << "out:"; + o->newline(1) << ";"; + + o->newline(-1) << "}" << endl; +} + + +void +test_unparser::emit_probe (derived_probe* v, unsigned i) +{ + o->newline() << "static void probe_" << i << " (struct context *c) {"; + o->indent(1); + + o->newline() << "/* initialize locals */"; + for (unsigned j=0; j<v->locals.size(); j++) + { + if (v->locals[j]->index_types.size() > 0) // array? + throw semantic_error ("not yet implemented", v->tok); + else if (v->locals[j]->type == pe_long) + o->newline() << "c->locals[c->nesting]" + << ".probe_" << i + << "." << c_varname (v->locals[j]->name) + << " = 0;"; + else if (v->locals[j]->type == pe_string) + o->newline() << "c->locals[c->nesting]" + << ".probe_" << i + << "." << c_varname (v->locals[j]->name) + << "[0] = '\\0';"; + else + throw semantic_error ("unsupported local variable type", + v->locals[j]->tok); + } + + this->current_function = 0; + this->current_probe = v; + this->current_probenum = i; + v->body->visit (this); + this->current_probe = 0; + this->current_probenum = 0; // not essential + + o->newline(-1) << "out:"; + o->newline(1) << ";"; + + // XXX: uninitialize locals + + o->newline(-1) << "}" << endl; + + v->emit_probe_entries (o, i); +} + + +string +test_unparser::c_typename (exp_type e) +{ + switch (e) + { + case pe_long: return string("long"); + case pe_string: return string("string_t"); + case pe_stats: return string("stats_t"); + case pe_unknown: + default: + throw semantic_error ("cannot expand unknown type"); + } +} + + +string +test_unparser::c_varname (const string& e) +{ + // XXX: safeify, uniquefy, given name + return e; +} + + +void +test_unparser::visit_block (block *s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + + o->newline() << "{"; + o->indent (1); + for (unsigned i=0; i<s->statements.size(); i++) + { + try + { + /* XXX: don't define an individual statement as an action + o->newline() << "c->actioncount ++;"; + o->newline() << "if (c->actioncount > MAXACTION)"; + o->newline(1) << "errorcount ++;"; + o->indent(-1); + */ + o->newline() << "if (errorcount)"; + o->newline(1) << "goto out;" << endl; + o->indent(-1); + + s->statements[i]->visit (this); + o->newline(); + } + catch (const semantic_error& e) + { + session->print_error (e); + } + } + o->newline(-1) << "}"; +} + + +void +test_unparser::visit_null_statement (null_statement *s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + o->newline() << "/* null */;"; +} + + +void +test_unparser::visit_expr_statement (expr_statement *s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + o->newline() << "(void) "; + s->value->visit (this); + o->line() << ";"; +} + + +void +test_unparser::visit_if_statement (if_statement *s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + o->newline() << "if ("; + o->indent (1); + s->condition->visit (this); + o->indent (-1); + o->line() << ") {"; + o->indent (1); + s->thenblock->visit (this); + o->newline(-1) << "}"; + if (s->elseblock) + { + o->newline() << "else {"; + o->indent (1); + s->thenblock->visit (this); + o->newline(-1) << "}"; + } +} + + +void +test_unparser::visit_for_loop (for_loop *s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + throw semantic_error ("not yet implemented", s->tok); +} + + +void +test_unparser::visit_return_statement (return_statement* s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + if (current_function == 0) + throw semantic_error ("cannot return from non-function", s->tok); + + if (s->value->type != current_function->type) + throw semantic_error ("return type mismatch", current_function->tok, + "vs", s->tok); + + o->newline() << "/* " << *s->tok << " */"; + if (s->value->type == pe_long) + { + o->newline() << "c->locals[c->nesting]" + << ".function_" << c_varname (current_function->name) + << ".retvalue = "; + s->value->visit (this); + o->line() << ";"; + } + else if (s->value->type == pe_string) + { + o->newline() << "strncpy (c->locals[c->nesting]" + << ".function_" << c_varname (current_function->name) + << ".retvalue = "; + s->value->visit (this); + o->line() << ", MAXSTRINGLEN);"; + o->line() << ";"; + } + else + throw semantic_error ("return type unsupported", s->tok); + + o->newline() << "goto out;"; +} + + +void +test_unparser::visit_delete_statement (delete_statement* s) +{ + const token* t = s->tok; + o->newline() << "# " << t->location.line + << " \"" << t->location.file << "\" " << endl; + throw semantic_error ("not yet implemented", s->tok); +} + +void +test_unparser::visit_literal_string (literal_string* e) +{ + o->line() << '"' << e->value << '"'; // XXX: escape special chars +} + +void +test_unparser::visit_literal_number (literal_number* e) +{ + o->line() << e->value; +} + +void +test_unparser::visit_binary_expression (binary_expression* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_unary_expression (unary_expression* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_pre_crement (pre_crement* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_post_crement (post_crement* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_logical_or_expr (logical_or_expr* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_logical_and_expr (logical_and_expr* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_array_in (array_in* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_comparison (comparison* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_concatenation (concatenation* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_exponentiation (exponentiation* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + +void +test_unparser::visit_ternary_expression (ternary_expression* e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + + + +struct test_unparser_assignment: public throwing_visitor +{ + test_unparser* parent; + string op; + expression* rvalue; + test_unparser_assignment (test_unparser* p, const string& o, expression* e): + parent (p), op (o), rvalue (e) {} + + // only symbols and arrayindex nodes are lvalues + void visit_symbol (symbol *e); + void visit_arrayindex (arrayindex *e); +}; + + +void +test_unparser::visit_assignment (assignment* e) +{ + if (e->type != e->left->type) + throw semantic_error ("type mismatch", e->tok, + "vs", e->left->tok); + if (e->right->type != e->left->type) + throw semantic_error ("type mismatch", e->right->tok, + "vs", e->left->tok); + + if (e->op == "=" || e->op == "<<") + { + test_unparser_assignment* tav = + new test_unparser_assignment (this, e->op, e->right); + e->left->visit (tav); + } + else + throw semantic_error ("not yet implemented ", e->tok); +} + + +void +test_unparser::visit_symbol (symbol* e) +{ + vardecl* r = e->referent; + + if (r->index_types.size() != 0) + throw semantic_error ("invalid reference to array", e->tok); + + // XXX: handle special macro symbols + + // maybe the variable is a local + if (current_probe) + { + for (unsigned i=0; i<current_probe->locals.size(); i++) + { + vardecl* rr = current_probe->locals[i]; + if (rr == r) // comparison of pointers is sufficient + { + o->line() << "c->locals[c->nesting]" + << ".probe_" << current_probenum + << "." << c_varname (r->name); + return; + } + } + } + else if (current_function) + { + for (unsigned i=0; i<current_function->locals.size(); i++) + { + vardecl* rr = current_function->locals[i]; + if (rr == r) // comparison of pointers is sufficient + { + o->line() << "c->locals[c->nesting]" + << ".function_" + << c_varname (current_function->name) + << "." << c_varname (r->name); + return; + } + } + } + + // it better be a global + for (unsigned i=0; i<session->globals.size(); i++) + { + if (session->globals[i] == r) + { + // XXX: acquire read lock on global; copy value + // into local temporary + o->line() << "global_" << c_varname (r->name); + return; + } + } + + throw semantic_error ("unresolved symbol", e->tok); +} + + +void +test_unparser_assignment::visit_symbol (symbol *e) +{ + vardecl* r = e->referent; + translator_output* o = parent->o; + functiondecl* current_function = parent->current_function; + derived_probe* current_probe = parent->current_probe; + unsigned current_probenum = parent->current_probenum; + systemtap_session* session = parent->session; + + if (op != "=") + throw semantic_error ("not yet implemented", e->tok); + + if (r->index_types.size() != 0) + throw semantic_error ("invalid reference to array", e->tok); + + // XXX: handle special macro symbols + unsigned tmpidx = ++ tmpidxgen; + + // NB: because assignments are nestable expressions, we have + // to emit C constructs that are nestable expressions too. + // ... (A = B) ... ==> + // ... ({ tmp = B; store(A,tmp); tmp; }) ... + + o->line() << "({ "; + o->indent(1); + + // XXX: muse use context temporary pool instead + o->newline() << parent->c_typename (e->type) + << " tmp" << tmpidx << ";"; + + if (e->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "tmp" << tmpidx; + o->line() << (e->type == pe_long ? " = " : ", "); + rvalue->visit (parent); + if (e->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + + // XXX: strings may be passed safely via a char*, without + // a full copy in tmpNNN + + // maybe the variable is a local + if (current_probe) + { + for (unsigned i=0; i<current_probe->locals.size(); i++) + { + vardecl* rr = current_probe->locals[i]; + if (rr == r) // comparison of pointers is sufficient + { + if (e->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "c->locals[c->nesting]" + << ".probe_" << current_probenum + << "." << parent->c_varname (r->name); + o->line() << (e->type == pe_long ? " = " : ", ") + << "tmp" << tmpidx; + if (e->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + o->newline() << "tmp" << tmpidx << ";"; + o->newline(-1) << "})"; + return; + } + } + } + else if (current_function) + { + for (unsigned i=0; i<current_function->locals.size(); i++) + { + vardecl* rr = current_function->locals[i]; + if (rr == r) // comparison of pointers is sufficient + { + if (e->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "c->locals[c->nesting]" + << ".function_" + << parent->c_varname (current_function->name) + << "." << parent->c_varname (r->name); + o->line() << (e->type == pe_long ? " = " : ", ") + << "tmp" << tmpidx; + if (e->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + o->newline() << "tmp" << tmpidx << ";"; + o->newline(-1) << "})"; + return; + } + } + } + + // it better be a global + for (unsigned i=0; i<session->globals.size(); i++) + { + if (session->globals[i] == r) + { + // XXX: acquire write lock on global + if (e->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "global_" << parent->c_varname (r->name); + o->line() << (e->type == pe_long ? " = " : ", ") + << "tmp" << tmpidx; + if (e->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + o->newline() << "tmp" << tmpidx << ";"; + o->newline(-1) << "})"; + return; + } + } + + throw semantic_error ("unresolved symbol", e->tok); +} + + +void +test_unparser::visit_arrayindex (arrayindex* e) +{ + vardecl* r = e->referent; + + if (r->index_types.size() == 0 || + r->index_types.size() != e->indexes.size()) + throw semantic_error ("invalid array reference", e->tok); + + o->line() << "({"; + o->indent(1); + + o->newline() << "c->actioncount ++;"; + o->newline() << "if (c->actioncount > MAXACTION)"; + o->newline(1) << "errorcount ++;"; + o->indent(-1); + + o->newline() << "if (errorcount)"; + o->newline(1) << "goto out;"; + o->indent(-1); + + // NB: because these expressions are nestable, emit this construct + // thusly: + // ({ tmp0=(idx0); ... tmpN=(idxN); + // fetch (array,idx0...N, &tmpresult); + // tmpresult; }) + // + // we store all indices in temporary variables to avoid nasty + // reentrancy issues that pop up with nested expressions: + // e.g. a[a[c]=5] could deadlock + + vector<unsigned> idxtmps; + for (unsigned i=0; i<r->index_types.size(); i++) + { + if (r->index_types[i] != e->indexes[i]->type) + throw semantic_error ("array index type mismatch", e->indexes[i]->tok); + + unsigned tmpidx = ++ tmpidxgen; + idxtmps.push_back (tmpidx); + // XXX: muse use context temporary pool instead + o->newline() << c_typename (r->index_types[i]) << " tmp" << tmpidx << ";"; + if (e->indexes[i]->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "tmp" << tmpidx; + o->line() << (e->indexes[i]->type == pe_long ? " = " : ", "); + e->indexes[i]->visit (this); + if (e->indexes[i]->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + } + + o->newline() << "if (errorcount)"; + o->newline(1) << "goto out;"; + o->indent(-1); + + o->newline() << c_typename (r->type) << " result;"; + +#if 0 + // maybe the variable is a local + if (current_probe) + { + for (unsigned i=0; i<current_probe->locals.size(); i++) + { + vardecl* rr = current_probe->locals[i]; + if (rr == r) // comparison of pointers is sufficient + { + o->line() << "c->locals[c->nesting]" + << ".probe_" << current_probenum + << "." << c_varname (r->name); + return; + } + } + } + else if (current_function) + { + for (unsigned i=0; i<current_function->locals.size(); i++) + { + vardecl* rr = current_function->locals[i]; + if (rr == r) // comparison of pointers is sufficient + { + o->line() << "c->locals[c->nesting]" + << ".function_" + << c_varname (current_function->name) + << "." << c_varname (r->name); + return; + } + } + } + + // it better be a global + for (unsigned i=0; i<session->globals.size(); i++) + { + if (session->globals[i] == r) + { + // XXX: acquire read lock on global; copy value + // into local temporary + o->line() << "global_" << c_varname (r->name); + return; + } + } + + throw semantic_error ("unresolved symbol", e->tok); +#endif + + + o->newline() << "result;"; + o->newline(-1) << "})"; +} + + +void +test_unparser_assignment::visit_arrayindex (arrayindex *e) +{ + throw semantic_error ("not yet implemented", e->tok); +} + + +void +test_unparser::visit_functioncall (functioncall* e) +{ + functiondecl* r = e->referent; + + if (r->formal_args.size() != e->args.size()) + throw semantic_error ("invalid length argument list", e->tok); + + o->line() << "({"; + o->indent(1); + + // NB: we store all actual arguments in temporary variables, + // to avoid colliding sharing of context variables with + // nested function calls: f(f(f(1))) + + o->newline() << "/* compute actual arguments */"; + for (unsigned i=0; i<e->args.size(); i++) + { + if (r->formal_args[i]->type != e->args[i]->type) + throw semantic_error ("function argument type mismatch", + e->args[i]->tok, "vs", r->formal_args[i]->tok); + + // XXX: muse use context temporary pool instead + o->newline() << c_typename (r->formal_args[i]->type) + << " tmp_" << r->formal_args[i]->name << ";"; + if (e->args[i]->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "tmp_" << c_varname (r->formal_args[i]->name); + o->line() << (e->args[i]->type == pe_long ? " = " : ", "); + e->args[i]->visit (this); + if (e->args[i]->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + } + + o->newline() << "if (c->nesting+2 >= MAXNESTING)"; + o->newline(1) << "errorcount ++;"; + o->newline(-1) << "c->actioncount ++;"; + o->newline() << "if (c->actioncount > MAXACTION)"; + o->newline(1) << "errorcount ++;"; + o->newline(-1) << "if (errorcount)"; + o->newline(1) << "goto out;"; + o->indent(-1); + + o->newline() << "/* initialize locals */"; + for (unsigned i=0; i<r->locals.size(); i++) + { + if (r->locals[i]->index_types.size() > 0) // array? + throw semantic_error ("not yet implemented", r->tok); + else if (r->locals[i]->type == pe_long) + o->newline() << "c->locals[c->nesting+1]" + << ".function_" << c_varname (r->name) + << "." << c_varname (r->locals[i]->name) + << " = 0;"; + else if (r->locals[i]->type == pe_string) + o->newline() << "c->locals[c->nesting+1]" + << ".function_" << c_varname (r->name) + << "." << c_varname (r->locals[i]->name) + << "[0] = '\\0';"; + else + throw semantic_error ("unsupported local variable type", + r->locals[i]->tok); + } + + o->newline() << "if (errorcount)"; + o->newline(1) << "goto out;"; + o->indent(-1); + + o->newline() << "/* copy in stored actual arguments */"; + for (unsigned i=0; i<e->args.size(); i++) + { + if (r->formal_args[i]->type != e->args[i]->type) + throw semantic_error ("function argument type mismatch", + e->args[i]->tok, "vs", r->formal_args[i]->tok); + + if (e->args[i]->type == pe_string) + o->newline() << "strncpy ("; + else + o->newline(); + o->line() << "c->locals[c->nesting+1]" + << ".function_" << c_varname (r->name) + << "." << c_varname (r->formal_args[i]->name); + o->line() << (e->args[i]->type == pe_long ? " = " : ", "); + o->line() << "tmp_" << c_varname (r->formal_args[i]->name); + if (e->args[i]->type == pe_string) + o->line() << ", MAXSTRINGLEN)"; + o->line() << ";"; + } + + // call function + o->newline() << "c->nesting ++;"; + o->newline() << "function_" << c_varname (r->name) << " (c);"; + o->newline() << "c->nesting --;"; + + // XXX: clean up any locals that need it (arrays) + + // return result from retvalue slot + o->newline() << "c->locals[c->nesting+1]" + << ".function_" << c_varname (r->name) + << ".retvalue;"; + + o->newline(-1) << "})"; +} + + + +int +translate_pass (systemtap_session& s) +{ + int rc = 0; + + s.up = new test_unparser (& s); + + try + { + s.op->newline() << "/* common header */"; + s.up->emit_common_header (); + s.op->newline() << "/* globals */"; + for (unsigned i=0; i<s.globals.size(); i++) + s.up->emit_global (s.globals[i]); + s.op->newline() << "/* function signatures */"; + for (unsigned i=0; i<s.functions.size(); i++) + s.up->emit_functionsig (s.functions[i]); + s.op->newline() << "/* functions */"; + for (unsigned i=0; i<s.functions.size(); i++) + s.up->emit_function (s.functions[i]); + s.op->newline() << "/* probes */"; + for (unsigned i=0; i<s.probes.size(); i++) + s.up->emit_probe (s.probes[i], i); + s.op->newline() << "/* module init */"; + s.up->emit_module_init (); + s.op->newline() << "/* module exit */"; + s.up->emit_module_exit (); + s.op->newline(); + } + catch (const semantic_error& e) + { + s.print_error (e); + } + + delete s.up; + return rc + s.num_errors; +} diff --git a/translate.h b/translate.h new file mode 100644 index 00000000..f0bd973c --- /dev/null +++ b/translate.h @@ -0,0 +1,93 @@ +// -*- C++ -*- +// Copyright 2005 Red Hat Inc. +// GPL + + +#ifndef TRANSLATE_H +#define TRANSLATE_H + +#include "staptree.h" +#include "parse.h" +#include <iostream> +#include <fstream> + + +// ------------------------------------------------------------------------ + +// Output context for systemtap translation, intended to allow +// pretty-printing. +class translator_output +{ + std::ofstream* o2; + std::ostream& o; + unsigned tablevel; + +public: + translator_output (std::ostream& file); + translator_output (const std::string& filename); + ~translator_output (); + + std::ostream& newline (int indent = 0); + void indent (int indent = 0); + std::ostream& line(); +}; + + +// An unparser instance is in charge of emitting code for generic +// probe bodies, functions, globals. +struct unparser +{ + virtual ~unparser () {} + + virtual void emit_common_header () = 0; + // #include<...> + // + // #define MAXNESTING nnn + // #define MAXCONCURRENCY mmm + // #define MAXSTRINGLEN ooo + // + // enum session_state_t { + // starting, begin, running, suspended, errored, ending, ended + // }; + // static atomic_t session_state; + // static atomic_t errorcount; /* subcategorize? */ + // + // struct context { + // unsigned busy; + // unsigned actioncount; + // unsigned nesting; + // union { + // struct { .... } probe_NUM_locals; + // struct { .... } function_NAME_locals; + // } locals [MAXNESTING]; + // } context [MAXCONCURRENCY]; + + virtual void emit_global (vardecl* v) = 0; + // static TYPE global_NAME; + // static DEFINE_RWLOCK(global_NAME_lock); + + virtual void emit_functionsig (functiondecl* v) = 0; + // static void function_NAME (context* c); + + virtual void emit_module_init () = 0; + virtual void emit_module_exit () = 0; + // XXX + + virtual void emit_function (functiondecl* v) = 0; + // void function_NAME (struct context* c) { + // .... + // } + + virtual void emit_probe (derived_probe* v, unsigned i) = 0; + // void probe_NUMBER (struct context* c) { + // ... lifecycle + // .... + // } + // ... then call over to the derived_probe's emit_probe_entries() fn +}; + + +int translate_pass (systemtap_session& s); + + +#endif // TRANSLATE_H |