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