From 2b066ec1b8801b08052a68282ce34ef9c425ae8f Mon Sep 17 00:00:00 2001 From: fche Date: Sat, 21 May 2005 01:35:34 +0000 Subject: * at long last, a more full-bodied snapshot 2005-05-20 Frank Ch. Eigler 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. --- .cvsignore | 1 + ChangeLog | 10 + Makefile.am | 5 +- Makefile.in | 41 +- README | 10 +- config.in | 12 + configure | 540 +++++++++++++++++++- configure.ac | 5 + cscope.files | 10 + elaborate.cxx | 1019 ++++++++++++++++++++++++++++++++++++++ elaborate.h | 161 ++++++ main.cxx | 196 ++++++++ parse.cxx | 59 ++- parse.h | 10 +- parsetest.cxx | 38 -- semtest.cxx | 182 ------- staptree.cxx | 692 +------------------------- staptree.h | 225 +++------ testsuite/parseko/five.stp | 2 +- testsuite/parseko/four.stp | 2 +- testsuite/parseko/one.stp | 2 +- testsuite/parseko/six.stp | 2 +- testsuite/parseko/three.stp | 6 +- testsuite/parseko/two.stp | 2 +- testsuite/parseok/five.stp | 2 +- testsuite/parseok/four.stp | 2 +- testsuite/parseok/one.stp | 2 +- testsuite/parseok/seven.stp | 26 + testsuite/parseok/six.stp | 2 +- testsuite/parseok/three.stp | 2 +- testsuite/parseok/two.stp | 2 +- testsuite/provok/one.stp | 26 + testsuite/semko/eight.stp | 2 +- testsuite/semko/five.stp | 2 +- testsuite/semko/four.stp | 2 +- testsuite/semko/nine.stp | 2 +- testsuite/semko/one.stp | 4 +- testsuite/semko/seven.stp | 3 +- testsuite/semko/six.stp | 2 +- testsuite/semko/three.stp | 2 +- testsuite/semko/two.stp | 2 +- testsuite/semok/five.stp | 3 +- testsuite/semok/four.stp | 2 +- testsuite/semok/one.stp | 2 +- testsuite/semok/seven.stp | 27 + testsuite/semok/six.stp | 4 +- testsuite/semok/three.stp | 3 +- testsuite/semok/two.stp | 2 +- translate.cxx | 1154 +++++++++++++++++++++++++++++++++++++++++++ translate.h | 93 ++++ 50 files changed, 3467 insertions(+), 1140 deletions(-) create mode 100644 .cvsignore create mode 100644 cscope.files create mode 100644 elaborate.cxx create mode 100644 elaborate.h create mode 100644 main.cxx delete mode 100644 parsetest.cxx delete mode 100644 semtest.cxx create mode 100755 testsuite/parseok/seven.stp create mode 100755 testsuite/provok/one.stp create mode 100755 testsuite/semok/seven.stp create mode 100644 translate.cxx create mode 100644 translate.h diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 00000000..c6be8e47 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +autom4te.* cscope*out diff --git a/ChangeLog b/ChangeLog index 4a24cc00..746b0370 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2005-05-20 Frank Ch. Eigler + + 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 * 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 $@ $<; \ diff --git a/README b/README index 1bf982b0..dc174ca5 100644 --- a/README +++ b/README @@ -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' diff --git a/config.in b/config.in index eb359e43..8297d4bd 100644 --- a/config.in +++ b/config.in @@ -1,8 +1,20 @@ /* config.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the header file. */ +#undef HAVE_ELFUTILS_LIBDW_H + /* Define to 1 if you have the 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 header file. */ +#undef HAVE_LIBELF_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H diff --git a/configure b/configure index b1c84799..8ecf2e47 100755 --- a/configure +++ b/configure @@ -310,7 +310,7 @@ ac_includes_default="\ # include #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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 +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 +} + +#include +#include + +#if 0 +#ifdef HAVE_ELFUTILS_LIBDW_H +#include +#else +#error "need " +#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; iglobals.size(); i++) + s.globals.push_back (dome->globals[i]); + + for (unsigned i=0; ifunctions.size(); i++) + s.functions.push_back (dome->functions[i]); + + // Pass 2: process functions + + for (unsigned i=0; ifunctions.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; iprobes.size(); i++) + { + probe* p = dome->probes [i]; + vector 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; jbody->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; istatements.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; iindexes.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; iargs.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& locals = (current_function ? + current_function->locals : + current_probe->locals); + for (unsigned i=0; iname == 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; iformal_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; iname == name) + if (session.globals[i]->arity <= 0) + { + session.globals[i]->set_arity (0); + return session.globals[i]; + } + + // search library globals + for (unsigned i=0; iglobals.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 = ""; + 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; iname == 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; iglobals.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; ifunctions.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; jbody->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; jbody->visit (& ti); + } + + for (unsigned j=0; jtype == 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 +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 ;" 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; iindexes.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; iindexes.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; iargs.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; istatements.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 +#include + + +// ------------------------------------------------------------------------ + +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& 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 library_files; + + // configuration options + // bool verbose_resolution; + + // resolved globals/functions/probes for the run as a whole + std::vector files; + std::vector globals; + std::vector functions; + std::vector 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 +#include +#include + +extern "C" { +#include +#include +} + +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 include_path; // -I DIR + vector 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; iline() << "# parse tree dump"; + s.user_file->print (s.op->newline()); + for (unsigned i=0; iprint (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; iprintsig (s.op->newline()); + } + + s.op->newline() << "# functions"; + for (unsigned i=0; iprintsig (s.op->newline()); + } + + s.op->newline() << "# probes"; + for (unsigned i=0; iprintsig (s.op->newline()); + } + } + + // PASS 3: TRANSLATION + if (rc == 0 && last_pass > 2) + rc = translate_pass (s); + + delete s.op; + + return rc; +} diff --git a/parse.cxx b/parse.cxx index b5881331..2bf0f977 100644 --- a/parse.cxx +++ b/parse.cxx @@ -2,12 +2,13 @@ // Copyright 2005 Red Hat Inc. // GPL -#include +#include "config.h" #include "staptree.h" #include "parse.h" +#include +#include #include #include -#include #include #include @@ -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 & 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; iname == 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; diff --git a/parse.h b/parse.h index 1d07645f..c0e457e9 100644 --- a/parse.h +++ b/parse.h @@ -2,6 +2,9 @@ // Copyright 2005 Red Hat Inc. // GPL +#ifndef PARSE_H +#define PARSE_H + #include #include #include @@ -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&); + void parse_global (std::vector&); 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 - - -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 - - -int -semantic_pass_1 (vector& files) -{ - int rc = 0; - - // link up symbols to their declarations - for (unsigned i=0; ifunctions.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; jprobes.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& 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; ifunctions.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; jprobes.size(); j++) - { - probe* pn = f->probes[j]; - ti.current_function = 0; - pn->body->resolve_types (ti); - } - - for (unsigned j=0; jglobals.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 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; ifunctions.size(); j++) - { - functiondecl* fn = f->functions[j]; - cerr << "Function "; - fn->printsig (cerr); - cerr << endl << "locals:" << endl; - for (unsigned k=0; klocals.size(); k++) - { - vardecl* fa = fn->locals[k]; - cerr << "\t"; - fa->printsig (cerr); - cerr << endl; - } - cerr << endl; - } - - for (unsigned j=0; jprobes.size(); j++) - { - probe* pn = f->probes[j]; - cerr << "Probe "; - pn->printsig (cerr); - cerr << "locals:" << endl; - for (unsigned k=0; klocals.size(); k++) - { - vardecl* fa = pn->locals[k]; - cerr << "\t"; - fa->printsig (cerr); - cerr << endl; - } - cerr << endl; - } - - cerr << "globals:" << endl; - for (unsigned k=0; kglobals.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 #include #include +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& l, - vector& g, - vector& f): - locals (l), globals (g), functions (f), current_function (0) -{ - num_unresolved = 0; -} - - -symresolution_info::symresolution_info (vector& l, - vector& g, - vector& 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; iresolve_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; iresolve_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; iresolve_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; iname == 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 = ""; - vb->tok = t; - - locals.push_back (vb); - return vb; - } - - // search function formal parameters (if any) - if (current_function) - { - for (unsigned i=0; iformal_args.size(); i++) - if (current_function->formal_args [i]->name == name) - return current_function->formal_args [i]; - } - - // search globals - for (unsigned i=0; iname == name) - return globals[i]; - - return 0; - // XXX: add checking for conflicting array or function -} - - -vardecl* -symresolution_info::find_array (const string& name, - const vector& indexes) -{ - // search locals - for (unsigned i=0; iname == name) - return locals[i]; - - // search function formal parameters (if any) - if (current_function) - { - for (unsigned i=0; iformal_args.size(); i++) - if (current_function->formal_args [i]->name == name) - return current_function->formal_args [i]; - } - - // search globals - for (unsigned i=0; iname == name) - return globals[i]; - - return 0; - // XXX: add checking for conflicting scalar or function -} - - -functiondecl* -symresolution_info::find_function (const string& name, - const vector& 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 -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; iindex_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; iresolve_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; iformal_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; iresolve_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 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 diff --git a/staptree.h b/staptree.h index 82b95292..203bc27e 100644 --- a/staptree.h +++ b/staptree.h @@ -7,28 +7,24 @@ #include #include -#include #include #include -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 indexes; + std::string base; + std::vector 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 args; + std::string function; + std::vector 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& locals; // includes incoming function parameters - vector& globals; - vector& functions; - functiondecl* current_function; - - symresolution_info (vector& l, - vector& g, - vector& f, - functiondecl* cfun); - symresolution_info (vector& l, - vector& g, - vector& f); - - vardecl* find_scalar (const string& name); - vardecl* find_array (const string& name, const vector&); - functiondecl* find_function (const string& name, const vector&); - - 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 index_types; // for arrays only + void set_arity (int arity); + int arity; // -1: unknown; 0: scalar; >0: array + std::vector index_types; // for arrays only }; @@ -305,12 +236,12 @@ struct vardecl_builtin: public vardecl struct block; struct functiondecl: public symboldecl { - vector formal_args; - vector locals; + std::vector formal_args; + std::vector 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 statements; - void print (ostream& o); + std::vector 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 probes; - vector functions; - vector globals; - void print (ostream& o); + std::string name; + std::vector probes; + std::vector functions; + std::vector 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 components; + std::vector 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 locations; + std::vector locations; block* body; const token* tok; - vector locals; + std::vector 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 + +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& dps) +{ + // XXX: there will be real ones coming later + for (unsigned i=0; ilocations.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 loop_break_labels; + // vector 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; iprobes.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 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; inewline() << "/* 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 "; + 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; iprobes.size(); i++) + { + o->newline() << "struct {"; + derived_probe* dp = session->probes[i]; + o->newline(1) << "/* local variables */"; + for (unsigned j=0; jlocals.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; ifunctions.size(); i++) + { + o->newline() << "struct {"; + functiondecl* fd = session->functions[i]; + o->newline(1) << "/* local variables */"; + for (unsigned j=0; jlocals.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; jformal_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; iprobes.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; iprobes.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; jlocals.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; istatements.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; ilocals.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; ilocals.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; iglobals.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; ilocals.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; ilocals.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; iglobals.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 idxtmps; + for (unsigned i=0; iindex_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; ilocals.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; ilocals.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; iglobals.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; iargs.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; ilocals.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; iargs.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; iemit_global (s.globals[i]); + s.op->newline() << "/* function signatures */"; + for (unsigned i=0; iemit_functionsig (s.functions[i]); + s.op->newline() << "/* functions */"; + for (unsigned i=0; iemit_function (s.functions[i]); + s.op->newline() << "/* probes */"; + for (unsigned i=0; iemit_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 +#include + + +// ------------------------------------------------------------------------ + +// 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 -- cgit