summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am5
-rw-r--r--Makefile.in130
-rw-r--r--tapset-been.cxx228
-rw-r--r--tapset-itrace.cxx337
-rw-r--r--tapset-mark.cxx712
-rw-r--r--tapset-perfmon.cxx463
-rw-r--r--tapset-procfs.cxx521
-rw-r--r--tapset-timers.cxx626
-rw-r--r--tapset-utrace.cxx1061
-rw-r--r--tapsets.cxx3996
-rw-r--r--tapsets.h41
-rw-r--r--task_finder.cxx103
-rw-r--r--task_finder.h20
13 files changed, 4291 insertions, 3952 deletions
diff --git a/Makefile.am b/Makefile.am
index d1b5789e..35f9a68b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,7 +37,10 @@ bin_SCRIPTS += dtrace
stap_SOURCES = main.cxx \
parse.cxx staptree.cxx elaborate.cxx translate.cxx \
tapsets.cxx buildrun.cxx loc2c.c hash.cxx mdfour.c \
- cache.cxx util.cxx coveragedb.cxx dwarf_wrappers.cxx
+ cache.cxx util.cxx coveragedb.cxx dwarf_wrappers.cxx \
+ tapset-been.cxx tapset-procfs.cxx tapset-timers.cxx \
+ tapset-perfmon.cxx tapset-mark.cxx tapset-itrace.cxx \
+ tapset-utrace.cxx task_finder.cxx
stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@
BUILT_SOURCES =
diff --git a/Makefile.in b/Makefile.in
index 0892715e..38f5d297 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -121,7 +121,11 @@ am_stap_OBJECTS = stap-main.$(OBJEXT) stap-parse.$(OBJEXT) \
stap-buildrun.$(OBJEXT) stap-loc2c.$(OBJEXT) \
stap-hash.$(OBJEXT) stap-mdfour.$(OBJEXT) stap-cache.$(OBJEXT) \
stap-util.$(OBJEXT) stap-coveragedb.$(OBJEXT) \
- stap-dwarf_wrappers.$(OBJEXT) $(am__objects_1)
+ stap-dwarf_wrappers.$(OBJEXT) stap-tapset-been.$(OBJEXT) \
+ stap-tapset-procfs.$(OBJEXT) stap-tapset-timers.$(OBJEXT) \
+ stap-tapset-perfmon.$(OBJEXT) stap-tapset-mark.$(OBJEXT) \
+ stap-tapset-itrace.$(OBJEXT) stap-tapset-utrace.$(OBJEXT) \
+ stap-task_finder.$(OBJEXT) $(am__objects_1)
stap_OBJECTS = $(am_stap_OBJECTS)
stap_LINK = $(CXXLD) $(stap_CXXFLAGS) $(CXXFLAGS) $(stap_LDFLAGS) \
$(LDFLAGS) -o $@
@@ -327,7 +331,9 @@ oldinclude_HEADERS = includes/sys/sdt.h
stap_SOURCES = main.cxx parse.cxx staptree.cxx elaborate.cxx \
translate.cxx tapsets.cxx buildrun.cxx loc2c.c hash.cxx \
mdfour.c cache.cxx util.cxx coveragedb.cxx dwarf_wrappers.cxx \
- $(am__append_4)
+ tapset-been.cxx tapset-procfs.cxx tapset-timers.cxx \
+ tapset-perfmon.cxx tapset-mark.cxx tapset-itrace.cxx \
+ tapset-utrace.cxx task_finder.cxx $(am__append_4)
stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@ $(am__append_6)
# Arrange for git_version.h to be regenerated at every "make".
@@ -601,7 +607,15 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-nsscommon.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-parse.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-staptree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-been.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-itrace.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-mark.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-perfmon.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-procfs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-timers.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-utrace.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapsets.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-task_finder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-translate.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap_client_connect-stap-client-connect.Po@am__quote@
@@ -1081,6 +1095,118 @@ stap-dwarf_wrappers.obj: dwarf_wrappers.cxx
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-dwarf_wrappers.obj `if test -f 'dwarf_wrappers.cxx'; then $(CYGPATH_W) 'dwarf_wrappers.cxx'; else $(CYGPATH_W) '$(srcdir)/dwarf_wrappers.cxx'; fi`
+stap-tapset-been.o: tapset-been.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-been.o -MD -MP -MF $(DEPDIR)/stap-tapset-been.Tpo -c -o stap-tapset-been.o `test -f 'tapset-been.cxx' || echo '$(srcdir)/'`tapset-been.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-been.Tpo $(DEPDIR)/stap-tapset-been.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-been.cxx' object='stap-tapset-been.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-been.o `test -f 'tapset-been.cxx' || echo '$(srcdir)/'`tapset-been.cxx
+
+stap-tapset-been.obj: tapset-been.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-been.obj -MD -MP -MF $(DEPDIR)/stap-tapset-been.Tpo -c -o stap-tapset-been.obj `if test -f 'tapset-been.cxx'; then $(CYGPATH_W) 'tapset-been.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-been.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-been.Tpo $(DEPDIR)/stap-tapset-been.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-been.cxx' object='stap-tapset-been.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-been.obj `if test -f 'tapset-been.cxx'; then $(CYGPATH_W) 'tapset-been.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-been.cxx'; fi`
+
+stap-tapset-procfs.o: tapset-procfs.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-procfs.o -MD -MP -MF $(DEPDIR)/stap-tapset-procfs.Tpo -c -o stap-tapset-procfs.o `test -f 'tapset-procfs.cxx' || echo '$(srcdir)/'`tapset-procfs.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-procfs.Tpo $(DEPDIR)/stap-tapset-procfs.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-procfs.cxx' object='stap-tapset-procfs.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-procfs.o `test -f 'tapset-procfs.cxx' || echo '$(srcdir)/'`tapset-procfs.cxx
+
+stap-tapset-procfs.obj: tapset-procfs.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-procfs.obj -MD -MP -MF $(DEPDIR)/stap-tapset-procfs.Tpo -c -o stap-tapset-procfs.obj `if test -f 'tapset-procfs.cxx'; then $(CYGPATH_W) 'tapset-procfs.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-procfs.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-procfs.Tpo $(DEPDIR)/stap-tapset-procfs.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-procfs.cxx' object='stap-tapset-procfs.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-procfs.obj `if test -f 'tapset-procfs.cxx'; then $(CYGPATH_W) 'tapset-procfs.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-procfs.cxx'; fi`
+
+stap-tapset-timers.o: tapset-timers.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-timers.o -MD -MP -MF $(DEPDIR)/stap-tapset-timers.Tpo -c -o stap-tapset-timers.o `test -f 'tapset-timers.cxx' || echo '$(srcdir)/'`tapset-timers.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-timers.Tpo $(DEPDIR)/stap-tapset-timers.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-timers.cxx' object='stap-tapset-timers.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-timers.o `test -f 'tapset-timers.cxx' || echo '$(srcdir)/'`tapset-timers.cxx
+
+stap-tapset-timers.obj: tapset-timers.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-timers.obj -MD -MP -MF $(DEPDIR)/stap-tapset-timers.Tpo -c -o stap-tapset-timers.obj `if test -f 'tapset-timers.cxx'; then $(CYGPATH_W) 'tapset-timers.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-timers.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-timers.Tpo $(DEPDIR)/stap-tapset-timers.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-timers.cxx' object='stap-tapset-timers.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-timers.obj `if test -f 'tapset-timers.cxx'; then $(CYGPATH_W) 'tapset-timers.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-timers.cxx'; fi`
+
+stap-tapset-perfmon.o: tapset-perfmon.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-perfmon.o -MD -MP -MF $(DEPDIR)/stap-tapset-perfmon.Tpo -c -o stap-tapset-perfmon.o `test -f 'tapset-perfmon.cxx' || echo '$(srcdir)/'`tapset-perfmon.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-perfmon.Tpo $(DEPDIR)/stap-tapset-perfmon.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-perfmon.cxx' object='stap-tapset-perfmon.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-perfmon.o `test -f 'tapset-perfmon.cxx' || echo '$(srcdir)/'`tapset-perfmon.cxx
+
+stap-tapset-perfmon.obj: tapset-perfmon.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-perfmon.obj -MD -MP -MF $(DEPDIR)/stap-tapset-perfmon.Tpo -c -o stap-tapset-perfmon.obj `if test -f 'tapset-perfmon.cxx'; then $(CYGPATH_W) 'tapset-perfmon.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-perfmon.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-perfmon.Tpo $(DEPDIR)/stap-tapset-perfmon.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-perfmon.cxx' object='stap-tapset-perfmon.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-perfmon.obj `if test -f 'tapset-perfmon.cxx'; then $(CYGPATH_W) 'tapset-perfmon.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-perfmon.cxx'; fi`
+
+stap-tapset-mark.o: tapset-mark.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-mark.o -MD -MP -MF $(DEPDIR)/stap-tapset-mark.Tpo -c -o stap-tapset-mark.o `test -f 'tapset-mark.cxx' || echo '$(srcdir)/'`tapset-mark.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-mark.Tpo $(DEPDIR)/stap-tapset-mark.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-mark.cxx' object='stap-tapset-mark.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-mark.o `test -f 'tapset-mark.cxx' || echo '$(srcdir)/'`tapset-mark.cxx
+
+stap-tapset-mark.obj: tapset-mark.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-mark.obj -MD -MP -MF $(DEPDIR)/stap-tapset-mark.Tpo -c -o stap-tapset-mark.obj `if test -f 'tapset-mark.cxx'; then $(CYGPATH_W) 'tapset-mark.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-mark.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-mark.Tpo $(DEPDIR)/stap-tapset-mark.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-mark.cxx' object='stap-tapset-mark.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-mark.obj `if test -f 'tapset-mark.cxx'; then $(CYGPATH_W) 'tapset-mark.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-mark.cxx'; fi`
+
+stap-tapset-itrace.o: tapset-itrace.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-itrace.o -MD -MP -MF $(DEPDIR)/stap-tapset-itrace.Tpo -c -o stap-tapset-itrace.o `test -f 'tapset-itrace.cxx' || echo '$(srcdir)/'`tapset-itrace.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-itrace.Tpo $(DEPDIR)/stap-tapset-itrace.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-itrace.cxx' object='stap-tapset-itrace.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-itrace.o `test -f 'tapset-itrace.cxx' || echo '$(srcdir)/'`tapset-itrace.cxx
+
+stap-tapset-itrace.obj: tapset-itrace.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-itrace.obj -MD -MP -MF $(DEPDIR)/stap-tapset-itrace.Tpo -c -o stap-tapset-itrace.obj `if test -f 'tapset-itrace.cxx'; then $(CYGPATH_W) 'tapset-itrace.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-itrace.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-itrace.Tpo $(DEPDIR)/stap-tapset-itrace.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-itrace.cxx' object='stap-tapset-itrace.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-itrace.obj `if test -f 'tapset-itrace.cxx'; then $(CYGPATH_W) 'tapset-itrace.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-itrace.cxx'; fi`
+
+stap-tapset-utrace.o: tapset-utrace.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-utrace.o -MD -MP -MF $(DEPDIR)/stap-tapset-utrace.Tpo -c -o stap-tapset-utrace.o `test -f 'tapset-utrace.cxx' || echo '$(srcdir)/'`tapset-utrace.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-utrace.Tpo $(DEPDIR)/stap-tapset-utrace.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-utrace.cxx' object='stap-tapset-utrace.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-utrace.o `test -f 'tapset-utrace.cxx' || echo '$(srcdir)/'`tapset-utrace.cxx
+
+stap-tapset-utrace.obj: tapset-utrace.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-utrace.obj -MD -MP -MF $(DEPDIR)/stap-tapset-utrace.Tpo -c -o stap-tapset-utrace.obj `if test -f 'tapset-utrace.cxx'; then $(CYGPATH_W) 'tapset-utrace.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-utrace.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-utrace.Tpo $(DEPDIR)/stap-tapset-utrace.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-utrace.cxx' object='stap-tapset-utrace.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-utrace.obj `if test -f 'tapset-utrace.cxx'; then $(CYGPATH_W) 'tapset-utrace.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-utrace.cxx'; fi`
+
+stap-task_finder.o: task_finder.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-task_finder.o -MD -MP -MF $(DEPDIR)/stap-task_finder.Tpo -c -o stap-task_finder.o `test -f 'task_finder.cxx' || echo '$(srcdir)/'`task_finder.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-task_finder.Tpo $(DEPDIR)/stap-task_finder.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='task_finder.cxx' object='stap-task_finder.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-task_finder.o `test -f 'task_finder.cxx' || echo '$(srcdir)/'`task_finder.cxx
+
+stap-task_finder.obj: task_finder.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-task_finder.obj -MD -MP -MF $(DEPDIR)/stap-task_finder.Tpo -c -o stap-task_finder.obj `if test -f 'task_finder.cxx'; then $(CYGPATH_W) 'task_finder.cxx'; else $(CYGPATH_W) '$(srcdir)/task_finder.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-task_finder.Tpo $(DEPDIR)/stap-task_finder.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='task_finder.cxx' object='stap-task_finder.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-task_finder.obj `if test -f 'task_finder.cxx'; then $(CYGPATH_W) 'task_finder.cxx'; else $(CYGPATH_W) '$(srcdir)/task_finder.cxx'; fi`
+
stap-modsign.o: modsign.cxx
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-modsign.o -MD -MP -MF $(DEPDIR)/stap-modsign.Tpo -c -o stap-modsign.o `test -f 'modsign.cxx' || echo '$(srcdir)/'`modsign.cxx
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-modsign.Tpo $(DEPDIR)/stap-modsign.Po
diff --git a/tapset-been.cxx b/tapset-been.cxx
new file mode 100644
index 00000000..e007a4f7
--- /dev/null
+++ b/tapset-been.cxx
@@ -0,0 +1,228 @@
+// tapset for begin/end/error/never
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#include "session.h"
+#include "tapsets.h"
+#include "translate.h"
+#include "util.h"
+
+#include <algorithm>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_BEGIN("begin");
+static string TOK_END("end");
+static string TOK_ERROR("error");
+static string TOK_NEVER("never");
+
+
+// ------------------------------------------------------------------------
+// begin/end/error probes are run right during registration / deregistration
+// ------------------------------------------------------------------------
+
+enum be_t { BEGIN, END, ERROR };
+
+struct be_derived_probe: public derived_probe
+{
+ be_t type;
+ int64_t priority;
+
+ be_derived_probe (probe* p, probe_point* l, be_t t, int64_t pr):
+ derived_probe (p, l), type (t), priority (pr) {}
+
+ void join_group (systemtap_session& s);
+
+ static inline bool comp(be_derived_probe const *a,
+ be_derived_probe const *b)
+ {
+ // This allows the BEGIN/END/ERROR probes to intermingle.
+ // But that's OK - they're always treversed with a nested
+ // "if (type==FOO)" conditional.
+ return a->priority < b->priority;
+ }
+
+ bool needs_global_locks () { return false; }
+ // begin/end probes don't need locks around global variables, since
+ // they aren't run concurrently with any other probes
+};
+
+
+struct be_derived_probe_group: public generic_dpg<be_derived_probe>
+{
+public:
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+struct be_builder: public derived_probe_builder
+{
+ be_t type;
+
+ be_builder(be_t t) : type(t) {}
+
+ virtual void build(systemtap_session &,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+ {
+ int64_t priority;
+ if ((type == BEGIN && !get_param(parameters, TOK_BEGIN, priority)) ||
+ (type == END && !get_param(parameters, TOK_END, priority)) ||
+ (type == ERROR && !get_param(parameters, TOK_ERROR, priority)))
+ priority = 0;
+ finished_results.push_back
+ (new be_derived_probe(base, location, type, priority));
+ }
+};
+
+
+void
+be_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.be_derived_probes)
+ s.be_derived_probes = new be_derived_probe_group ();
+ s.be_derived_probes->enroll (this);
+}
+
+
+void
+be_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ map<be_t, const char *> states;
+ states[BEGIN] = "STAP_SESSION_STARTING";
+ states[END] = "STAP_SESSION_STOPPING";
+ states[ERROR] = "STAP_SESSION_ERROR";
+
+ s.op->newline() << "/* ---- begin/end/error probes ---- */";
+
+ // NB: We emit the table in sorted order here, so we don't have to
+ // store the priority numbers as integers and sort at run time.
+
+ sort(probes.begin(), probes.end(), be_derived_probe::comp);
+
+ s.op->newline() << "static struct stap_be_probe {";
+ s.op->newline(1) << "void (*ph)(struct context*);";
+ s.op->newline() << "const char* pp;";
+ s.op->newline() << "int state, type;";
+ s.op->newline(-1) << "} stap_be_probes[] = {";
+ s.op->indent(1);
+
+ for (unsigned i=0; i < probes.size(); i++)
+ {
+ s.op->newline () << "{";
+ s.op->line() << " .pp="
+ << lex_cast_qstring (*probes[i]->sole_location()) << ",";
+ s.op->line() << " .ph=&" << probes[i]->name << ",";
+ s.op->line() << " .state=" << states[probes[i]->type] << ",";
+ s.op->line() << " .type=" << probes[i]->type;
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+
+ s.op->newline() << "static void enter_be_probe (struct stap_be_probe *stp) {";
+ s.op->indent(1);
+ common_probe_entryfn_prologue (s.op, "stp->state", "stp->pp", false);
+ s.op->newline() << "(*stp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op, false);
+ s.op->newline(-1) << "}";
+}
+
+void
+be_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_be_probe* stp = & stap_be_probes [i];";
+ s.op->newline() << "if (stp->type == " << BEGIN << ")";
+ s.op->newline(1) << "enter_be_probe (stp); /* rc = 0 */";
+ // NB: begin probes that cause errors do not constitute registration
+ // failures. An error message will probably get printed and if
+ // MAXERRORS was left at 1, we'll get an stp_exit. The
+ // error-handling probes will be run during the ordinary
+ // unregistration phase.
+ s.op->newline(-2) << "}";
+}
+
+void
+be_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_be_probe* stp = & stap_be_probes [i];";
+ s.op->newline() << "if (stp->type == " << END << ")";
+ s.op->newline(1) << "enter_be_probe (stp);";
+ s.op->newline(-2) << "}";
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_be_probe* stp = & stap_be_probes [i];";
+ s.op->newline() << "if (stp->type == " << ERROR << ")";
+ s.op->newline(1) << "enter_be_probe (stp);";
+ s.op->newline(-2) << "}";
+}
+
+
+
+// ------------------------------------------------------------------------
+// never probes are never run
+// ------------------------------------------------------------------------
+
+struct never_derived_probe: public derived_probe
+{
+ never_derived_probe (probe* p): derived_probe (p) {}
+ never_derived_probe (probe* p, probe_point* l): derived_probe (p, l) {}
+ void join_group (systemtap_session&) { /* thus no probe_group */ }
+};
+
+
+struct never_builder: public derived_probe_builder
+{
+ never_builder() {}
+ virtual void build(systemtap_session &,
+ probe * base,
+ probe_point * location,
+ literal_map_t const &,
+ vector<derived_probe *> & finished_results)
+ {
+ finished_results.push_back(new never_derived_probe(base, location));
+ }
+};
+
+
+
+// ------------------------------------------------------------------------
+// unified registration for begin/end/error/never
+// ------------------------------------------------------------------------
+
+void
+register_tapset_been(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+
+ root->bind(TOK_BEGIN)->bind(new be_builder(BEGIN));
+ root->bind_num(TOK_BEGIN)->bind(new be_builder(BEGIN));
+ root->bind(TOK_END)->bind(new be_builder(END));
+ root->bind_num(TOK_END)->bind(new be_builder(END));
+ root->bind(TOK_ERROR)->bind(new be_builder(ERROR));
+ root->bind_num(TOK_ERROR)->bind(new be_builder(ERROR));
+
+ root->bind(TOK_NEVER)->bind(new never_builder());
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapset-itrace.cxx b/tapset-itrace.cxx
new file mode 100644
index 00000000..38304a98
--- /dev/null
+++ b/tapset-itrace.cxx
@@ -0,0 +1,337 @@
+// tapset for timers
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+
+#include "session.h"
+#include "tapsets.h"
+#include "task_finder.h"
+#include "translate.h"
+#include "util.h"
+
+#include <cstring>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_PROCESS("process");
+static string TOK_INSN("insn");
+static string TOK_BLOCK("block");
+
+
+// ------------------------------------------------------------------------
+// itrace user-space probes
+// ------------------------------------------------------------------------
+
+
+struct itrace_derived_probe: public derived_probe
+{
+ bool has_path;
+ string path;
+ int64_t pid;
+ int single_step;
+
+ itrace_derived_probe (systemtap_session &s, probe* p, probe_point* l,
+ bool hp, string &pn, int64_t pd, int ss
+ );
+ void join_group (systemtap_session& s);
+};
+
+
+struct itrace_derived_probe_group: public generic_dpg<itrace_derived_probe>
+{
+private:
+ map<string, vector<itrace_derived_probe*> > probes_by_path;
+ typedef map<string, vector<itrace_derived_probe*> >::iterator p_b_path_iterator;
+ map<int64_t, vector<itrace_derived_probe*> > probes_by_pid;
+ typedef map<int64_t, vector<itrace_derived_probe*> >::iterator p_b_pid_iterator;
+ unsigned num_probes;
+
+ void emit_probe_decl (systemtap_session& s, itrace_derived_probe *p);
+
+public:
+ itrace_derived_probe_group(): num_probes(0) { }
+
+ void enroll (itrace_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+itrace_derived_probe::itrace_derived_probe (systemtap_session &s,
+ probe* p, probe_point* l,
+ bool hp, string &pn, int64_t pd,
+ int ss
+ ):
+ derived_probe(p, l), has_path(hp), path(pn), pid(pd), single_step(ss)
+{
+}
+
+
+void
+itrace_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.itrace_derived_probes)
+ s.itrace_derived_probes = new itrace_derived_probe_group ();
+
+ s.itrace_derived_probes->enroll (this);
+
+ enable_task_finder(s);
+}
+
+struct itrace_builder: public derived_probe_builder
+{
+ itrace_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ std::map<std::string, literal *> const & parameters,
+ vector<derived_probe *> & finished_results)
+ {
+ string path;
+ int64_t pid = 0;
+ int single_step;
+
+ bool has_path = get_param (parameters, TOK_PROCESS, path);
+ bool has_pid = get_param (parameters, TOK_PROCESS, pid);
+ // XXX: PR 6445 needs !has_path && !has_pid support
+ assert (has_path || has_pid);
+
+ single_step = ! has_null_param (parameters, TOK_BLOCK);
+
+ // If we have a path, we need to validate it.
+ if (has_path)
+ path = find_executable (path);
+
+ finished_results.push_back(new itrace_derived_probe(sess, base, location,
+ has_path, path, pid,
+ single_step
+ ));
+ }
+};
+
+
+void
+itrace_derived_probe_group::enroll (itrace_derived_probe* p)
+{
+ if (p->has_path)
+ probes_by_path[p->path].push_back(p);
+ else
+ probes_by_pid[p->pid].push_back(p);
+ num_probes++;
+
+ // XXX: multiple exec probes (for instance) for the same path (or
+ // pid) should all share a itrace report function, and have their
+ // handlers executed sequentially.
+}
+
+
+void
+itrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
+ itrace_derived_probe *p)
+{
+ s.op->newline() << "{";
+ s.op->line() << " .tgt={";
+
+ if (p->has_path)
+ {
+ s.op->line() << " .pathname=\"" << p->path << "\",";
+ s.op->line() << " .pid=0,";
+ }
+ else
+ {
+ s.op->line() << " .pathname=NULL,";
+ s.op->line() << " .pid=" << p->pid << ",";
+ }
+
+ s.op->line() << " .callback=&_stp_itrace_probe_cb,";
+ s.op->line() << " },";
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .single_step=" << p->single_step << ",";
+ s.op->line() << " .ph=&" << p->name << ",";
+
+ s.op->line() << " },";
+}
+
+
+void
+itrace_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty())
+ return;
+
+ s.op->newline();
+ s.op->newline() << "/* ---- itrace probes ---- */";
+
+ s.op->newline() << "struct stap_itrace_probe {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_task_finder_target tgt;";
+ s.op->newline() << "const char *pp;";
+ s.op->newline() << "void (*ph) (struct context*);";
+ s.op->newline() << "int single_step;";
+ s.op->newline(-1) << "};";
+ s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data);";
+ s.op->newline() << "#include \"itrace.c\"";
+
+ // output routine to call itrace probe
+ s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data) {";
+ s.op->indent(1);
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "c->data = data;";
+
+ // call probe function
+ s.op->newline() << "(*p->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return;";
+ s.op->newline(-1) << "}";
+
+ // Output task finder callback routine that gets called for all
+ // itrace probe types.
+ s.op->newline() << "static int _stp_itrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {";
+ s.op->indent(1);
+ s.op->newline() << "int rc = 0;";
+ s.op->newline() << "struct stap_itrace_probe *p = container_of(tgt, struct stap_itrace_probe, tgt);";
+
+ s.op->newline() << "if (register_p) ";
+ s.op->indent(1);
+
+ s.op->newline() << "rc = usr_itrace_init(p->single_step, tsk->pid, p);";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "remove_usr_itrace_info(find_itrace_info(p->tgt.pid));";
+ s.op->newline(-1) << "return rc;";
+ s.op->newline(-1) << "}";
+
+ // Emit vma callbacks.
+ s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
+ s.op->newline() << "static struct stap_task_finder_target stap_itrace_vmcbs[] = {";
+ s.op->indent(1);
+ if (! probes_by_path.empty())
+ {
+ for (p_b_path_iterator it = probes_by_path.begin();
+ it != probes_by_path.end(); it++)
+ emit_vma_callback_probe_decl (s, it->first, (int64_t)0);
+ }
+ if (! probes_by_pid.empty())
+ {
+ for (p_b_pid_iterator it = probes_by_pid.begin();
+ it != probes_by_pid.end(); it++)
+ emit_vma_callback_probe_decl (s, "", it->first);
+ }
+ s.op->newline(-1) << "};";
+ s.op->newline() << "#endif";
+
+ s.op->newline() << "static struct stap_itrace_probe stap_itrace_probes[] = {";
+ s.op->indent(1);
+
+ // Set up 'process(PATH)' probes
+ if (! probes_by_path.empty())
+ {
+ for (p_b_path_iterator it = probes_by_path.begin();
+ it != probes_by_path.end(); it++)
+ {
+ for (unsigned i = 0; i < it->second.size(); i++)
+ {
+ itrace_derived_probe *p = it->second[i];
+ emit_probe_decl(s, p);
+ }
+ }
+ }
+
+ // Set up 'process(PID)' probes
+ if (! probes_by_pid.empty())
+ {
+ for (p_b_pid_iterator it = probes_by_pid.begin();
+ it != probes_by_pid.end(); it++)
+ {
+ for (unsigned i = 0; i < it->second.size(); i++)
+ {
+ itrace_derived_probe *p = it->second[i];
+ emit_probe_decl(s, p);
+ }
+ }
+ }
+ s.op->newline(-1) << "};";
+}
+
+
+void
+itrace_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty())
+ return;
+
+ s.op->newline();
+ s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
+ s.op->newline() << "_stp_sym_init();";
+ s.op->newline() << "/* ---- itrace vma callbacks ---- */";
+ s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_itrace_vmcbs); i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_task_finder_target *r = &stap_itrace_vmcbs[i];";
+ s.op->newline() << "rc = stap_register_task_finder_target(r);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "#endif";
+
+ s.op->newline();
+ s.op->newline() << "/* ---- itrace probes ---- */";
+
+ s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_itrace_probe *p = &stap_itrace_probes[i];";
+
+ // 'arch_has_single_step' needs to be defined for either single step mode
+ // or branch mode.
+ s.op->newline() << "if (!arch_has_single_step()) {";
+ s.op->indent(1);
+ s.op->newline() << "_stp_error (\"insn probe init: arch does not support step mode\");";
+ s.op->newline() << "rc = -EPERM;";
+ s.op->newline() << "break;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "if (!p->single_step && !arch_has_block_step()) {";
+ s.op->indent(1);
+ s.op->newline() << "_stp_error (\"insn probe init: arch does not support block step mode\");";
+ s.op->newline() << "rc = -EPERM;";
+ s.op->newline() << "break;";
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+itrace_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty()) return;
+ s.op->newline();
+ s.op->newline() << "/* ---- itrace probes ---- */";
+ s.op->newline() << "cleanup_usr_itrace();";
+}
+
+void
+register_tapset_itrace(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+ derived_probe_builder *builder = new itrace_builder();
+
+ root->bind_str(TOK_PROCESS)->bind(TOK_INSN)->bind(builder);
+ root->bind_num(TOK_PROCESS)->bind(TOK_INSN)->bind(builder);
+ root->bind_str(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK)->bind(builder);
+ root->bind_num(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK)->bind(builder);
+}
+
+
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapset-mark.cxx b/tapset-mark.cxx
new file mode 100644
index 00000000..1f0ef2ce
--- /dev/null
+++ b/tapset-mark.cxx
@@ -0,0 +1,712 @@
+// tapset for kernel static markers
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#include "session.h"
+#include "tapsets.h"
+#include "translate.h"
+#include "util.h"
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+extern "C" {
+#include <fnmatch.h>
+}
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_KERNEL("kernel");
+static string TOK_MARK("mark");
+static string TOK_FORMAT("format");
+
+
+// ------------------------------------------------------------------------
+// statically inserted macro-based derived probes
+// ------------------------------------------------------------------------
+
+struct mark_arg
+{
+ bool str;
+ string c_type;
+ exp_type stp_type;
+};
+
+struct mark_derived_probe: public derived_probe
+{
+ mark_derived_probe (systemtap_session &s,
+ const string& probe_name, const string& probe_format,
+ probe* base_probe, probe_point* location);
+
+ systemtap_session& sess;
+ string probe_name, probe_format;
+ vector <struct mark_arg *> mark_args;
+ bool target_symbol_seen;
+
+ void join_group (systemtap_session& s);
+ void print_dupe_stamp (ostream& o);
+ void emit_probe_context_vars (translator_output* o);
+ void initialize_probe_context_vars (translator_output* o);
+ void printargs (std::ostream &o) const;
+
+ void parse_probe_format ();
+};
+
+
+struct mark_derived_probe_group: public generic_dpg<mark_derived_probe>
+{
+public:
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+struct mark_var_expanding_visitor: public var_expanding_visitor
+{
+ mark_var_expanding_visitor(systemtap_session& s, const string& pn,
+ vector <struct mark_arg *> &mark_args):
+ sess (s), probe_name (pn), mark_args (mark_args),
+ target_symbol_seen (false) {}
+ systemtap_session& sess;
+ string probe_name;
+ vector <struct mark_arg *> &mark_args;
+ bool target_symbol_seen;
+
+ void visit_target_symbol (target_symbol* e);
+ void visit_target_symbol_arg (target_symbol* e);
+ void visit_target_symbol_context (target_symbol* e);
+};
+
+
+void
+mark_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
+{
+ string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
+ int argnum = atoi (argnum_s.c_str());
+
+ if (argnum < 1 || argnum > (int)mark_args.size())
+ throw semantic_error ("invalid marker argument number", e->tok);
+
+ if (is_active_lvalue (e))
+ throw semantic_error("write to marker parameter not permitted", e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("marker argument may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("marker argument may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid marker argument use", e->tok);
+ break;
+ }
+ }
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+
+ e->probe_context_var = "__mark_arg" + lex_cast<string>(argnum);
+ e->type = mark_args[argnum-1]->stp_type;
+ provide (e);
+}
+
+
+void
+mark_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
+{
+ string sname = e->base_name;
+
+ if (is_active_lvalue (e))
+ throw semantic_error("write to marker '" + sname + "' not permitted", e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("marker '" + sname + "' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("marker '" + sname + "' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid marker '" + sname + "' use", e->tok);
+ break;
+ }
+ }
+
+ string fname;
+ if (e->base_name == "$format") {
+ fname = string("_mark_format_get");
+ } else {
+ fname = string("_mark_name_get");
+ }
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+ provide (n);
+}
+
+void
+mark_var_expanding_visitor::visit_target_symbol (target_symbol* e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ if (e->base_name.substr(0,4) == "$arg")
+ visit_target_symbol_arg (e);
+ else if (e->base_name == "$format" || e->base_name == "$name")
+ visit_target_symbol_context (e);
+ else
+ throw semantic_error ("invalid target symbol for marker, $argN, $name or $format expected",
+ e->tok);
+}
+
+
+
+mark_derived_probe::mark_derived_probe (systemtap_session &s,
+ const string& p_n,
+ const string& p_f,
+ probe* base, probe_point* loc):
+ derived_probe (base, new probe_point(*loc) /* .components soon rewritten */),
+ sess (s), probe_name (p_n), probe_format (p_f),
+ target_symbol_seen (false)
+{
+ // create synthetic probe point name; preserve condition
+ vector<probe_point::component*> comps;
+ comps.push_back (new probe_point::component (TOK_KERNEL));
+ comps.push_back (new probe_point::component (TOK_MARK, new literal_string (probe_name)));
+ comps.push_back (new probe_point::component (TOK_FORMAT, new literal_string (probe_format)));
+ this->sole_location()->components = comps;
+
+ // expand the marker format
+ parse_probe_format();
+
+ // Now expand the local variables in the probe body
+ mark_var_expanding_visitor v (sess, name, mark_args);
+ this->body = v.require (this->body);
+ target_symbol_seen = v.target_symbol_seen;
+
+ if (sess.verbose > 2)
+ clog << "marker-based " << name << " mark=" << probe_name
+ << " fmt='" << probe_format << "'" << endl;
+}
+
+
+static int
+skip_atoi(const char **s)
+{
+ int i = 0;
+ while (isdigit(**s))
+ i = i * 10 + *((*s)++) - '0';
+ return i;
+}
+
+
+void
+mark_derived_probe::parse_probe_format()
+{
+ const char *fmt = probe_format.c_str();
+ int qualifier; // 'h', 'l', or 'L' for integer fields
+ mark_arg *arg;
+
+ for (; *fmt ; ++fmt)
+ {
+ if (*fmt != '%')
+ {
+ /* Skip text */
+ continue;
+ }
+
+repeat:
+ ++fmt;
+
+ // skip conversion flags (if present)
+ switch (*fmt)
+ {
+ case '-':
+ case '+':
+ case ' ':
+ case '#':
+ case '0':
+ goto repeat;
+ }
+
+ // skip minimum field witdh (if present)
+ if (isdigit(*fmt))
+ skip_atoi(&fmt);
+
+ // skip precision (if present)
+ if (*fmt == '.')
+ {
+ ++fmt;
+ if (isdigit(*fmt))
+ skip_atoi(&fmt);
+ }
+
+ // get the conversion qualifier (if present)
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
+ {
+ qualifier = *fmt;
+ ++fmt;
+ if (qualifier == 'l' && *fmt == 'l')
+ {
+ qualifier = 'L';
+ ++fmt;
+ }
+ }
+
+ // get the conversion type
+ switch (*fmt)
+ {
+ case 'c':
+ arg = new mark_arg;
+ arg->str = false;
+ arg->c_type = "int";
+ arg->stp_type = pe_long;
+ mark_args.push_back(arg);
+ continue;
+
+ case 's':
+ arg = new mark_arg;
+ arg->str = true;
+ arg->c_type = "char *";
+ arg->stp_type = pe_string;
+ mark_args.push_back(arg);
+ continue;
+
+ case 'p':
+ arg = new mark_arg;
+ arg->str = false;
+ // This should really be 'void *'. But, then we'll get a
+ // compile error when we assign the void pointer to an
+ // integer without a cast. So, we use 'long' instead, since
+ // it should have the same size as 'void *'.
+ arg->c_type = "long";
+ arg->stp_type = pe_long;
+ mark_args.push_back(arg);
+ continue;
+
+ case '%':
+ continue;
+
+ case 'o':
+ case 'X':
+ case 'x':
+ case 'd':
+ case 'i':
+ case 'u':
+ // fall through...
+ break;
+
+ default:
+ if (!*fmt)
+ --fmt;
+ continue;
+ }
+
+ arg = new mark_arg;
+ arg->str = false;
+ arg->stp_type = pe_long;
+ switch (qualifier)
+ {
+ case 'L':
+ arg->c_type = "long long";
+ break;
+
+ case 'l':
+ arg->c_type = "long";
+ break;
+
+ case 'h':
+ arg->c_type = "short";
+ break;
+
+ default:
+ arg->c_type = "int";
+ break;
+ }
+ mark_args.push_back(arg);
+ }
+}
+
+
+void
+mark_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.mark_derived_probes)
+ {
+ s.mark_derived_probes = new mark_derived_probe_group ();
+
+ // Make sure <linux/marker.h> is included early.
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = NULL;
+ ec->code = string("#if ! defined(CONFIG_MARKERS)\n")
+ + string("#error \"Need CONFIG_MARKERS!\"\n")
+ + string("#endif\n")
+ + string("#include <linux/marker.h>\n");
+
+ s.embeds.push_back(ec);
+ }
+ s.mark_derived_probes->enroll (this);
+}
+
+
+void
+mark_derived_probe::print_dupe_stamp (ostream& o)
+{
+ if (target_symbol_seen)
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ o << mark_args[i]->c_type << " __mark_arg" << (i+1) << endl;
+}
+
+
+void
+mark_derived_probe::emit_probe_context_vars (translator_output* o)
+{
+ // If we haven't seen a target symbol for this probe, quit.
+ if (! target_symbol_seen)
+ return;
+
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ {
+ string localname = "__mark_arg" + lex_cast<string>(i+1);
+ switch (mark_args[i]->stp_type)
+ {
+ case pe_long:
+ o->newline() << "int64_t " << localname << ";";
+ break;
+ case pe_string:
+ o->newline() << "string_t " << localname << ";";
+ break;
+ default:
+ throw semantic_error ("cannot expand unknown type");
+ break;
+ }
+ }
+}
+
+
+void
+mark_derived_probe::initialize_probe_context_vars (translator_output* o)
+{
+ // If we haven't seen a target symbol for this probe, quit.
+ if (! target_symbol_seen)
+ return;
+
+ bool deref_fault_needed = false;
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ {
+ string localname = "l->__mark_arg" + lex_cast<string>(i+1);
+ switch (mark_args[i]->stp_type)
+ {
+ case pe_long:
+ o->newline() << localname << " = va_arg(*c->mark_va_list, "
+ << mark_args[i]->c_type << ");";
+ break;
+
+ case pe_string:
+ // We're assuming that this is a kernel string (this code is
+ // basically the guts of kernel_string), not a user string.
+ o->newline() << "{ " << mark_args[i]->c_type
+ << " tmp_str = va_arg(*c->mark_va_list, "
+ << mark_args[i]->c_type << ");";
+ o->newline() << "deref_string (" << localname
+ << ", tmp_str, MAXSTRINGLEN); }";
+ deref_fault_needed = true;
+ break;
+
+ default:
+ throw semantic_error ("cannot expand unknown type");
+ break;
+ }
+ }
+ if (deref_fault_needed)
+ // Need to report errors?
+ o->newline() << "deref_fault: ;";
+}
+
+void
+mark_derived_probe::printargs(std::ostream &o) const
+{
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ {
+ string localname = "$arg" + lex_cast<string>(i+1);
+ switch (mark_args[i]->stp_type)
+ {
+ case pe_long:
+ o << " " << localname << ":long";
+ break;
+ case pe_string:
+ o << " " << localname << ":string";
+ break;
+ default:
+ o << " " << localname << ":unknown";
+ break;
+ }
+ }
+}
+
+
+void
+mark_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes.empty())
+ return;
+
+ s.op->newline() << "/* ---- marker probes ---- */";
+
+ s.op->newline() << "static struct stap_marker_probe {";
+ s.op->newline(1) << "const char * const name;";
+ s.op->newline() << "const char * const format;";
+ s.op->newline() << "const char * const pp;";
+ s.op->newline() << "void (* const ph) (struct context *);";
+
+ s.op->newline(-1) << "} stap_marker_probes [" << probes.size() << "] = {";
+ s.op->indent(1);
+ for (unsigned i=0; i < probes.size(); i++)
+ {
+ s.op->newline () << "{";
+ s.op->line() << " .name=" << lex_cast_qstring(probes[i]->probe_name)
+ << ",";
+ s.op->line() << " .format=" << lex_cast_qstring(probes[i]->probe_format)
+ << ",";
+ s.op->line() << " .pp=" << lex_cast_qstring (*probes[i]->sole_location())
+ << ",";
+ s.op->line() << " .ph=&" << probes[i]->name;
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+ s.op->newline();
+
+
+ // Emit the marker callback function
+ s.op->newline();
+ s.op->newline() << "static void enter_marker_probe (void *probe_data, void *call_data, const char *fmt, va_list *args) {";
+ s.op->newline(1) << "struct stap_marker_probe *smp = (struct stap_marker_probe *)probe_data;";
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "smp->pp");
+ s.op->newline() << "c->marker_name = smp->name;";
+ s.op->newline() << "c->marker_format = smp->format;";
+ s.op->newline() << "c->mark_va_list = args;";
+ s.op->newline() << "(*smp->ph) (c);";
+ s.op->newline() << "c->mark_va_list = NULL;";
+ s.op->newline() << "c->data = NULL;";
+
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline(-1) << "}";
+
+ return;
+}
+
+
+void
+mark_derived_probe_group::emit_module_init (systemtap_session &s)
+{
+ if (probes.size () == 0)
+ return;
+
+ s.op->newline() << "/* init marker probes */";
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];";
+ s.op->newline() << "probe_point = smp->pp;";
+ s.op->newline() << "rc = marker_probe_register(smp->name, smp->format, enter_marker_probe, smp);";
+ s.op->newline() << "if (rc) {";
+ s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; // partial rollback
+ s.op->newline(1) << "struct stap_marker_probe *smp2 = &stap_marker_probes[j];";
+ s.op->newline() << "marker_probe_unregister(smp2->name, enter_marker_probe, smp2);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "break;"; // don't attempt to register any more probes
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+void
+mark_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty())
+ return;
+
+ s.op->newline() << "/* deregister marker probes */";
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];";
+ s.op->newline() << "marker_probe_unregister(smp->name, enter_marker_probe, smp);";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+struct mark_builder: public derived_probe_builder
+{
+private:
+ bool cache_initialized;
+ typedef multimap<string, string> mark_cache_t;
+ typedef multimap<string, string>::const_iterator mark_cache_const_iterator_t;
+ typedef pair<mark_cache_const_iterator_t, mark_cache_const_iterator_t>
+ mark_cache_const_iterator_pair_t;
+ mark_cache_t mark_cache;
+
+public:
+ mark_builder(): cache_initialized(false) {}
+
+ void build_no_more (systemtap_session &s)
+ {
+ if (! mark_cache.empty())
+ {
+ if (s.verbose > 3)
+ clog << "mark_builder releasing cache" << endl;
+ mark_cache.clear();
+ }
+ }
+
+ void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+
+void
+mark_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point *loc,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string mark_str_val;
+ bool has_mark_str = get_param (parameters, TOK_MARK, mark_str_val);
+ string mark_format_val;
+ bool has_mark_format = get_param (parameters, TOK_FORMAT, mark_format_val);
+ assert (has_mark_str);
+ (void) has_mark_str;
+
+ if (! cache_initialized)
+ {
+ cache_initialized = true;
+ string module_markers_path = sess.kernel_build_tree + "/Module.markers";
+
+ ifstream module_markers;
+ module_markers.open(module_markers_path.c_str(), ifstream::in);
+ if (! module_markers)
+ {
+ if (sess.verbose>3)
+ clog << module_markers_path << " cannot be opened: "
+ << strerror(errno) << endl;
+ return;
+ }
+
+ string name, module, format;
+ do
+ {
+ module_markers >> name >> module;
+ getline(module_markers, format);
+
+ // trim leading whitespace
+ string::size_type notwhite = format.find_first_not_of(" \t");
+ format.erase(0, notwhite);
+
+ // If the format is empty, make sure we add back a space
+ // character, which is what MARK_NOARGS expands to.
+ if (format.length() == 0)
+ format = " ";
+
+ if (sess.verbose>3)
+ clog << "'" << name << "' '" << module << "' '" << format
+ << "'" << endl;
+
+ if (mark_cache.count(name) > 0)
+ {
+ // If we have 2 markers with the same we've got 2 cases:
+ // different format strings or duplicate format strings.
+ // If an existing marker in the cache doesn't have the
+ // same format string, add this marker.
+ mark_cache_const_iterator_pair_t ret;
+ mark_cache_const_iterator_t it;
+ bool matching_format_string = false;
+
+ ret = mark_cache.equal_range(name);
+ for (it = ret.first; it != ret.second; ++it)
+ {
+ if (format == it->second)
+ {
+ matching_format_string = true;
+ break;
+ }
+ }
+
+ if (! matching_format_string)
+ mark_cache.insert(pair<string,string>(name, format));
+ }
+ else
+ mark_cache.insert(pair<string,string>(name, format));
+ }
+ while (! module_markers.eof());
+ module_markers.close();
+ }
+
+ // Search marker list for matching markers
+ for (mark_cache_const_iterator_t it = mark_cache.begin();
+ it != mark_cache.end(); it++)
+ {
+ // Below, "rc" has negative polarity: zero iff matching.
+ int rc = fnmatch(mark_str_val.c_str(), it->first.c_str(), 0);
+ if (! rc)
+ {
+ bool add_result = true;
+
+ // Match format strings (if the user specified one)
+ if (has_mark_format && fnmatch(mark_format_val.c_str(),
+ it->second.c_str(), 0))
+ add_result = false;
+
+ if (add_result)
+ {
+ derived_probe *dp
+ = new mark_derived_probe (sess,
+ it->first, it->second,
+ base, loc);
+ finished_results.push_back (dp);
+ }
+ }
+ }
+}
+
+
+
+void
+register_tapset_mark(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+ derived_probe_builder *builder = new mark_builder();
+
+ root = root->bind(TOK_KERNEL);
+ root = root->bind_str(TOK_MARK);
+
+ root->bind(builder);
+ root->bind_str(TOK_FORMAT)->bind(builder);
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapset-perfmon.cxx b/tapset-perfmon.cxx
new file mode 100644
index 00000000..e3f30ece
--- /dev/null
+++ b/tapset-perfmon.cxx
@@ -0,0 +1,463 @@
+// tapset for HW performance monitoring
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#include "session.h"
+#include "tapsets.h"
+#include "util.h"
+
+#include <string>
+
+#ifdef PERFMON
+#include <perfmon/pfmlib.h>
+#include <perfmon/perfmon.h>
+#endif
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+
+// ------------------------------------------------------------------------
+// perfmon derived probes
+// ------------------------------------------------------------------------
+// This is a new interface to the perfmon hw.
+//
+
+
+struct perfmon_var_expanding_visitor: public var_expanding_visitor
+{
+ systemtap_session & sess;
+ unsigned counter_number;
+ perfmon_var_expanding_visitor(systemtap_session & s, unsigned c):
+ sess(s), counter_number(c) {}
+ void visit_target_symbol (target_symbol* e);
+};
+
+
+void
+perfmon_var_expanding_visitor::visit_target_symbol (target_symbol *e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ // Synthesize a function.
+ functiondecl *fdecl = new functiondecl;
+ fdecl->tok = e->tok;
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = e->tok;
+ bool lvalue = is_active_lvalue(e);
+
+ if (lvalue )
+ throw semantic_error("writes to $counter not permitted");
+
+ string fname = string("_perfmon_tvar_get")
+ + "_" + e->base_name.substr(1)
+ + "_" + lex_cast<string>(counter_number);
+
+ if (e->base_name != "$counter")
+ throw semantic_error ("target variables not available to perfmon probes");
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("perfmon probe '$counter' variable may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("perfmon probe '$counter' variable may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid use of perfmon probe '$counter' variable",
+ e->tok);
+ break;
+ }
+ }
+
+ ec->code = "THIS->__retvalue = _pfm_pmd_x[" +
+ lex_cast<string>(counter_number) + "].reg_num;";
+ ec->code += "/* pure */";
+ fdecl->name = fname;
+ fdecl->body = ec;
+ fdecl->type = pe_long;
+ sess.functions[fdecl->name]=fdecl;
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ provide (n);
+}
+
+
+enum perfmon_mode
+{
+ perfmon_count,
+ perfmon_sample
+};
+
+
+struct perfmon_derived_probe: public derived_probe
+{
+protected:
+ static unsigned probes_allocated;
+
+public:
+ systemtap_session & sess;
+ string event;
+ perfmon_mode mode;
+
+ perfmon_derived_probe (probe* p, probe_point* l, systemtap_session &s,
+ string e, perfmon_mode m);
+ virtual void join_group (systemtap_session& s);
+};
+
+
+struct perfmon_derived_probe_group: public generic_dpg<perfmon_derived_probe>
+{
+public:
+ void emit_module_decls (systemtap_session&) {}
+ void emit_module_init (systemtap_session&) {}
+ void emit_module_exit (systemtap_session&) {}
+};
+
+
+struct perfmon_builder: public derived_probe_builder
+{
+ perfmon_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+ {
+ string event;
+ if (!get_param (parameters, "counter", event))
+ throw semantic_error("perfmon requires an event");
+
+ sess.perfmon++;
+
+ // XXX: need to revise when doing sampling
+ finished_results.push_back(new perfmon_derived_probe(base, location,
+ sess, event,
+ perfmon_count));
+ }
+};
+
+
+unsigned perfmon_derived_probe::probes_allocated;
+
+perfmon_derived_probe::perfmon_derived_probe (probe* p, probe_point* l,
+ systemtap_session &s,
+ string e, perfmon_mode m)
+ : derived_probe (p, l), sess(s), event(e), mode(m)
+{
+ ++probes_allocated;
+
+ // Now expand the local variables in the probe body
+ perfmon_var_expanding_visitor v (sess, probes_allocated-1);
+ this->body = v.require (this->body);
+
+ if (sess.verbose > 1)
+ clog << "perfmon-based probe" << endl;
+}
+
+
+void
+perfmon_derived_probe::join_group (systemtap_session& s)
+{
+ throw semantic_error ("incomplete", this->tok);
+
+ if (! s.perfmon_derived_probes)
+ s.perfmon_derived_probes = new perfmon_derived_probe_group ();
+ s.perfmon_derived_probes->enroll (this);
+}
+
+
+#if 0
+void
+perfmon_derived_probe::emit_registrations_start (translator_output* o,
+ unsigned index)
+{
+ for (unsigned i=0; i<locations.size(); i++)
+ o->newline() << "enter_" << name << "_" << i << " ();";
+}
+
+
+void
+perfmon_derived_probe::emit_registrations_end (translator_output * o,
+ unsigned index)
+{
+}
+
+
+void
+perfmon_derived_probe::emit_deregistrations (translator_output * o)
+{
+}
+
+
+void
+perfmon_derived_probe::emit_probe_entries (translator_output * o)
+{
+ o->newline() << "#ifdef STP_TIMING";
+ // NB: This variable may be multiply (but identically) defined.
+ o->newline() << "static __cacheline_aligned Stat " << "time_" << basest()->name << ";";
+ o->newline() << "#endif";
+
+ for (unsigned i=0; i<locations.size(); i++)
+ {
+ probe_point *l = locations[i];
+ o->newline() << "/* location " << i << ": " << *l << " */";
+ o->newline() << "static void enter_" << name << "_" << i << " (void) {";
+
+ o->indent(1);
+ o->newline() << "const char* probe_point = "
+ << lex_cast_qstring(*l) << ";";
+
+ o->newline() << "static struct pfarg_ctx _pfm_context;";
+ o->newline() << "static void *_pfm_desc;";
+ o->newline() << "static struct pfarg_pmc *_pfm_pmc_x;";
+ o->newline() << "static int _pfm_num_pmc_x;";
+ o->newline() << "static struct pfarg_pmd *_pfm_pmd_x;";
+ o->newline() << "static int _pfm_num_pmd_x;";
+
+ emit_probe_prologue (o,
+ (mode == perfmon_count ?
+ "STAP_SESSION_STARTING" :
+ "STAP_SESSION_RUNNING"),
+ "probe_point");
+
+ // NB: locals are initialized by probe function itself
+ o->newline() << name << " (c);";
+
+ emit_probe_epilogue (o);
+
+ o->newline(-1) << "}\n";
+ }
+}
+#endif
+
+
+#if 0
+void no_pfm_event_error (string s)
+{
+ string msg(string("Cannot find event:" + s));
+ throw semantic_error(msg);
+}
+
+
+void no_pfm_mask_error (string s)
+{
+ string msg(string("Cannot find mask:" + s));
+ throw semantic_error(msg);
+}
+
+
+void
+split(const string& s, vector<string>& v, const string & separator)
+{
+ string::size_type last_pos = s.find_first_not_of(separator, 0);
+ string::size_type pos = s.find_first_of(separator, last_pos);
+
+ while (string::npos != pos || string::npos != last_pos) {
+ v.push_back(s.substr(last_pos, pos - last_pos));
+ last_pos = s.find_first_not_of(separator, pos);
+ pos = s.find_first_of(separator, last_pos);
+ }
+}
+
+
+void
+perfmon_derived_probe_group::emit_probes (translator_output* op, unparser* up)
+{
+ for (unsigned i=0; i < probes.size(); i++)
+ {
+ op->newline ();
+ up->emit_probe (probes[i]);
+ }
+}
+
+
+void
+perfmon_derived_probe_group::emit_module_init (translator_output* o)
+{
+ int ret;
+ pfmlib_input_param_t inp;
+ pfmlib_output_param_t outp;
+ pfarg_pmd_t pd[PFMLIB_MAX_PMDS];
+ pfarg_pmc_t pc[PFMLIB_MAX_PMCS];
+ pfarg_ctx_t ctx;
+ pfarg_load_t load_args;
+ pfmlib_options_t pfmlib_options;
+ unsigned int max_counters;
+
+ if ( probes.size() == 0)
+ return;
+ ret = pfm_initialize();
+ if (ret != PFMLIB_SUCCESS)
+ throw semantic_error("Unable to generate performance monitoring events (no libpfm)");
+
+ pfm_get_num_counters(&max_counters);
+
+ memset(&pfmlib_options, 0, sizeof(pfmlib_options));
+ pfmlib_options.pfm_debug = 0; /* set to 1 for debug */
+ pfmlib_options.pfm_verbose = 0; /* set to 1 for debug */
+ pfm_set_options(&pfmlib_options);
+
+ memset(pd, 0, sizeof(pd));
+ memset(pc, 0, sizeof(pc));
+ memset(&ctx, 0, sizeof(ctx));
+ memset(&load_args, 0, sizeof(load_args));
+
+ /*
+ * prepare parameters to library.
+ */
+ memset(&inp,0, sizeof(inp));
+ memset(&outp,0, sizeof(outp));
+
+ /* figure out the events */
+ for (unsigned i=0; i<probes.size(); ++i)
+ {
+ if (probes[i]->event == "cycles") {
+ if (pfm_get_cycle_event( &inp.pfp_events[i].event) != PFMLIB_SUCCESS)
+ no_pfm_event_error(probes[i]->event);
+ } else if (probes[i]->event == "instructions") {
+ if (pfm_get_inst_retired_event( &inp.pfp_events[i].event) !=
+ PFMLIB_SUCCESS)
+ no_pfm_event_error(probes[i]->event);
+ } else {
+ unsigned int event_id = 0;
+ unsigned int mask_id = 0;
+ vector<string> event_spec;
+ split(probes[i]->event, event_spec, ":");
+ int num = event_spec.size();
+ int masks = num - 1;
+
+ if (num == 0)
+ throw semantic_error("No events found");
+
+ /* setup event */
+ if (pfm_find_event(event_spec[0].c_str(), &event_id) != PFMLIB_SUCCESS)
+ no_pfm_event_error(event_spec[0]);
+ inp.pfp_events[i].event = event_id;
+
+ /* set up masks */
+ if (masks > PFMLIB_MAX_MASKS_PER_EVENT)
+ throw semantic_error("Too many unit masks specified");
+
+ for (int j=0; j < masks; j++) {
+ if (pfm_find_event_mask(event_id, event_spec[j+1].c_str(),
+ &mask_id) != PFMLIB_SUCCESS)
+ no_pfm_mask_error(string(event_spec[j+1]));
+ inp.pfp_events[i].unit_masks[j] = mask_id;
+ }
+ inp.pfp_events[i].num_masks = masks;
+ }
+ }
+
+ /* number of counters in use */
+ inp.pfp_event_count = probes.size();
+
+ // XXX: no elimination of duplicated counters
+ if (inp.pfp_event_count>max_counters)
+ throw semantic_error("Too many performance monitoring events.");
+
+ /* count events both in kernel and user-space */
+ inp.pfp_dfl_plm = PFM_PLM0 | PFM_PLM3;
+
+ /* XXX: some cases a perfmon register might be used of watch dog
+ this code doesn't handle that case */
+
+ /* figure out the pmcs for the events */
+ if ((ret=pfm_dispatch_events(&inp, NULL, &outp, NULL)) != PFMLIB_SUCCESS)
+ throw semantic_error("Cannot configure events");
+
+ for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
+ pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
+ pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
+ }
+
+ /*
+ * There could be more pmc settings than pmd.
+ * Figure out the actual pmds to use.
+ */
+ for (unsigned i=0, j=0; i < inp.pfp_event_count; i++) {
+ pd[i].reg_num = outp.pfp_pmcs[j].reg_pmd_num;
+ for(; j < outp.pfp_pmc_count; j++)
+ if (outp.pfp_pmcs[j].reg_evt_idx != i) break;
+ }
+
+ // Output the be probes create function
+ o->newline() << "static int register_perfmon_probes (void) {";
+ o->newline(1) << "int rc = 0;";
+
+ o->newline() << "/* data for perfmon */";
+ o->newline() << "static int _pfm_num_pmc = " << outp.pfp_pmc_count << ";";
+ o->newline() << "static struct pfarg_pmc _pfm_pmc[" << outp.pfp_pmc_count
+ << "] = {";
+ /* output the needed bits for pmc here */
+ for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
+ o->newline() << "{.reg_num=" << pc[i].reg_num << ", "
+ << ".reg_value=" << lex_cast_hex<string>(pc[i].reg_value)
+ << "},";
+ }
+
+ o->newline() << "};";
+ o->newline() << "static int _pfm_num_pmd = " << inp.pfp_event_count << ";";
+ o->newline() << "static struct pfarg_pmd _pfm_pmd[" << inp.pfp_event_count
+ << "] = {";
+ /* output the needed bits for pmd here */
+ for (unsigned i=0; i < inp.pfp_event_count; i++) {
+ o->newline() << "{.reg_num=" << pd[i].reg_num << ", "
+ << ".reg_value=" << pd[i].reg_value << "},";
+ }
+ o->newline() << "};";
+ o->newline();
+
+ o->newline() << "_pfm_pmc_x=_pfm_pmc;";
+ o->newline() << "_pfm_num_pmc_x=_pfm_num_pmc;";
+ o->newline() << "_pfm_pmd_x=_pfm_pmd;";
+ o->newline() << "_pfm_num_pmd_x=_pfm_num_pmd;";
+
+ // call all the function bodies associated with perfcounters
+ for (unsigned i=0; i < probes.size (); i++)
+ probes[i]->emit_registrations_start (o,i);
+
+ /* generate call to turn on instrumentation */
+ o->newline() << "_pfm_context.ctx_flags |= PFM_FL_SYSTEM_WIDE;";
+ o->newline() << "rc = rc || _stp_perfmon_setup(&_pfm_desc, &_pfm_context,";
+ o->newline(1) << "_pfm_pmc, _pfm_num_pmc,";
+ o->newline() << "_pfm_pmd, _pfm_num_pmd);";
+ o->newline(-1);
+
+ o->newline() << "return rc;";
+ o->newline(-1) << "}\n";
+
+ // Output the be probes destroy function
+ o->newline() << "static void unregister_perfmon_probes (void) {";
+ o->newline(1) << "_stp_perfmon_shutdown(_pfm_desc);";
+ o->newline(-1) << "}\n";
+}
+#endif
+
+
+void
+register_tapset_perfmon(systemtap_session& s)
+{
+ s.pattern_root->bind("perfmon")->bind_str("counter")
+ ->bind(new perfmon_builder());
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapset-procfs.cxx b/tapset-procfs.cxx
new file mode 100644
index 00000000..3568c3d3
--- /dev/null
+++ b/tapset-procfs.cxx
@@ -0,0 +1,521 @@
+// tapset for procfs
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+
+#include "session.h"
+#include "tapsets.h"
+#include "translate.h"
+#include "util.h"
+
+#include <cstring>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_PROCFS("procfs");
+static string TOK_READ("read");
+static string TOK_WRITE("write");
+
+
+// ------------------------------------------------------------------------
+// procfs file derived probes
+// ------------------------------------------------------------------------
+
+
+struct procfs_derived_probe: public derived_probe
+{
+ string path;
+ bool write;
+ bool target_symbol_seen;
+
+ procfs_derived_probe (systemtap_session &, probe* p, probe_point* l, string ps, bool w);
+ void join_group (systemtap_session& s);
+};
+
+
+struct procfs_probe_set
+{
+ procfs_derived_probe* read_probe;
+ procfs_derived_probe* write_probe;
+
+ procfs_probe_set () : read_probe (NULL), write_probe (NULL) {}
+};
+
+
+struct procfs_derived_probe_group: public generic_dpg<procfs_derived_probe>
+{
+private:
+ map<string, procfs_probe_set*> probes_by_path;
+ typedef map<string, procfs_probe_set*>::iterator p_b_p_iterator;
+ bool has_read_probes;
+ bool has_write_probes;
+
+public:
+ procfs_derived_probe_group () :
+ has_read_probes(false), has_write_probes(false) {}
+
+ void enroll (procfs_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+struct procfs_var_expanding_visitor: public var_expanding_visitor
+{
+ procfs_var_expanding_visitor(systemtap_session& s, const string& pn,
+ string path, bool write_probe):
+ sess (s), probe_name (pn), path (path), write_probe (write_probe),
+ target_symbol_seen (false) {}
+
+ systemtap_session& sess;
+ string probe_name;
+ string path;
+ bool write_probe;
+ bool target_symbol_seen;
+
+ void visit_target_symbol (target_symbol* e);
+};
+
+
+procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p,
+ probe_point* l, string ps, bool w):
+ derived_probe(p, l), path(ps), write(w), target_symbol_seen(false)
+{
+ // Expand local variables in the probe body
+ procfs_var_expanding_visitor v (s, name, path, write);
+ this->body = v.require (this->body);
+ target_symbol_seen = v.target_symbol_seen;
+}
+
+
+void
+procfs_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.procfs_derived_probes)
+ s.procfs_derived_probes = new procfs_derived_probe_group ();
+ s.procfs_derived_probes->enroll (this);
+}
+
+
+void
+procfs_derived_probe_group::enroll (procfs_derived_probe* p)
+{
+ procfs_probe_set *pset;
+
+ if (probes_by_path.count(p->path) == 0)
+ {
+ pset = new procfs_probe_set;
+ probes_by_path[p->path] = pset;
+ }
+ else
+ {
+ pset = probes_by_path[p->path];
+
+ // You can only specify 1 read and 1 write probe.
+ if (p->write && pset->write_probe != NULL)
+ throw semantic_error("only one write procfs probe can exist for procfs path \"" + p->path + "\"");
+ else if (! p->write && pset->read_probe != NULL)
+ throw semantic_error("only one read procfs probe can exist for procfs path \"" + p->path + "\"");
+
+ // XXX: multiple writes should be acceptable
+ }
+
+ if (p->write)
+ {
+ pset->write_probe = p;
+ has_write_probes = true;
+ }
+ else
+ {
+ pset->read_probe = p;
+ has_read_probes = true;
+ }
+}
+
+
+void
+procfs_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_path.empty())
+ return;
+
+ s.op->newline() << "/* ---- procfs probes ---- */";
+ s.op->newline() << "#include \"procfs.c\"";
+
+ // Emit the procfs probe data list
+ s.op->newline() << "static struct stap_procfs_probe {";
+ s.op->newline(1)<< "const char *path;";
+ s.op->newline() << "const char *read_pp;";
+ s.op->newline() << "void (*read_ph) (struct context*);";
+ s.op->newline() << "const char *write_pp;";
+ s.op->newline() << "void (*write_ph) (struct context*);";
+ s.op->newline(-1) << "} stap_procfs_probes[] = {";
+ s.op->indent(1);
+
+ for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end();
+ it++)
+ {
+ procfs_probe_set *pset = it->second;
+
+ s.op->newline() << "{";
+ s.op->line() << " .path=" << lex_cast_qstring (it->first) << ",";
+
+ if (pset->read_probe != NULL)
+ {
+ s.op->line() << " .read_pp="
+ << lex_cast_qstring (*pset->read_probe->sole_location())
+ << ",";
+ s.op->line() << " .read_ph=&" << pset->read_probe->name << ",";
+ }
+ else
+ {
+ s.op->line() << " .read_pp=NULL,";
+ s.op->line() << " .read_ph=NULL,";
+ }
+
+ if (pset->write_probe != NULL)
+ {
+ s.op->line() << " .write_pp="
+ << lex_cast_qstring (*pset->write_probe->sole_location())
+ << ",";
+ s.op->line() << " .write_ph=&" << pset->write_probe->name;
+ }
+ else
+ {
+ s.op->line() << " .write_pp=NULL,";
+ s.op->line() << " .write_ph=NULL";
+ }
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+
+ if (has_read_probes)
+ {
+ // Output routine to fill in 'page' with our data.
+ s.op->newline();
+
+ s.op->newline() << "static int _stp_procfs_read(char *page, char **start, off_t off, int count, int *eof, void *data) {";
+
+ s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
+ s.op->newline() << "int bytes = 0;";
+ s.op->newline() << "string_t strdata = {'\\0'};";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->read_pp");
+
+ s.op->newline() << "if (c->data == NULL)";
+ s.op->newline(1) << "c->data = &strdata;";
+ s.op->newline(-1) << "else {";
+
+ s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
+ s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << "_stp_exit ();";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "atomic_dec (& c->busy);";
+ s.op->newline() << "goto probe_epilogue;";
+ s.op->newline(-1) << "}";
+
+ // call probe function (which copies data into strdata)
+ s.op->newline() << "(*spp->read_ph) (c);";
+
+ // copy string data into 'page'
+ s.op->newline() << "c->data = NULL;";
+ s.op->newline() << "bytes = strnlen(strdata, MAXSTRINGLEN - 1);";
+ s.op->newline() << "if (off >= bytes)";
+ s.op->newline(1) << "*eof = 1;";
+ s.op->newline(-1) << "else {";
+ s.op->newline(1) << "bytes -= off;";
+ s.op->newline() << "if (bytes > count)";
+ s.op->newline(1) << "bytes = count;";
+ s.op->newline(-1) << "memcpy(page, strdata + off, bytes);";
+ s.op->newline() << "*start = page;";
+ s.op->newline(-1) << "}";
+
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return bytes;";
+
+ s.op->newline(-1) << "}";
+ }
+ if (has_write_probes)
+ {
+ s.op->newline() << "static int _stp_procfs_write(struct file *file, const char *buffer, unsigned long count, void *data) {";
+
+ s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
+ s.op->newline() << "string_t strdata = {'\\0'};";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->write_pp");
+
+ s.op->newline() << "if (count > (MAXSTRINGLEN - 1))";
+ s.op->newline(1) << "count = MAXSTRINGLEN - 1;";
+ s.op->newline(-1) << "_stp_copy_from_user(strdata, buffer, count);";
+
+ s.op->newline() << "if (c->data == NULL)";
+ s.op->newline(1) << "c->data = &strdata;";
+ s.op->newline(-1) << "else {";
+
+ s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
+ s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << "_stp_exit ();";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "atomic_dec (& c->busy);";
+ s.op->newline() << "goto probe_epilogue;";
+ s.op->newline(-1) << "}";
+
+ // call probe function (which copies data out of strdata)
+ s.op->newline() << "(*spp->write_ph) (c);";
+
+ s.op->newline() << "c->data = NULL;";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return count;";
+ s.op->newline(-1) << "}";
+ }
+}
+
+
+void
+procfs_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes_by_path.empty())
+ return;
+
+ s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];";
+
+ s.op->newline() << "if (spp->read_pp)";
+ s.op->newline(1) << "probe_point = spp->read_pp;";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "probe_point = spp->write_pp;";
+
+ s.op->newline(-1) << "rc = _stp_create_procfs(spp->path, i);";
+
+ s.op->newline() << "if (rc) {";
+ s.op->newline(1) << "_stp_close_procfs();";
+ s.op->newline() << "break;";
+ s.op->newline(-1) << "}";
+
+ if (has_read_probes)
+ {
+ s.op->newline() << "if (spp->read_pp)";
+ s.op->newline(1) << "_stp_procfs_files[i]->read_proc = &_stp_procfs_read;";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "_stp_procfs_files[i]->read_proc = NULL;";
+ s.op->indent(-1);
+ }
+ else
+ s.op->newline() << "_stp_procfs_files[i]->read_proc = NULL;";
+
+ if (has_write_probes)
+ {
+ s.op->newline() << "if (spp->write_pp)";
+ s.op->newline(1) << "_stp_procfs_files[i]->write_proc = &_stp_procfs_write;";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "_stp_procfs_files[i]->write_proc = NULL;";
+ s.op->indent(-1);
+ }
+ else
+ s.op->newline() << "_stp_procfs_files[i]->write_proc = NULL;";
+
+ s.op->newline() << "_stp_procfs_files[i]->data = spp;";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+void
+procfs_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes_by_path.empty())
+ return;
+
+ s.op->newline() << "_stp_close_procfs();";
+}
+
+
+void
+procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ if (e->base_name != "$value")
+ throw semantic_error ("invalid target symbol for procfs probe, $value expected",
+ e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("procfs target variable '$value' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("procfs target variable '$value' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid use of procfs target variable '$value'",
+ e->tok);
+ break;
+ }
+ }
+
+ bool lvalue = is_active_lvalue(e);
+ if (write_probe && lvalue)
+ throw semantic_error("procfs $value variable is read-only in a procfs write probe", e->tok);
+ else if (! write_probe && ! lvalue)
+ throw semantic_error("procfs $value variable cannot be read in a procfs read probe", e->tok);
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+
+ // Synthesize a function.
+ functiondecl *fdecl = new functiondecl;
+ fdecl->tok = e->tok;
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = e->tok;
+
+ string fname = (string(lvalue ? "_procfs_value_set" : "_procfs_value_get")
+ + "_" + lex_cast<string>(tick++));
+ string locvalue = "CONTEXT->data";
+
+ if (! lvalue)
+ ec->code = string("strlcpy (THIS->__retvalue, ") + locvalue
+ + string(", MAXSTRINGLEN); /* pure */");
+ else
+ ec->code = string("strlcpy (") + locvalue
+ + string(", THIS->value, MAXSTRINGLEN);");
+
+ fdecl->name = fname;
+ fdecl->body = ec;
+ fdecl->type = pe_string;
+
+ if (lvalue)
+ {
+ // Modify the fdecl so it carries a single pe_string formal
+ // argument called "value".
+
+ vardecl *v = new vardecl;
+ v->type = pe_string;
+ v->name = "value";
+ v->tok = e->tok;
+ fdecl->formal_args.push_back(v);
+ }
+ sess.functions[fdecl->name]=fdecl;
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ if (lvalue)
+ {
+ // Provide the functioncall to our parent, so that it can be
+ // used to substitute for the assignment node immediately above
+ // us.
+ assert(!target_symbol_setter_functioncalls.empty());
+ *(target_symbol_setter_functioncalls.top()) = n;
+ }
+
+ provide (n);
+}
+
+
+struct procfs_builder: public derived_probe_builder
+{
+ procfs_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+
+void
+procfs_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string path;
+ bool has_procfs = get_param(parameters, TOK_PROCFS, path);
+ bool has_read = (parameters.find(TOK_READ) != parameters.end());
+ bool has_write = (parameters.find(TOK_WRITE) != parameters.end());
+
+ // If no procfs path, default to "command". The runtime will do
+ // this for us, but if we don't do it here, we'll think the
+ // following 2 probes are attached to different paths:
+ //
+ // probe procfs("command").read {}"
+ // probe procfs.write {}
+
+ if (! has_procfs)
+ path = "command";
+ // If we have a path, we need to validate it.
+ else
+ {
+ string::size_type start_pos, end_pos;
+ string component;
+ start_pos = 0;
+ while ((end_pos = path.find('/', start_pos)) != string::npos)
+ {
+ // Make sure it doesn't start with '/'.
+ if (end_pos == 0)
+ throw semantic_error ("procfs path cannot start with a '/'",
+ location->tok);
+
+ component = path.substr(start_pos, end_pos - start_pos);
+ // Make sure it isn't empty.
+ if (component.size() == 0)
+ throw semantic_error ("procfs path component cannot be empty",
+ location->tok);
+ // Make sure it isn't relative.
+ else if (component == "." || component == "..")
+ throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
+
+ start_pos = end_pos + 1;
+ }
+ component = path.substr(start_pos);
+ // Make sure it doesn't end with '/'.
+ if (component.size() == 0)
+ throw semantic_error ("procfs path cannot end with a '/'", location->tok);
+ // Make sure it isn't relative.
+ else if (component == "." || component == "..")
+ throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
+ }
+
+ if (!(has_read ^ has_write))
+ throw semantic_error ("need read/write component", location->tok);
+
+ finished_results.push_back(new procfs_derived_probe(sess, base, location,
+ path, has_write));
+}
+
+
+void
+register_tapset_procfs(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+ derived_probe_builder *builder = new procfs_builder();
+
+ root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(builder);
+ root->bind_str(TOK_PROCFS)->bind(TOK_READ)->bind(builder);
+ root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder);
+ root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder);
+}
+
+
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapset-timers.cxx b/tapset-timers.cxx
new file mode 100644
index 00000000..d32a22a6
--- /dev/null
+++ b/tapset-timers.cxx
@@ -0,0 +1,626 @@
+// tapset for timers
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+
+#include "session.h"
+#include "tapsets.h"
+#include "translate.h"
+#include "util.h"
+
+#include <cstring>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_TIMER("timer");
+
+
+// ------------------------------------------------------------------------
+// timer derived probes
+// ------------------------------------------------------------------------
+
+
+struct timer_derived_probe: public derived_probe
+{
+ int64_t interval, randomize;
+ bool time_is_msecs; // NB: hrtimers get ms-based probes on modern kernels instead
+ timer_derived_probe (probe* p, probe_point* l,
+ int64_t i, int64_t r, bool ms=false);
+ virtual void join_group (systemtap_session& s);
+};
+
+
+struct timer_derived_probe_group: public generic_dpg<timer_derived_probe>
+{
+ void emit_interval (translator_output* o);
+public:
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+timer_derived_probe::timer_derived_probe (probe* p, probe_point* l,
+ int64_t i, int64_t r, bool ms):
+ derived_probe (p, l), interval (i), randomize (r), time_is_msecs(ms)
+{
+ if (interval <= 0 || interval > 1000000) // make i and r fit into plain ints
+ throw semantic_error ("invalid interval for jiffies timer");
+ // randomize = 0 means no randomization
+ if (randomize < 0 || randomize > interval)
+ throw semantic_error ("invalid randomize for jiffies timer");
+
+ if (locations.size() != 1)
+ throw semantic_error ("expect single probe point");
+ // so we don't have to loop over them in the other functions
+}
+
+
+void
+timer_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.timer_derived_probes)
+ s.timer_derived_probes = new timer_derived_probe_group ();
+ s.timer_derived_probes->enroll (this);
+}
+
+
+void
+timer_derived_probe_group::emit_interval (translator_output* o)
+{
+ o->line() << "({";
+ o->newline(1) << "unsigned i = stp->intrv;";
+ o->newline() << "if (stp->rnd != 0)";
+ o->newline(1) << "i += _stp_random_pm(stp->rnd);";
+ o->newline(-1) << "stp->ms ? msecs_to_jiffies(i) : i;";
+ o->newline(-1) << "})";
+}
+
+
+void
+timer_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "/* ---- timer probes ---- */";
+
+ s.op->newline() << "static struct stap_timer_probe {";
+ s.op->newline(1) << "struct timer_list timer_list;";
+ s.op->newline() << "const char *pp;";
+ s.op->newline() << "void (*ph) (struct context*);";
+ s.op->newline() << "unsigned intrv, ms, rnd;";
+ s.op->newline(-1) << "} stap_timer_probes [" << probes.size() << "] = {";
+ s.op->indent(1);
+ for (unsigned i=0; i < probes.size(); i++)
+ {
+ s.op->newline () << "{";
+ s.op->line() << " .pp="
+ << lex_cast_qstring (*probes[i]->sole_location()) << ",";
+ s.op->line() << " .ph=&" << probes[i]->name << ",";
+ s.op->line() << " .intrv=" << probes[i]->interval << ",";
+ s.op->line() << " .ms=" << probes[i]->time_is_msecs << ",";
+ s.op->line() << " .rnd=" << probes[i]->randomize;
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+ s.op->newline();
+
+ s.op->newline() << "static void enter_timer_probe (unsigned long val) {";
+ s.op->newline(1) << "struct stap_timer_probe* stp = & stap_timer_probes [val];";
+ s.op->newline() << "if ((atomic_read (&session_state) == STAP_SESSION_STARTING) ||";
+ s.op->newline() << " (atomic_read (&session_state) == STAP_SESSION_RUNNING))";
+ s.op->newline(1) << "mod_timer (& stp->timer_list, jiffies + ";
+ emit_interval (s.op);
+ s.op->line() << ");";
+ s.op->newline(-1) << "{";
+ s.op->indent(1);
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "stp->pp");
+ s.op->newline() << "(*stp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+timer_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_timer_probe* stp = & stap_timer_probes [i];";
+ s.op->newline() << "probe_point = stp->pp;";
+ s.op->newline() << "init_timer (& stp->timer_list);";
+ s.op->newline() << "stp->timer_list.function = & enter_timer_probe;";
+ s.op->newline() << "stp->timer_list.data = i;"; // NB: important!
+ // copy timer renew calculations from above :-(
+ s.op->newline() << "stp->timer_list.expires = jiffies + ";
+ emit_interval (s.op);
+ s.op->line() << ";";
+ s.op->newline() << "add_timer (& stp->timer_list);";
+ // note: no partial failure rollback is needed: add_timer cannot fail.
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+void
+timer_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)";
+ s.op->newline(1) << "del_timer_sync (& stap_timer_probes[i].timer_list);";
+ s.op->indent(-1);
+}
+
+
+
+// ------------------------------------------------------------------------
+// hrtimer derived probes
+// ------------------------------------------------------------------------
+// This is a new timer interface that provides more flexibility in specifying
+// intervals, and uses the hrtimer APIs when available for greater precision.
+// While hrtimers were added in 2.6.16, the API's weren't exported until
+// 2.6.17, so we must check this kernel version before attempting to use
+// hrtimers.
+//
+// * hrtimer_derived_probe: creates a probe point based on the hrtimer APIs.
+
+
+struct hrtimer_derived_probe: public derived_probe
+{
+ // set a (generous) maximum of one day in ns
+ static const int64_t max_ns_interval = 1000000000LL * 60LL * 60LL * 24LL;
+
+ // 100us seems like a reasonable minimum
+ static const int64_t min_ns_interval = 100000LL;
+
+ int64_t interval, randomize;
+
+ hrtimer_derived_probe (probe* p, probe_point* l, int64_t i, int64_t r,
+ int64_t scale):
+ derived_probe (p, l), interval (i), randomize (r)
+ {
+ if ((i < min_ns_interval) || (i > max_ns_interval))
+ throw semantic_error(string("interval value out of range (")
+ + lex_cast<string>(scale < min_ns_interval
+ ? min_ns_interval/scale : 1)
+ + ","
+ + lex_cast<string>(max_ns_interval/scale) + ")");
+
+ // randomize = 0 means no randomization
+ if ((r < 0) || (r > i))
+ throw semantic_error("randomization value out of range");
+ }
+
+ void join_group (systemtap_session& s);
+};
+
+
+struct hrtimer_derived_probe_group: public generic_dpg<hrtimer_derived_probe>
+{
+ void emit_interval (translator_output* o);
+public:
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+void
+hrtimer_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.hrtimer_derived_probes)
+ s.hrtimer_derived_probes = new hrtimer_derived_probe_group ();
+ s.hrtimer_derived_probes->enroll (this);
+}
+
+
+void
+hrtimer_derived_probe_group::emit_interval (translator_output* o)
+{
+ o->line() << "({";
+ o->newline(1) << "unsigned long nsecs;";
+ o->newline() << "int64_t i = stp->intrv;";
+ o->newline() << "if (stp->rnd != 0) {";
+ // XXX: why not use stp_random_pm instead of this?
+ o->newline(1) << "int64_t r;";
+ o->newline() << "get_random_bytes(&r, sizeof(r));";
+ // ensure that r is positive
+ o->newline() << "r &= ((uint64_t)1 << (8*sizeof(r) - 1)) - 1;";
+ o->newline() << "r = _stp_mod64(NULL, r, (2*stp->rnd+1));";
+ o->newline() << "r -= stp->rnd;";
+ o->newline() << "i += r;";
+ o->newline(-1) << "}";
+ o->newline() << "if (unlikely(i < stap_hrtimer_resolution))";
+ o->newline(1) << "i = stap_hrtimer_resolution;";
+ o->indent(-1);
+ o->newline() << "nsecs = do_div(i, NSEC_PER_SEC);";
+ o->newline() << "ktime_set(i, nsecs);";
+ o->newline(-1) << "})";
+}
+
+
+void
+hrtimer_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "/* ---- hrtimer probes ---- */";
+
+ s.op->newline() << "static unsigned long stap_hrtimer_resolution;"; // init later
+ s.op->newline() << "static struct stap_hrtimer_probe {";
+ s.op->newline(1) << "struct hrtimer hrtimer;";
+ s.op->newline() << "const char *pp;";
+ s.op->newline() << "void (*ph) (struct context*);";
+ s.op->newline() << "int64_t intrv, rnd;";
+ s.op->newline(-1) << "} stap_hrtimer_probes [" << probes.size() << "] = {";
+ s.op->indent(1);
+ for (unsigned i=0; i < probes.size(); i++)
+ {
+ s.op->newline () << "{";
+ s.op->line() << " .pp=" << lex_cast_qstring (*probes[i]->sole_location()) << ",";
+ s.op->line() << " .ph=&" << probes[i]->name << ",";
+ s.op->line() << " .intrv=" << probes[i]->interval << "LL,";
+ s.op->line() << " .rnd=" << probes[i]->randomize << "LL";
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+ s.op->newline();
+
+ // autoconf: add get/set expires if missing (pre 2.6.28-rc1)
+ s.op->newline() << "#ifndef STAPCONF_HRTIMER_GETSET_EXPIRES";
+ s.op->newline() << "#define hrtimer_get_expires(timer) ((timer)->expires)";
+ s.op->newline() << "#define hrtimer_set_expires(timer, time) (void)((timer)->expires = (time))";
+ s.op->newline() << "#endif";
+
+ // autoconf: adapt to HRTIMER_REL -> HRTIMER_MODE_REL renaming near 2.6.21
+ s.op->newline() << "#ifdef STAPCONF_HRTIMER_REL";
+ s.op->newline() << "#define HRTIMER_MODE_REL HRTIMER_REL";
+ s.op->newline() << "#endif";
+
+ // The function signature changed in 2.6.21.
+ s.op->newline() << "#ifdef STAPCONF_HRTIMER_REL";
+ s.op->newline() << "static int ";
+ s.op->newline() << "#else";
+ s.op->newline() << "static enum hrtimer_restart ";
+ s.op->newline() << "#endif";
+ s.op->newline() << "enter_hrtimer_probe (struct hrtimer *timer) {";
+
+ s.op->newline(1) << "int rc = HRTIMER_NORESTART;";
+ s.op->newline() << "struct stap_hrtimer_probe *stp = container_of(timer, struct stap_hrtimer_probe, hrtimer);";
+ s.op->newline() << "if ((atomic_read (&session_state) == STAP_SESSION_STARTING) ||";
+ s.op->newline() << " (atomic_read (&session_state) == STAP_SESSION_RUNNING)) {";
+ // Compute next trigger time
+ s.op->newline(1) << "hrtimer_set_expires(timer, ktime_add (hrtimer_get_expires(timer),";
+ emit_interval (s.op);
+ s.op->line() << "));";
+ s.op->newline() << "rc = HRTIMER_RESTART;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "{";
+ s.op->indent(1);
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "stp->pp");
+ s.op->newline() << "(*stp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline(-1) << "}";
+ s.op->newline() << "return rc;";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+hrtimer_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "{";
+ s.op->newline(1) << "struct timespec res;";
+ s.op->newline() << "hrtimer_get_res (CLOCK_MONOTONIC, &res);";
+ s.op->newline() << "stap_hrtimer_resolution = timespec_to_ns (&res);";
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_hrtimer_probe* stp = & stap_hrtimer_probes [i];";
+ s.op->newline() << "probe_point = stp->pp;";
+ s.op->newline() << "hrtimer_init (& stp->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);";
+ s.op->newline() << "stp->hrtimer.function = & enter_hrtimer_probe;";
+ // There is no hrtimer field to identify *this* (i-th) probe handler
+ // callback. So instead we'll deduce it at entry time.
+ s.op->newline() << "(void) hrtimer_start (& stp->hrtimer, ";
+ emit_interval (s.op);
+ s.op->line() << ", HRTIMER_MODE_REL);";
+ // Note: no partial failure rollback is needed: hrtimer_start only
+ // "fails" if the timer was already active, which cannot be.
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+void
+hrtimer_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)";
+ s.op->newline(1) << "hrtimer_cancel (& stap_hrtimer_probes[i].hrtimer);";
+ s.op->indent(-1);
+}
+
+
+
+// ------------------------------------------------------------------------
+// profile derived probes
+// ------------------------------------------------------------------------
+// On kernels < 2.6.10, this uses the register_profile_notifier API to
+// generate the timed events for profiling; on kernels >= 2.6.10 this
+// uses the register_timer_hook API. The latter doesn't currently allow
+// simultaneous users, so insertion will fail if the profiler is busy.
+// (Conflicting users may include OProfile, other SystemTap probes, etc.)
+
+
+struct profile_derived_probe: public derived_probe
+{
+ profile_derived_probe (systemtap_session &s, probe* p, probe_point* l);
+ void join_group (systemtap_session& s);
+};
+
+
+struct profile_derived_probe_group: public generic_dpg<profile_derived_probe>
+{
+public:
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+profile_derived_probe::profile_derived_probe (systemtap_session &, probe* p, probe_point* l):
+ derived_probe(p, l)
+{
+}
+
+
+void
+profile_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.profile_derived_probes)
+ s.profile_derived_probes = new profile_derived_probe_group ();
+ s.profile_derived_probes->enroll (this);
+}
+
+
+// timer.profile probe handlers are hooked up in an entertaining way
+// to the underlying kernel facility. The fact that 2.6.11+ era
+// "register_timer_hook" API allows only one consumer *system-wide*
+// will give a hint. We will have a single entry function (and thus
+// trivial registration / unregistration), and it will call all probe
+// handler functions in sequence.
+
+void
+profile_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ // kernels < 2.6.10: use register_profile_notifier API
+ // kernels >= 2.6.10: use register_timer_hook API
+ s.op->newline() << "/* ---- profile probes ---- */";
+
+ // This function calls all the profiling probe handlers in sequence.
+ // The only tricky thing is that the context will be reused amongst
+ // them. While a simple sequence of calls to the individual probe
+ // handlers is unlikely to go terribly wrong (with c->last_error
+ // being set causing an early return), but for extra assurance, we
+ // open-code the same logic here.
+
+ s.op->newline() << "static void enter_all_profile_probes (struct pt_regs *regs) {";
+ s.op->indent(1);
+ string pp = lex_cast_qstring("timer.profile"); // hard-coded for convenience
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", pp);
+ s.op->newline() << "c->regs = regs;";
+
+ for (unsigned i=0; i<probes.size(); i++)
+ {
+ if (i > 0)
+ {
+ // Some lightweight inter-probe context resetting
+ // XXX: not quite right: MAXERRORS not respected
+ s.op->newline() << "c->actionremaining = MAXACTION;";
+ }
+ s.op->newline() << "if (c->last_error == NULL) " << probes[i]->name << " (c);";
+ }
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore
+
+ s.op->newline() << "static int enter_profile_probes (struct notifier_block *self,"
+ << " unsigned long val, void *data) {";
+ s.op->newline(1) << "(void) self; (void) val;";
+ s.op->newline() << "enter_all_profile_probes ((struct pt_regs *) data);";
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "struct notifier_block stap_profile_notifier = {"
+ << " .notifier_call = & enter_profile_probes };";
+
+ s.op->newline() << "#else";
+
+ s.op->newline() << "static int enter_profile_probes (struct pt_regs *regs) {";
+ s.op->newline(1) << "enter_all_profile_probes (regs);";
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "#endif";
+}
+
+
+void
+profile_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "probe_point = \"timer.profile\";"; // NB: hard-coded for convenience
+ s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore
+ s.op->newline() << "rc = register_profile_notifier (& stap_profile_notifier);";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_timer_hook (& enter_profile_probes);";
+ s.op->newline() << "#endif";
+}
+
+
+void
+profile_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty()) return;
+
+ s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)";
+ s.op->newline(1) << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore
+ s.op->newline() << "unregister_profile_notifier (& stap_profile_notifier);";
+ s.op->newline() << "#else";
+ s.op->newline() << "unregister_timer_hook (& enter_profile_probes);";
+ s.op->newline() << "#endif";
+ s.op->indent(-1);
+}
+
+
+
+// ------------------------------------------------------------------------
+// unified probe builder for timer probes
+// ------------------------------------------------------------------------
+
+
+struct timer_builder: public derived_probe_builder
+{
+ virtual void build(systemtap_session & sess,
+ probe * base, probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+
+ static void register_patterns(systemtap_session& s);
+};
+
+void
+timer_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ int64_t scale=1, period, rand=0;
+
+ if (has_null_param(parameters, "profile"))
+ {
+ sess.unwindsym_modules.insert ("kernel");
+ finished_results.push_back
+ (new profile_derived_probe(sess, base, location));
+ return;
+ }
+
+ if (!get_param(parameters, "randomize", rand))
+ rand = 0;
+
+ if (get_param(parameters, "jiffies", period))
+ {
+ // always use basic timers for jiffies
+ finished_results.push_back
+ (new timer_derived_probe(base, location, period, rand, false));
+ return;
+ }
+ else if (get_param(parameters, "hz", period))
+ {
+ if (period <= 0)
+ throw semantic_error ("frequency must be greater than 0");
+ period = (1000000000 + period - 1)/period;
+ }
+ else if (get_param(parameters, "s", period) ||
+ get_param(parameters, "sec", period))
+ {
+ scale = 1000000000;
+ period *= scale;
+ rand *= scale;
+ }
+ else if (get_param(parameters, "ms", period) ||
+ get_param(parameters, "msec", period))
+ {
+ scale = 1000000;
+ period *= scale;
+ rand *= scale;
+ }
+ else if (get_param(parameters, "us", period) ||
+ get_param(parameters, "usec", period))
+ {
+ scale = 1000;
+ period *= scale;
+ rand *= scale;
+ }
+ else if (get_param(parameters, "ns", period) ||
+ get_param(parameters, "nsec", period))
+ {
+ // ok
+ }
+ else
+ throw semantic_error ("unrecognized timer variant");
+
+ // Redirect wallclock-time based probes to hrtimer code on recent
+ // enough kernels.
+ if (strverscmp(sess.kernel_base_release.c_str(), "2.6.17") < 0)
+ {
+ // hrtimers didn't exist, so use the old-school timers
+ period = (period + 1000000 - 1)/1000000;
+ rand = (rand + 1000000 - 1)/1000000;
+
+ finished_results.push_back
+ (new timer_derived_probe(base, location, period, rand, true));
+ }
+ else
+ finished_results.push_back
+ (new hrtimer_derived_probe(base, location, period, rand, scale));
+}
+
+void
+register_tapset_timers(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+ derived_probe_builder *builder = new timer_builder();
+
+ root = root->bind(TOK_TIMER);
+
+ root->bind_num("s")->bind(builder);
+ root->bind_num("s")->bind_num("randomize")->bind(builder);
+ root->bind_num("sec")->bind(builder);
+ root->bind_num("sec")->bind_num("randomize")->bind(builder);
+
+ root->bind_num("ms")->bind(builder);
+ root->bind_num("ms")->bind_num("randomize")->bind(builder);
+ root->bind_num("msec")->bind(builder);
+ root->bind_num("msec")->bind_num("randomize")->bind(builder);
+
+ root->bind_num("us")->bind(builder);
+ root->bind_num("us")->bind_num("randomize")->bind(builder);
+ root->bind_num("usec")->bind(builder);
+ root->bind_num("usec")->bind_num("randomize")->bind(builder);
+
+ root->bind_num("ns")->bind(builder);
+ root->bind_num("ns")->bind_num("randomize")->bind(builder);
+ root->bind_num("nsec")->bind(builder);
+ root->bind_num("nsec")->bind_num("randomize")->bind(builder);
+
+ root->bind_num("jiffies")->bind(builder);
+ root->bind_num("jiffies")->bind_num("randomize")->bind(builder);
+
+ root->bind_num("hz")->bind(builder);
+
+ root->bind("profile")->bind(builder);
+}
+
+
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx
new file mode 100644
index 00000000..41a6f24f
--- /dev/null
+++ b/tapset-utrace.cxx
@@ -0,0 +1,1061 @@
+// utrace tapset
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+
+#include "session.h"
+#include "tapsets.h"
+#include "task_finder.h"
+#include "translate.h"
+#include "util.h"
+
+#include <cstring>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_PROCESS("process");
+static string TOK_BEGIN("begin");
+static string TOK_END("end");
+static string TOK_THREAD("thread");
+static string TOK_SYSCALL("syscall");
+static string TOK_RETURN("return");
+
+
+// ------------------------------------------------------------------------
+// utrace user-space probes
+// ------------------------------------------------------------------------
+
+// Note that these flags don't match up exactly with UTRACE_EVENT
+// flags (and that's OK).
+enum utrace_derived_probe_flags {
+ UDPF_NONE,
+ UDPF_BEGIN, // process begin
+ UDPF_END, // process end
+ UDPF_THREAD_BEGIN, // thread begin
+ UDPF_THREAD_END, // thread end
+ UDPF_SYSCALL, // syscall entry
+ UDPF_SYSCALL_RETURN, // syscall exit
+ UDPF_NFLAGS
+};
+
+struct utrace_derived_probe: public derived_probe
+{
+ bool has_path;
+ string path;
+ int64_t pid;
+ enum utrace_derived_probe_flags flags;
+ bool target_symbol_seen;
+
+ utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l,
+ bool hp, string &pn, int64_t pd,
+ enum utrace_derived_probe_flags f);
+ void join_group (systemtap_session& s);
+};
+
+
+struct utrace_derived_probe_group: public generic_dpg<utrace_derived_probe>
+{
+private:
+ map<string, vector<utrace_derived_probe*> > probes_by_path;
+ typedef map<string, vector<utrace_derived_probe*> >::iterator p_b_path_iterator;
+ map<int64_t, vector<utrace_derived_probe*> > probes_by_pid;
+ typedef map<int64_t, vector<utrace_derived_probe*> >::iterator p_b_pid_iterator;
+ unsigned num_probes;
+ bool flags_seen[UDPF_NFLAGS];
+
+ void emit_probe_decl (systemtap_session& s, utrace_derived_probe *p);
+
+public:
+ utrace_derived_probe_group(): num_probes(0), flags_seen() { }
+
+ void enroll (utrace_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+struct utrace_var_expanding_visitor: public var_expanding_visitor
+{
+ utrace_var_expanding_visitor(systemtap_session& s, probe_point* l,
+ const string& pn,
+ enum utrace_derived_probe_flags f):
+ sess (s), base_loc (l), probe_name (pn), flags (f),
+ target_symbol_seen (false), add_block(NULL), add_probe(NULL) {}
+
+ systemtap_session& sess;
+ probe_point* base_loc;
+ string probe_name;
+ enum utrace_derived_probe_flags flags;
+ bool target_symbol_seen;
+ block *add_block;
+ probe *add_probe;
+ std::map<std::string, symbol *> return_ts_map;
+
+ void visit_target_symbol_arg (target_symbol* e);
+ void visit_target_symbol_context (target_symbol* e);
+ void visit_target_symbol_cached (target_symbol* e);
+ void visit_target_symbol (target_symbol* e);
+};
+
+
+
+utrace_derived_probe::utrace_derived_probe (systemtap_session &s,
+ probe* p, probe_point* l,
+ bool hp, string &pn, int64_t pd,
+ enum utrace_derived_probe_flags f):
+ derived_probe (p, new probe_point (*l) /* .components soon rewritten */ ),
+ has_path(hp), path(pn), pid(pd), flags(f),
+ target_symbol_seen(false)
+{
+ // Expand local variables in the probe body
+ utrace_var_expanding_visitor v (s, l, name, flags);
+ this->body = v.require (this->body);
+ target_symbol_seen = v.target_symbol_seen;
+
+ // If during target-variable-expanding the probe, we added a new block
+ // of code, add it to the start of the probe.
+ if (v.add_block)
+ this->body = new block(v.add_block, this->body);
+ // If when target-variable-expanding the probe, we added a new
+ // probe, add it in a new file to the list of files to be processed.
+ if (v.add_probe)
+ {
+ stapfile *f = new stapfile;
+ f->probes.push_back(v.add_probe);
+ s.files.push_back(f);
+ }
+
+ // Reset the sole element of the "locations" vector as a
+ // "reverse-engineered" form of the incoming (q.base_loc) probe
+ // point. This allows a user to see what program etc.
+ // number any particular match of the wildcards.
+
+ vector<probe_point::component*> comps;
+ if (hp)
+ comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(path)));
+ else if (pid != 0)
+ comps.push_back (new probe_point::component(TOK_PROCESS, new literal_number(pid)));
+ else
+ comps.push_back (new probe_point::component(TOK_PROCESS));
+
+ switch (flags)
+ {
+ case UDPF_THREAD_BEGIN:
+ comps.push_back (new probe_point::component(TOK_THREAD));
+ comps.push_back (new probe_point::component(TOK_BEGIN));
+ break;
+ case UDPF_THREAD_END:
+ comps.push_back (new probe_point::component(TOK_THREAD));
+ comps.push_back (new probe_point::component(TOK_END));
+ break;
+ case UDPF_SYSCALL:
+ comps.push_back (new probe_point::component(TOK_SYSCALL));
+ break;
+ case UDPF_SYSCALL_RETURN:
+ comps.push_back (new probe_point::component(TOK_SYSCALL));
+ comps.push_back (new probe_point::component(TOK_RETURN));
+ break;
+ case UDPF_BEGIN:
+ comps.push_back (new probe_point::component(TOK_BEGIN));
+ break;
+ case UDPF_END:
+ comps.push_back (new probe_point::component(TOK_END));
+ break;
+ default:
+ assert (0);
+ }
+
+ // Overwrite it.
+ this->sole_location()->components = comps;
+}
+
+
+void
+utrace_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.utrace_derived_probes)
+ {
+ s.utrace_derived_probes = new utrace_derived_probe_group ();
+ }
+ s.utrace_derived_probes->enroll (this);
+
+ enable_task_finder(s);
+}
+
+
+void
+utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e)
+{
+ // Get the full name of the target symbol.
+ stringstream ts_name_stream;
+ e->print(ts_name_stream);
+ string ts_name = ts_name_stream.str();
+
+ // Check and make sure we haven't already seen this target
+ // variable in this return probe. If we have, just return our
+ // last replacement.
+ map<string, symbol *>::iterator i = return_ts_map.find(ts_name);
+ if (i != return_ts_map.end())
+ {
+ provide (i->second);
+ return;
+ }
+
+ // We've got to do several things here to handle target
+ // variables in return probes.
+
+ // (1) Synthesize a global array which is the cache of the
+ // target variable value. We don't need a nesting level counter
+ // like the dwarf_var_expanding_visitor::visit_target_symbol()
+ // does since a particular thread can only be in one system
+ // calls at a time. The array will look like this:
+ //
+ // _utrace_tvar_{name}_{num}
+ string aname = (string("_utrace_tvar_")
+ + e->base_name.substr(1)
+ + "_" + lex_cast<string>(tick++));
+ vardecl* vd = new vardecl;
+ vd->name = aname;
+ vd->tok = e->tok;
+ sess.globals.push_back (vd);
+
+ // (2) Create a new code block we're going to insert at the
+ // beginning of this probe to get the cached value into a
+ // temporary variable. We'll replace the target variable
+ // reference with the temporary variable reference. The code
+ // will look like this:
+ //
+ // _utrace_tvar_tid = tid()
+ // _utrace_tvar_{name}_{num}_tmp
+ // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+ // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ // (2a) Synthesize the tid temporary expression, which will look
+ // like this:
+ //
+ // _utrace_tvar_tid = tid()
+ symbol* tidsym = new symbol;
+ tidsym->name = string("_utrace_tvar_tid");
+ tidsym->tok = e->tok;
+
+ if (add_block == NULL)
+ {
+ add_block = new block;
+ add_block->tok = e->tok;
+
+ // Synthesize a functioncall to grab the thread id.
+ functioncall* fc = new functioncall;
+ fc->tok = e->tok;
+ fc->function = string("tid");
+
+ // Assign the tid to '_utrace_tvar_tid'.
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tidsym;
+ a->right = fc;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+ add_block->statements.push_back (es);
+ }
+
+ // (2b) Synthesize an array reference and assign it to a
+ // temporary variable (that we'll use as replacement for the
+ // target variable reference). It will look like this:
+ //
+ // _utrace_tvar_{name}_{num}_tmp
+ // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ arrayindex* ai_tvar = new arrayindex;
+ ai_tvar->tok = e->tok;
+
+ symbol* sym = new symbol;
+ sym->name = aname;
+ sym->tok = e->tok;
+ ai_tvar->base = sym;
+
+ ai_tvar->indexes.push_back(tidsym);
+
+ symbol* tmpsym = new symbol;
+ tmpsym->name = aname + "_tmp";
+ tmpsym->tok = e->tok;
+
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tmpsym;
+ a->right = ai_tvar;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+
+ add_block->statements.push_back (es);
+
+ // (2c) Delete the array value. It will look like this:
+ //
+ // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ delete_statement* ds = new delete_statement;
+ ds->tok = e->tok;
+ ds->value = ai_tvar;
+ add_block->statements.push_back (ds);
+
+ // (3) We need an entry probe that saves the value for us in the
+ // global array we created. Create the entry probe, which will
+ // look like this:
+ //
+ // probe process(PATH_OR_PID).syscall {
+ // _utrace_tvar_tid = tid()
+ // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
+ // }
+ //
+ // Why the temporary for tid()? If we end up caching more
+ // than one target variable, we can reuse the temporary instead
+ // of calling tid() multiple times.
+
+ if (add_probe == NULL)
+ {
+ add_probe = new probe;
+ add_probe->tok = e->tok;
+
+ // We need the name of the current probe point, minus the
+ // ".return". Create a new probe point, copying all the
+ // components, stopping when we see the ".return"
+ // component.
+ probe_point* pp = new probe_point;
+ for (unsigned c = 0; c < base_loc->components.size(); c++)
+ {
+ if (base_loc->components[c]->functor == "return")
+ break;
+ else
+ pp->components.push_back(base_loc->components[c]);
+ }
+ pp->tok = e->tok;
+ pp->optional = base_loc->optional;
+ add_probe->locations.push_back(pp);
+
+ add_probe->body = new block;
+ add_probe->body->tok = e->tok;
+
+ // Synthesize a functioncall to grab the thread id.
+ functioncall* fc = new functioncall;
+ fc->tok = e->tok;
+ fc->function = string("tid");
+
+ // Assign the tid to '_utrace_tvar_tid'.
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tidsym;
+ a->right = fc;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+ add_probe->body = new block(add_probe->body, es);
+
+ vardecl* vd = new vardecl;
+ vd->tok = e->tok;
+ vd->name = tidsym->name;
+ vd->type = pe_long;
+ vd->set_arity(0);
+ add_probe->locals.push_back(vd);
+ }
+
+ // Save the value, like this:
+ //
+ // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
+ a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = ai_tvar;
+ a->right = e;
+
+ es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+
+ add_probe->body = new block(add_probe->body, es);
+
+ // (4) Provide the '_utrace_tvar_{name}_{num}_tmp' variable to
+ // our parent so it can be used as a substitute for the target
+ // symbol.
+ provide (tmpsym);
+
+ // (5) Remember this replacement since we might be able to reuse
+ // it later if the same return probe references this target
+ // symbol again.
+ return_ts_map[ts_name] = tmpsym;
+ return;
+}
+
+
+void
+utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
+{
+ string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
+ int argnum = lex_cast<int>(argnum_s);
+
+ if (flags != UDPF_SYSCALL)
+ throw semantic_error ("only \"process(PATH_OR_PID).syscall\" support $argN.", e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("utrace target variable '$argN' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("utrace target variable '$argN' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid use of utrace target variable '$argN'",
+ e->tok);
+ break;
+ }
+ }
+
+ // FIXME: max argnument number should not be hardcoded.
+ if (argnum < 1 || argnum > 6)
+ throw semantic_error ("invalid syscall argument number (1-6)", e->tok);
+
+ bool lvalue = is_active_lvalue(e);
+ if (lvalue)
+ throw semantic_error("utrace '$argN' variable is read-only", e->tok);
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+
+ // We're going to substitute a synthesized '_utrace_syscall_arg'
+ // function call for the '$argN' reference.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = "_utrace_syscall_arg";
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ literal_number *num = new literal_number(argnum - 1);
+ num->tok = e->tok;
+ n->args.push_back(num);
+
+ provide (n);
+}
+
+void
+utrace_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
+{
+ string sname = e->base_name;
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("utrace target variable '" + sname + "' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("utrace target variable '" + sname + "' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid use of utrace target variable '" + sname + "'",
+ e->tok);
+ break;
+ }
+ }
+
+ bool lvalue = is_active_lvalue(e);
+ if (lvalue)
+ throw semantic_error("utrace '" + sname + "' variable is read-only", e->tok);
+
+ string fname;
+ if (sname == "$return")
+ {
+ if (flags != UDPF_SYSCALL_RETURN)
+ throw semantic_error ("only \"process(PATH_OR_PID).syscall.return\" support $return.", e->tok);
+ fname = "_utrace_syscall_return";
+ }
+ else if (sname == "$syscall")
+ {
+ // If we've got a syscall entry probe, we can just call the
+ // right function.
+ if (flags == UDPF_SYSCALL) {
+ fname = "_utrace_syscall_nr";
+ }
+ // If we're in a syscal return probe, we can't really access
+ // $syscall. So, similar to what
+ // dwarf_var_expanding_visitor::visit_target_symbol() does,
+ // we'll create an syscall entry probe to cache $syscall, then
+ // we'll access the cached value in the syscall return probe.
+ else {
+ visit_target_symbol_cached (e);
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+ return;
+ }
+ }
+ else
+ {
+ throw semantic_error ("unknown target variable", e->tok);
+ }
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+
+ // We're going to substitute a synthesized '_utrace_syscall_nr'
+ // function call for the '$syscall' reference.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ provide (n);
+}
+
+void
+utrace_var_expanding_visitor::visit_target_symbol (target_symbol* e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ if (flags != UDPF_SYSCALL && flags != UDPF_SYSCALL_RETURN)
+ throw semantic_error ("only \"process(PATH_OR_PID).syscall\" and \"process(PATH_OR_PID).syscall.return\" probes support target symbols",
+ e->tok);
+
+ if (e->base_name.substr(0,4) == "$arg")
+ visit_target_symbol_arg(e);
+ else if (e->base_name == "$syscall" || e->base_name == "$return")
+ visit_target_symbol_context(e);
+ else
+ throw semantic_error ("invalid target symbol for utrace probe, $syscall, $return or $argN expected",
+ e->tok);
+}
+
+
+struct utrace_builder: public derived_probe_builder
+{
+ utrace_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+ {
+ string path;
+ int64_t pid;
+
+ bool has_path = get_param (parameters, TOK_PROCESS, path);
+ bool has_pid = get_param (parameters, TOK_PROCESS, pid);
+ enum utrace_derived_probe_flags flags = UDPF_NONE;
+
+ if (has_null_param (parameters, TOK_THREAD))
+ {
+ if (has_null_param (parameters, TOK_BEGIN))
+ flags = UDPF_THREAD_BEGIN;
+ else if (has_null_param (parameters, TOK_END))
+ flags = UDPF_THREAD_END;
+ }
+ else if (has_null_param (parameters, TOK_SYSCALL))
+ {
+ if (has_null_param (parameters, TOK_RETURN))
+ flags = UDPF_SYSCALL_RETURN;
+ else
+ flags = UDPF_SYSCALL;
+ }
+ else if (has_null_param (parameters, TOK_BEGIN))
+ flags = UDPF_BEGIN;
+ else if (has_null_param (parameters, TOK_END))
+ flags = UDPF_END;
+
+ // If we didn't get a path or pid, this means to probe everything.
+ // Convert this to a pid-based probe.
+ if (! has_path && ! has_pid)
+ {
+ has_path = false;
+ path.clear();
+ has_pid = true;
+ pid = 0;
+ }
+ else if (has_path)
+ {
+ path = find_executable (path);
+ sess.unwindsym_modules.insert (path);
+ }
+ else if (has_pid)
+ {
+ // We can't probe 'init' (pid 1). XXX: where does this limitation come from?
+ if (pid < 2)
+ throw semantic_error ("process pid must be greater than 1",
+ location->tok);
+
+ // XXX: could we use /proc/$pid/exe in unwindsym_modules and elsewhere?
+ }
+
+ finished_results.push_back(new utrace_derived_probe(sess, base, location,
+ has_path, path, pid,
+ flags));
+ }
+};
+
+
+void
+utrace_derived_probe_group::enroll (utrace_derived_probe* p)
+{
+ if (p->has_path)
+ probes_by_path[p->path].push_back(p);
+ else
+ probes_by_pid[p->pid].push_back(p);
+ num_probes++;
+ flags_seen[p->flags] = true;
+
+ // XXX: multiple exec probes (for instance) for the same path (or
+ // pid) should all share a utrace report function, and have their
+ // handlers executed sequentially.
+}
+
+
+void
+utrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
+ utrace_derived_probe *p)
+{
+ s.op->newline() << "{";
+ s.op->line() << " .tgt={";
+
+ if (p->has_path)
+ {
+ s.op->line() << " .pathname=\"" << p->path << "\",";
+ s.op->line() << " .pid=0,";
+ }
+ else
+ {
+ s.op->line() << " .pathname=NULL,";
+ s.op->line() << " .pid=" << p->pid << ",";
+ }
+
+ s.op->line() << " .callback=&_stp_utrace_probe_cb,";
+ s.op->line() << " .mmap_callback=NULL,";
+ s.op->line() << " .munmap_callback=NULL,";
+ s.op->line() << " .mprotect_callback=NULL,";
+ s.op->line() << " },";
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .ph=&" << p->name << ",";
+
+ // Handle flags
+ switch (p->flags)
+ {
+ // Notice that we'll just call the probe directly when we get
+ // notified, since the task_finder layer stops the thread for us.
+ case UDPF_BEGIN: // process begin
+ s.op->line() << " .flags=(UDPF_BEGIN),";
+ break;
+ case UDPF_THREAD_BEGIN: // thread begin
+ s.op->line() << " .flags=(UDPF_THREAD_BEGIN),";
+ break;
+
+ // Notice we're not setting up a .ops/.report_death handler for
+ // either UDPF_END or UDPF_THREAD_END. Instead, we'll just call
+ // the probe directly when we get notified.
+ case UDPF_END: // process end
+ s.op->line() << " .flags=(UDPF_END),";
+ break;
+ case UDPF_THREAD_END: // thread end
+ s.op->line() << " .flags=(UDPF_THREAD_END),";
+ break;
+
+ // For UDPF_SYSCALL/UDPF_SYSCALL_RETURN probes, the .report_death
+ // handler isn't strictly necessary. However, it helps to keep
+ // our attaches/detaches symmetrical. Since the task_finder layer
+ // stops the thread, that works around bug 6841.
+ case UDPF_SYSCALL:
+ s.op->line() << " .flags=(UDPF_SYSCALL),";
+ s.op->line() << " .ops={ .report_syscall_entry=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },";
+ s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)),";
+ break;
+ case UDPF_SYSCALL_RETURN:
+ s.op->line() << " .flags=(UDPF_SYSCALL_RETURN),";
+ s.op->line() << " .ops={ .report_syscall_exit=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },";
+ s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)),";
+ break;
+
+ case UDPF_NONE:
+ s.op->line() << " .flags=(UDPF_NONE),";
+ s.op->line() << " .ops={ },";
+ s.op->line() << " .events=0,";
+ break;
+ default:
+ throw semantic_error ("bad utrace probe flag");
+ break;
+ }
+ s.op->line() << " .engine_attached=0,";
+ s.op->line() << " },";
+}
+
+
+void
+utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty())
+ return;
+
+ s.op->newline();
+ s.op->newline() << "/* ---- utrace probes ---- */";
+
+ s.op->newline() << "enum utrace_derived_probe_flags {";
+ s.op->indent(1);
+ s.op->newline() << "UDPF_NONE,";
+ s.op->newline() << "UDPF_BEGIN,";
+ s.op->newline() << "UDPF_END,";
+ s.op->newline() << "UDPF_THREAD_BEGIN,";
+ s.op->newline() << "UDPF_THREAD_END,";
+ s.op->newline() << "UDPF_SYSCALL,";
+ s.op->newline() << "UDPF_SYSCALL_RETURN,";
+ s.op->newline() << "UDPF_NFLAGS";
+ s.op->newline(-1) << "};";
+
+ s.op->newline() << "struct stap_utrace_probe {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_task_finder_target tgt;";
+ s.op->newline() << "const char *pp;";
+ s.op->newline() << "void (*ph) (struct context*);";
+ s.op->newline() << "enum utrace_derived_probe_flags flags;";
+ s.op->newline() << "struct utrace_engine_ops ops;";
+ s.op->newline() << "unsigned long events;";
+ s.op->newline() << "int engine_attached;";
+ s.op->newline(-1) << "};";
+
+
+ // Output handler function for UDPF_BEGIN, UDPF_THREAD_BEGIN,
+ // UDPF_END, and UDPF_THREAD_END
+ if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN]
+ || flags_seen[UDPF_END] || flags_seen[UDPF_THREAD_END])
+ {
+ s.op->newline() << "static void stap_utrace_probe_handler(struct task_struct *tsk, struct stap_utrace_probe *p) {";
+ s.op->indent(1);
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->pp");
+
+ // call probe function
+ s.op->newline() << "(*p->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return;";
+ s.op->newline(-1) << "}";
+ }
+
+ // Output handler function for SYSCALL_ENTRY and SYSCALL_EXIT events
+ if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN])
+ {
+ s.op->newline() << "#ifdef UTRACE_ORIG_VERSION";
+ s.op->newline() << "static u32 stap_utrace_probe_syscall(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {";
+ s.op->newline() << "#else";
+ s.op->newline() << "static u32 stap_utrace_probe_syscall(enum utrace_resume_action action, struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {";
+ s.op->newline() << "#endif";
+
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->pp");
+ s.op->newline() << "c->regs = regs;";
+
+ // call probe function
+ s.op->newline() << "(*p->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "if ((atomic_read (&session_state) != STAP_SESSION_STARTING) && (atomic_read (&session_state) != STAP_SESSION_RUNNING)) {";
+ s.op->indent(1);
+ s.op->newline() << "debug_task_finder_detach();";
+ s.op->newline() << "return UTRACE_DETACH;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "return UTRACE_RESUME;";
+ s.op->newline(-1) << "}";
+ }
+
+ // Output task_finder callback routine that gets called for all
+ // utrace probe types.
+ s.op->newline() << "static int _stp_utrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {";
+ s.op->indent(1);
+ s.op->newline() << "int rc = 0;";
+ s.op->newline() << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);";
+ s.op->newline() << "struct utrace_attached_engine *engine;";
+
+ s.op->newline() << "if (register_p) {";
+ s.op->indent(1);
+
+ s.op->newline() << "switch (p->flags) {";
+ s.op->indent(1);
+
+ // When receiving a UTRACE_EVENT(CLONE) event, we can't call the
+ // begin/thread.begin probe directly. So, we'll just attach an
+ // engine that waits for the thread to quiesce. When the thread
+ // quiesces, then call the probe.
+ if (flags_seen[UDPF_BEGIN])
+ {
+ s.op->newline() << "case UDPF_BEGIN:";
+ s.op->indent(1);
+ s.op->newline() << "if (process_p) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+ if (flags_seen[UDPF_THREAD_BEGIN])
+ {
+ s.op->newline() << "case UDPF_THREAD_BEGIN:";
+ s.op->indent(1);
+ s.op->newline() << "if (! process_p) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+
+ // For end/thread_end probes, do nothing at registration time.
+ // We'll handle these in the 'register_p == 0' case.
+ if (flags_seen[UDPF_END] || flags_seen[UDPF_THREAD_END])
+ {
+ s.op->newline() << "case UDPF_END:";
+ s.op->newline() << "case UDPF_THREAD_END:";
+ s.op->indent(1);
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+
+ // Attach an engine for SYSCALL_ENTRY and SYSCALL_EXIT events.
+ if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN])
+ {
+ s.op->newline() << "case UDPF_SYSCALL:";
+ s.op->newline() << "case UDPF_SYSCALL_RETURN:";
+ s.op->indent(1);
+ s.op->newline() << "rc = stap_utrace_attach(tsk, &p->ops, p, p->events);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->indent(1);
+ s.op->newline() << "p->engine_attached = 1;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+
+ s.op->newline() << "default:";
+ s.op->indent(1);
+ s.op->newline() << "_stp_error(\"unhandled flag value %d at %s:%d\", p->flags, __FUNCTION__, __LINE__);";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+
+ // Since this engine could be attached to multiple threads, don't
+ // call stap_utrace_detach_ops() here, only call
+ // stap_utrace_detach() as necessary.
+ s.op->newline() << "else {";
+ s.op->indent(1);
+ s.op->newline() << "switch (p->flags) {";
+ s.op->indent(1);
+ // For end probes, go ahead and call the probe directly.
+ if (flags_seen[UDPF_END])
+ {
+ s.op->newline() << "case UDPF_END:";
+ s.op->indent(1);
+ s.op->newline() << "if (process_p) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+ if (flags_seen[UDPF_THREAD_END])
+ {
+ s.op->newline() << "case UDPF_THREAD_END:";
+ s.op->indent(1);
+ s.op->newline() << "if (! process_p) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+
+ // For begin/thread_begin probes, we don't need to do anything.
+ if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN])
+ {
+ s.op->newline() << "case UDPF_BEGIN:";
+ s.op->newline() << "case UDPF_THREAD_BEGIN:";
+ s.op->indent(1);
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+
+ if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN])
+ {
+ s.op->newline() << "case UDPF_SYSCALL:";
+ s.op->newline() << "case UDPF_SYSCALL_RETURN:";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_detach(tsk, &p->ops);";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ }
+
+ s.op->newline() << "default:";
+ s.op->indent(1);
+ s.op->newline() << "_stp_error(\"unhandled flag value %d at %s:%d\", p->flags, __FUNCTION__, __LINE__);";
+ s.op->newline() << "break;";
+ s.op->indent(-1);
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "return rc;";
+ s.op->newline(-1) << "}";
+
+ // Emit vma callbacks.
+ s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
+ s.op->newline() << "static struct stap_task_finder_target stap_utrace_vmcbs[] = {";
+ s.op->indent(1);
+ if (! probes_by_path.empty())
+ {
+ for (p_b_path_iterator it = probes_by_path.begin();
+ it != probes_by_path.end(); it++)
+ emit_vma_callback_probe_decl (s, it->first, (int64_t)0);
+ }
+ if (! probes_by_pid.empty())
+ {
+ for (p_b_pid_iterator it = probes_by_pid.begin();
+ it != probes_by_pid.end(); it++)
+ emit_vma_callback_probe_decl (s, "", it->first);
+ }
+ s.op->newline(-1) << "};";
+ s.op->newline() << "#endif";
+
+ s.op->newline() << "static struct stap_utrace_probe stap_utrace_probes[] = {";
+ s.op->indent(1);
+
+ // Set up 'process(PATH)' probes
+ if (! probes_by_path.empty())
+ {
+ for (p_b_path_iterator it = probes_by_path.begin();
+ it != probes_by_path.end(); it++)
+ {
+ for (unsigned i = 0; i < it->second.size(); i++)
+ {
+ utrace_derived_probe *p = it->second[i];
+ emit_probe_decl(s, p);
+ }
+ }
+ }
+
+ // Set up 'process(PID)' probes
+ if (! probes_by_pid.empty())
+ {
+ for (p_b_pid_iterator it = probes_by_pid.begin();
+ it != probes_by_pid.end(); it++)
+ {
+ for (unsigned i = 0; i < it->second.size(); i++)
+ {
+ utrace_derived_probe *p = it->second[i];
+ emit_probe_decl(s, p);
+ }
+ }
+ }
+ s.op->newline(-1) << "};";
+}
+
+
+void
+utrace_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty())
+ return;
+
+ s.op->newline();
+ s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
+ s.op->newline() << "_stp_sym_init();";
+ s.op->newline() << "/* ---- utrace vma callbacks ---- */";
+ s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_utrace_vmcbs); i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_task_finder_target *r = &stap_utrace_vmcbs[i];";
+ s.op->newline() << "rc = stap_register_task_finder_target(r);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "#endif";
+
+ s.op->newline() << "/* ---- utrace probes ---- */";
+ s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_utrace_probes); i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];";
+ s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);";
+ s.op->newline(-1) << "}";
+
+ // rollback all utrace probes
+ s.op->newline() << "if (rc) {";
+ s.op->indent(1);
+ s.op->newline() << "for (j=i-1; j>=0; j--) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[j];";
+
+ s.op->newline() << "if (p->engine_attached) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_detach_ops(&p->ops);";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+
+ s.op->newline(-1) << "}";
+}
+
+
+void
+utrace_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty()) return;
+
+ s.op->newline();
+ s.op->newline() << "/* ---- utrace probes ---- */";
+ s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_utrace_probes); i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];";
+
+ s.op->newline() << "if (p->engine_attached) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_detach_ops(&p->ops);";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+register_tapset_utrace(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+ derived_probe_builder *builder = new utrace_builder();
+
+ vector<match_node*> roots;
+ roots.push_back(root->bind(TOK_PROCESS));
+ roots.push_back(root->bind_str(TOK_PROCESS));
+ roots.push_back(root->bind_num(TOK_PROCESS));
+
+ for (unsigned i = 0; i < roots.size(); ++i)
+ {
+ roots[i]->bind(TOK_BEGIN)->bind(builder);
+ roots[i]->bind(TOK_END)->bind(builder);
+ roots[i]->bind(TOK_THREAD)->bind(TOK_BEGIN)->bind(builder);
+ roots[i]->bind(TOK_THREAD)->bind(TOK_END)->bind(builder);
+ roots[i]->bind(TOK_SYSCALL)->bind(builder);
+ roots[i]->bind(TOK_SYSCALL)->bind(TOK_RETURN)->bind(builder);
+ }
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapsets.cxx b/tapsets.cxx
index db14a787..f0002073 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -12,6 +12,7 @@
#include "staptree.h"
#include "elaborate.h"
#include "tapsets.h"
+#include "task_finder.h"
#include "translate.h"
#include "session.h"
#include "util.h"
@@ -58,109 +59,16 @@ extern "C" {
}
-#ifdef PERFMON
-#include <perfmon/pfmlib.h>
-#include <perfmon/perfmon.h>
-#endif
-
using namespace std;
using namespace __gnu_cxx;
-// ------------------------------------------------------------------------
-// Generic derived_probe_group: contains an ordinary vector of the
-// given type. It provides only the enrollment function.
-
-template <class DP> struct generic_dpg: public derived_probe_group
-{
-protected:
- vector <DP*> probes;
-public:
- generic_dpg () {}
- void enroll (DP* probe) { probes.push_back (probe); }
-};
-
-
-
-// ------------------------------------------------------------------------
-// begin/end/error probes are run right during registration / deregistration
-// ------------------------------------------------------------------------
-
-static string TOK_BEGIN("begin");
-static string TOK_END("end");
-static string TOK_ERROR("error");
-
-enum be_t { BEGIN, END, ERROR };
-
-struct be_derived_probe: public derived_probe
-{
- be_t type;
- int64_t priority;
-
- be_derived_probe (probe* p, probe_point* l, be_t t, int64_t pr):
- derived_probe (p, l), type (t), priority (pr) {}
-
- void join_group (systemtap_session& s);
-
- static inline bool comp(be_derived_probe const *a,
- be_derived_probe const *b)
- {
- // This allows the BEGIN/END/ERROR probes to intermingle.
- // But that's OK - they're always treversed with a nested
- // "if (type==FOO)" conditional.
- return a->priority < b->priority;
- }
-
- bool needs_global_locks () { return false; }
- // begin/end probes don't need locks around global variables, since
- // they aren't run concurrently with any other probes
-};
-
-
-struct be_derived_probe_group: public generic_dpg<be_derived_probe>
-{
-public:
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-struct be_builder: public derived_probe_builder
-{
- be_t type;
-
- be_builder(be_t t) : type(t) {}
-
- virtual void build(systemtap_session &,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
- {
- int64_t priority;
- if ((type == BEGIN && !get_param(parameters, TOK_BEGIN, priority)) ||
- (type == END && !get_param(parameters, TOK_END, priority)) ||
- (type == ERROR && !get_param(parameters, TOK_ERROR, priority)))
- priority = 0;
- finished_results.push_back(
- new be_derived_probe(base, location, type, priority));
- }
-};
-
-
-void
-be_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.be_derived_probes)
- s.be_derived_probes = new be_derived_probe_group ();
- s.be_derived_probes->enroll (this);
-}
// ------------------------------------------------------------------------
void
common_probe_entryfn_prologue (translator_output* o, string statestr,
string new_pp,
- bool overload_processing = true)
+ bool overload_processing)
{
o->newline() << "struct context* __restrict__ c;";
o->newline() << "#if !INTERRUPTIBLE";
@@ -174,15 +82,6 @@ common_probe_entryfn_prologue (translator_output* o, string statestr,
o->newline() << "cycles_t cycles_atstart = get_cycles ();";
o->newline() << "#endif";
-#if 0 /* XXX: PERFMON */
- o->newline() << "static struct pfarg_ctx _pfm_context;";
- o->newline() << "static void *_pfm_desc;";
- o->newline() << "static struct pfarg_pmc *_pfm_pmc_x;";
- o->newline() << "static int _pfm_num_pmc_x;";
- o->newline() << "static struct pfarg_pmd *_pfm_pmd_x;";
- o->newline() << "static int _pfm_num_pmd_x;";
-#endif
-
o->newline() << "#if INTERRUPTIBLE";
o->newline() << "preempt_disable ();";
o->newline() << "#else";
@@ -261,7 +160,7 @@ common_probe_entryfn_prologue (translator_output* o, string statestr,
void
common_probe_entryfn_epilogue (translator_output* o,
- bool overload_processing = true)
+ bool overload_processing)
{
if (overload_processing)
o->newline() << "#if defined(STP_TIMING) || defined(STP_OVERLOAD)";
@@ -347,124 +246,6 @@ common_probe_entryfn_epilogue (translator_output* o,
// ------------------------------------------------------------------------
-void
-be_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "/* ---- begin/end probes ---- */";
- s.op->newline() << "static void enter_begin_probe (void (*fn)(struct context*), const char* pp) {";
- s.op->indent(1);
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_STARTING", "pp", false);
- s.op->newline() << "(*fn) (c);";
- common_probe_entryfn_epilogue (s.op, false);
- s.op->newline(-1) << "}";
-
- s.op->newline() << "static void enter_end_probe (void (*fn)(struct context*), const char* pp) {";
- s.op->indent(1);
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_STOPPING", "pp", false);
- s.op->newline() << "(*fn) (c);";
- common_probe_entryfn_epilogue (s.op, false);
- s.op->newline(-1) << "}";
-
- s.op->newline() << "static void enter_error_probe (void (*fn)(struct context*), const char* pp) {";
- s.op->indent(1);
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_ERROR", "pp", false);
- s.op->newline() << "(*fn) (c);";
- common_probe_entryfn_epilogue (s.op, false);
- s.op->newline(-1) << "}";
-
- s.op->newline() << "static struct stap_be_probe {";
- s.op->newline(1) << "void (*ph)(struct context*);";
- s.op->newline() << "const char* pp;";
- s.op->newline() << "int type;";
- s.op->newline(-1) << "} stap_be_probes[] = {";
- s.op->indent(1);
-
- // NB: We emit the table in sorted order here, so we don't have to
- // store the priority numbers as integers and sort at run time.
-
- sort(probes.begin(), probes.end(), be_derived_probe::comp);
-
- for (unsigned i=0; i < probes.size(); i++)
- {
- s.op->newline () << "{";
- s.op->line() << " .pp="
- << lex_cast_qstring (*probes[i]->sole_location()) << ",";
- s.op->line() << " .ph=&" << probes[i]->name << ",";
- s.op->line() << " .type=" << probes[i]->type;
- s.op->line() << " },";
- }
- s.op->newline(-1) << "};";
-}
-
-void
-be_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_be_probe* stp = & stap_be_probes [i];";
- s.op->newline() << "if (stp->type != " << BEGIN << ") continue;";
- s.op->newline() << "enter_begin_probe (stp->ph, stp->pp);";
- s.op->newline() << "/* rc = 0; */";
- // NB: begin probes that cause errors do not constitute registration
- // failures. An error message will probably get printed and if
- // MAXERRORS was left at 1, we'll get an stp_exit. The
- // error-handling probes will be run during the ordinary
- // unregistration phase.
- s.op->newline(-1) << "}";
-}
-
-void
-be_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_be_probe* stp = & stap_be_probes [i];";
- s.op->newline() << "if (stp->type != " << END << ") continue;";
- s.op->newline() << "enter_end_probe (stp->ph, stp->pp);";
- s.op->newline(-1) << "}";
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_be_probe* stp = & stap_be_probes [i];";
- s.op->newline() << "if (stp->type != " << ERROR << ") continue;";
- s.op->newline() << "enter_error_probe (stp->ph, stp->pp);";
- s.op->newline(-1) << "}";
-}
-
-
-
-// ------------------------------------------------------------------------
-// never probes are never run
-// ------------------------------------------------------------------------
-
-static string TOK_NEVER("never");
-
-struct never_derived_probe: public derived_probe
-{
- never_derived_probe (probe* p): derived_probe (p) {}
- never_derived_probe (probe* p, probe_point* l): derived_probe (p, l) {}
- void join_group (systemtap_session&) { /* thus no probe_group */ }
-};
-
-
-struct never_builder: public derived_probe_builder
-{
- never_builder() {}
- virtual void build(systemtap_session &,
- probe * base,
- probe_point * location,
- literal_map_t const &,
- vector<derived_probe *> & finished_results)
- {
- finished_results.push_back(new never_derived_probe(base, location));
- }
-};
-
-
-
// ------------------------------------------------------------------------
// Dwarf derived probes. "We apologize for the inconvience."
// ------------------------------------------------------------------------
@@ -2635,41 +2416,6 @@ struct uprobe_derived_probe: public derived_probe
void join_group (systemtap_session& s);
};
-struct kprobe_derived_probe: public derived_probe
-{
- kprobe_derived_probe (probe *base,
- probe_point *location,
- const string& name,
- int64_t stmt_addr,
- bool has_return,
- bool has_statement,
- bool has_maxactive,
- long maxactive_val
- );
- string symbol_name;
- Dwarf_Addr addr;
- bool has_return;
- bool has_statement;
- bool has_maxactive;
- long maxactive_val;
- bool access_var;
- void printsig (std::ostream &o) const;
- void join_group (systemtap_session& s);
-};
-
-struct kprobe_derived_probe_group: public derived_probe_group
-{
-private:
- multimap<string,kprobe_derived_probe*> probes_by_module;
- typedef multimap<string,kprobe_derived_probe*>::iterator p_b_m_iterator;
-
-public:
- void enroll (kprobe_derived_probe* probe);
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
struct dwarf_derived_probe_group: public derived_probe_group
{
private:
@@ -4476,15 +4222,6 @@ dwflpp::query_modules(base_query *q)
iterate_over_modules(&query_module, q);
}
-struct var_expanding_visitor: public update_visitor
-{
- static unsigned tick;
- stack<functioncall**> target_symbol_setter_functioncalls;
-
- var_expanding_visitor() {}
- void visit_assignment (assignment* e);
-};
-
struct dwarf_var_expanding_visitor: public var_expanding_visitor
{
@@ -5587,24 +5324,6 @@ dwarf_derived_probe_group::enroll (dwarf_derived_probe* p)
// sequentially.
}
-/*
-void
-dwarf_derived_probe_group::enroll (kprobe_derived_probe* p)
-{
- dwarf_derived_probe *dw_probe = new dwarf_derived_probe (p->symbol_name,
- "",0,
- p->module_name,
- p->section_name,
- 0,0,
- p->q,NULL);
- probes_by_module.insert (make_pair (p->module, p));
-
- // XXX: probes put at the same address should all share a
- // single kprobe/kretprobe, and have their handlers executed
- // sequentially.
-}
-*/
-
void
dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
{
@@ -6432,1387 +6151,6 @@ module_info::~module_info()
delete sym_table;
}
-// Helper function to emit vma tracker callbacks.
-static void
-emit_vma_callback_probe_decl (systemtap_session& s,
- string path,
- int64_t pid)
-{
- s.op->newline() << "{";
- if (pid == 0)
- {
- s.op->line() << " .pathname=\"" << path << "\",";
- s.op->line() << " .pid=0,";
- }
- else
- {
- s.op->line() << " .pathname=NULL,";
- s.op->line() << " .pid=" << pid << ",";
- }
- s.op->line() << " .callback=NULL,";
- s.op->line() << " .mmap_callback=&_stp_tf_mmap_cb,";
- s.op->line() << " .munmap_callback=&_stp_tf_munmap_cb,";
- s.op->line() << " .mprotect_callback=NULL,";
- s.op->line() << " },";
-}
-
-// ------------------------------------------------------------------------
-// task_finder derived 'probes': These don't really exist. The whole
-// purpose of the task_finder_derived_probe_group is to make sure that
-// stap_start_task_finder()/stap_stop_task_finder() get called only
-// once and in the right place.
-// ------------------------------------------------------------------------
-
-struct task_finder_derived_probe: public derived_probe
-{
- // Dummy constructor for gcc 3.4 compatibility
- task_finder_derived_probe (): derived_probe (0) { assert(0); }
-};
-
-
-struct task_finder_derived_probe_group: public generic_dpg<task_finder_derived_probe>
-{
-public:
- static void create_session_group (systemtap_session& s);
-
- void emit_module_decls (systemtap_session& ) { }
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-void
-task_finder_derived_probe_group::create_session_group (systemtap_session& s)
-{
- if (! s.task_finder_derived_probes)
- s.task_finder_derived_probes = new task_finder_derived_probe_group();
-}
-
-
-void
-task_finder_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- s.op->newline();
- s.op->newline() << "/* ---- task finder ---- */";
- s.op->newline() << "rc = stap_start_task_finder();";
-
- s.op->newline() << "if (rc) {";
- s.op->newline(1) << "stap_stop_task_finder();";
- s.op->newline(-1) << "}";
-}
-
-
-void
-task_finder_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- s.op->newline();
- s.op->newline() << "/* ---- task finder ---- */";
- s.op->newline() << "stap_stop_task_finder();";
-}
-
-// ------------------------------------------------------------------------
-// itrace user-space probes
-// ------------------------------------------------------------------------
-
-
-static string TOK_INSN("insn");
-static string TOK_BLOCK("block");
-
-struct itrace_derived_probe: public derived_probe
-{
- bool has_path;
- string path;
- int64_t pid;
- int single_step;
-
- itrace_derived_probe (systemtap_session &s, probe* p, probe_point* l,
- bool hp, string &pn, int64_t pd, int ss
- );
- void join_group (systemtap_session& s);
-};
-
-
-struct itrace_derived_probe_group: public generic_dpg<itrace_derived_probe>
-{
-private:
- map<string, vector<itrace_derived_probe*> > probes_by_path;
- typedef map<string, vector<itrace_derived_probe*> >::iterator p_b_path_iterator;
- map<int64_t, vector<itrace_derived_probe*> > probes_by_pid;
- typedef map<int64_t, vector<itrace_derived_probe*> >::iterator p_b_pid_iterator;
- unsigned num_probes;
-
- void emit_probe_decl (systemtap_session& s, itrace_derived_probe *p);
-
-public:
- itrace_derived_probe_group(): num_probes(0) { }
-
- void enroll (itrace_derived_probe* probe);
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-itrace_derived_probe::itrace_derived_probe (systemtap_session &s,
- probe* p, probe_point* l,
- bool hp, string &pn, int64_t pd,
- int ss
- ):
- derived_probe(p, l), has_path(hp), path(pn), pid(pd), single_step(ss)
-{
-}
-
-
-void
-itrace_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.itrace_derived_probes)
- s.itrace_derived_probes = new itrace_derived_probe_group ();
-
- s.itrace_derived_probes->enroll (this);
-
- task_finder_derived_probe_group::create_session_group (s);
-}
-
-struct itrace_builder: public derived_probe_builder
-{
- itrace_builder() {}
- virtual void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- std::map<std::string, literal *> const & parameters,
- vector<derived_probe *> & finished_results)
- {
- string path;
- int64_t pid = 0;
- int single_step;
-
- bool has_path = get_param (parameters, TOK_PROCESS, path);
- bool has_pid = get_param (parameters, TOK_PROCESS, pid);
- // XXX: PR 6445 needs !has_path && !has_pid support
- assert (has_path || has_pid);
-
- single_step = ! has_null_param (parameters, TOK_BLOCK);
-
- // If we have a path, we need to validate it.
- if (has_path)
- path = find_executable (path);
-
- finished_results.push_back(new itrace_derived_probe(sess, base, location,
- has_path, path, pid,
- single_step
- ));
- }
-};
-
-
-void
-itrace_derived_probe_group::enroll (itrace_derived_probe* p)
-{
- if (p->has_path)
- probes_by_path[p->path].push_back(p);
- else
- probes_by_pid[p->pid].push_back(p);
- num_probes++;
-
- // XXX: multiple exec probes (for instance) for the same path (or
- // pid) should all share a itrace report function, and have their
- // handlers executed sequentially.
-}
-
-
-void
-itrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
- itrace_derived_probe *p)
-{
- s.op->newline() << "{";
- s.op->line() << " .tgt={";
-
- if (p->has_path)
- {
- s.op->line() << " .pathname=\"" << p->path << "\",";
- s.op->line() << " .pid=0,";
- }
- else
- {
- s.op->line() << " .pathname=NULL,";
- s.op->line() << " .pid=" << p->pid << ",";
- }
-
- s.op->line() << " .callback=&_stp_itrace_probe_cb,";
- s.op->line() << " },";
- s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
- s.op->line() << " .single_step=" << p->single_step << ",";
- s.op->line() << " .ph=&" << p->name << ",";
-
- s.op->line() << " },";
-}
-
-
-void
-itrace_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes_by_path.empty() && probes_by_pid.empty())
- return;
-
- s.op->newline();
- s.op->newline() << "/* ---- itrace probes ---- */";
-
- s.op->newline() << "struct stap_itrace_probe {";
- s.op->indent(1);
- s.op->newline() << "struct stap_task_finder_target tgt;";
- s.op->newline() << "const char *pp;";
- s.op->newline() << "void (*ph) (struct context*);";
- s.op->newline() << "int single_step;";
- s.op->newline(-1) << "};";
- s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data);";
- s.op->newline() << "#include \"itrace.c\"";
-
- // output routine to call itrace probe
- s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data) {";
- s.op->indent(1);
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->pp");
- s.op->newline() << "c->regs = regs;";
- s.op->newline() << "c->data = data;";
-
- // call probe function
- s.op->newline() << "(*p->ph) (c);";
- common_probe_entryfn_epilogue (s.op);
-
- s.op->newline() << "return;";
- s.op->newline(-1) << "}";
-
- // Output task finder callback routine that gets called for all
- // itrace probe types.
- s.op->newline() << "static int _stp_itrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {";
- s.op->indent(1);
- s.op->newline() << "int rc = 0;";
- s.op->newline() << "struct stap_itrace_probe *p = container_of(tgt, struct stap_itrace_probe, tgt);";
-
- s.op->newline() << "if (register_p) ";
- s.op->indent(1);
-
- s.op->newline() << "rc = usr_itrace_init(p->single_step, tsk->pid, p);";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "remove_usr_itrace_info(find_itrace_info(p->tgt.pid));";
- s.op->newline(-1) << "return rc;";
- s.op->newline(-1) << "}";
-
- // Emit vma callbacks.
- s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
- s.op->newline() << "static struct stap_task_finder_target stap_itrace_vmcbs[] = {";
- s.op->indent(1);
- if (! probes_by_path.empty())
- {
- for (p_b_path_iterator it = probes_by_path.begin();
- it != probes_by_path.end(); it++)
- emit_vma_callback_probe_decl (s, it->first, (int64_t)0);
- }
- if (! probes_by_pid.empty())
- {
- for (p_b_pid_iterator it = probes_by_pid.begin();
- it != probes_by_pid.end(); it++)
- emit_vma_callback_probe_decl (s, "", it->first);
- }
- s.op->newline(-1) << "};";
- s.op->newline() << "#endif";
-
- s.op->newline() << "static struct stap_itrace_probe stap_itrace_probes[] = {";
- s.op->indent(1);
-
- // Set up 'process(PATH)' probes
- if (! probes_by_path.empty())
- {
- for (p_b_path_iterator it = probes_by_path.begin();
- it != probes_by_path.end(); it++)
- {
- for (unsigned i = 0; i < it->second.size(); i++)
- {
- itrace_derived_probe *p = it->second[i];
- emit_probe_decl(s, p);
- }
- }
- }
-
- // Set up 'process(PID)' probes
- if (! probes_by_pid.empty())
- {
- for (p_b_pid_iterator it = probes_by_pid.begin();
- it != probes_by_pid.end(); it++)
- {
- for (unsigned i = 0; i < it->second.size(); i++)
- {
- itrace_derived_probe *p = it->second[i];
- emit_probe_decl(s, p);
- }
- }
- }
- s.op->newline(-1) << "};";
-}
-
-
-void
-itrace_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes_by_path.empty() && probes_by_pid.empty())
- return;
-
- s.op->newline();
- s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
- s.op->newline() << "_stp_sym_init();";
- s.op->newline() << "/* ---- itrace vma callbacks ---- */";
- s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_itrace_vmcbs); i++) {";
- s.op->indent(1);
- s.op->newline() << "struct stap_task_finder_target *r = &stap_itrace_vmcbs[i];";
- s.op->newline() << "rc = stap_register_task_finder_target(r);";
- s.op->newline(-1) << "}";
- s.op->newline() << "#endif";
-
- s.op->newline();
- s.op->newline() << "/* ---- itrace probes ---- */";
-
- s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {";
- s.op->indent(1);
- s.op->newline() << "struct stap_itrace_probe *p = &stap_itrace_probes[i];";
-
- // 'arch_has_single_step' needs to be defined for either single step mode
- // or branch mode.
- s.op->newline() << "if (!arch_has_single_step()) {";
- s.op->indent(1);
- s.op->newline() << "_stp_error (\"insn probe init: arch does not support step mode\");";
- s.op->newline() << "rc = -EPERM;";
- s.op->newline() << "break;";
- s.op->newline(-1) << "}";
- s.op->newline() << "if (!p->single_step && !arch_has_block_step()) {";
- s.op->indent(1);
- s.op->newline() << "_stp_error (\"insn probe init: arch does not support block step mode\");";
- s.op->newline() << "rc = -EPERM;";
- s.op->newline() << "break;";
- s.op->newline(-1) << "}";
-
- s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);";
- s.op->newline(-1) << "}";
-}
-
-
-void
-itrace_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes_by_path.empty() && probes_by_pid.empty()) return;
- s.op->newline();
- s.op->newline() << "/* ---- itrace probes ---- */";
- s.op->newline() << "cleanup_usr_itrace();";
-}
-
-// ------------------------------------------------------------------------
-// utrace user-space probes
-// ------------------------------------------------------------------------
-
-static string TOK_THREAD("thread");
-static string TOK_SYSCALL("syscall");
-
-// Note that these flags don't match up exactly with UTRACE_EVENT
-// flags (and that's OK).
-enum utrace_derived_probe_flags {
- UDPF_NONE,
- UDPF_BEGIN, // process begin
- UDPF_END, // process end
- UDPF_THREAD_BEGIN, // thread begin
- UDPF_THREAD_END, // thread end
- UDPF_SYSCALL, // syscall entry
- UDPF_SYSCALL_RETURN, // syscall exit
- UDPF_NFLAGS
-};
-
-struct utrace_derived_probe: public derived_probe
-{
- bool has_path;
- string path;
- int64_t pid;
- enum utrace_derived_probe_flags flags;
- bool target_symbol_seen;
-
- utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l,
- bool hp, string &pn, int64_t pd,
- enum utrace_derived_probe_flags f);
- void join_group (systemtap_session& s);
-};
-
-
-struct utrace_derived_probe_group: public generic_dpg<utrace_derived_probe>
-{
-private:
- map<string, vector<utrace_derived_probe*> > probes_by_path;
- typedef map<string, vector<utrace_derived_probe*> >::iterator p_b_path_iterator;
- map<int64_t, vector<utrace_derived_probe*> > probes_by_pid;
- typedef map<int64_t, vector<utrace_derived_probe*> >::iterator p_b_pid_iterator;
- unsigned num_probes;
- bool flags_seen[UDPF_NFLAGS];
-
- void emit_probe_decl (systemtap_session& s, utrace_derived_probe *p);
-
-public:
- utrace_derived_probe_group(): num_probes(0), flags_seen() { }
-
- void enroll (utrace_derived_probe* probe);
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-struct utrace_var_expanding_visitor: public var_expanding_visitor
-{
- utrace_var_expanding_visitor(systemtap_session& s, probe_point* l,
- const string& pn,
- enum utrace_derived_probe_flags f):
- sess (s), base_loc (l), probe_name (pn), flags (f),
- target_symbol_seen (false), add_block(NULL), add_probe(NULL) {}
-
- systemtap_session& sess;
- probe_point* base_loc;
- string probe_name;
- enum utrace_derived_probe_flags flags;
- bool target_symbol_seen;
- block *add_block;
- probe *add_probe;
- std::map<std::string, symbol *> return_ts_map;
-
- void visit_target_symbol_arg (target_symbol* e);
- void visit_target_symbol_context (target_symbol* e);
- void visit_target_symbol_cached (target_symbol* e);
- void visit_target_symbol (target_symbol* e);
-};
-
-
-
-utrace_derived_probe::utrace_derived_probe (systemtap_session &s,
- probe* p, probe_point* l,
- bool hp, string &pn, int64_t pd,
- enum utrace_derived_probe_flags f):
- derived_probe (p, new probe_point (*l) /* .components soon rewritten */ ),
- has_path(hp), path(pn), pid(pd), flags(f),
- target_symbol_seen(false)
-{
- // Expand local variables in the probe body
- utrace_var_expanding_visitor v (s, l, name, flags);
- this->body = v.require (this->body);
- target_symbol_seen = v.target_symbol_seen;
-
- // If during target-variable-expanding the probe, we added a new block
- // of code, add it to the start of the probe.
- if (v.add_block)
- this->body = new block(v.add_block, this->body);
- // If when target-variable-expanding the probe, we added a new
- // probe, add it in a new file to the list of files to be processed.
- if (v.add_probe)
- {
- stapfile *f = new stapfile;
- f->probes.push_back(v.add_probe);
- s.files.push_back(f);
- }
-
- // Reset the sole element of the "locations" vector as a
- // "reverse-engineered" form of the incoming (q.base_loc) probe
- // point. This allows a user to see what program etc.
- // number any particular match of the wildcards.
-
- vector<probe_point::component*> comps;
- if (hp)
- comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(path)));
- else if (pid != 0)
- comps.push_back (new probe_point::component(TOK_PROCESS, new literal_number(pid)));
- else
- comps.push_back (new probe_point::component(TOK_PROCESS));
-
- switch (flags)
- {
- case UDPF_THREAD_BEGIN:
- comps.push_back (new probe_point::component(TOK_THREAD));
- comps.push_back (new probe_point::component(TOK_BEGIN));
- break;
- case UDPF_THREAD_END:
- comps.push_back (new probe_point::component(TOK_THREAD));
- comps.push_back (new probe_point::component(TOK_END));
- break;
- case UDPF_SYSCALL:
- comps.push_back (new probe_point::component(TOK_SYSCALL));
- break;
- case UDPF_SYSCALL_RETURN:
- comps.push_back (new probe_point::component(TOK_SYSCALL));
- comps.push_back (new probe_point::component(TOK_RETURN));
- break;
- case UDPF_BEGIN:
- comps.push_back (new probe_point::component(TOK_BEGIN));
- break;
- case UDPF_END:
- comps.push_back (new probe_point::component(TOK_END));
- break;
- default:
- assert (0);
- }
-
- // Overwrite it.
- this->sole_location()->components = comps;
-}
-
-
-void
-utrace_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.utrace_derived_probes)
- {
- s.utrace_derived_probes = new utrace_derived_probe_group ();
- }
- s.utrace_derived_probes->enroll (this);
-
- task_finder_derived_probe_group::create_session_group (s);
-}
-
-
-void
-utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e)
-{
- // Get the full name of the target symbol.
- stringstream ts_name_stream;
- e->print(ts_name_stream);
- string ts_name = ts_name_stream.str();
-
- // Check and make sure we haven't already seen this target
- // variable in this return probe. If we have, just return our
- // last replacement.
- map<string, symbol *>::iterator i = return_ts_map.find(ts_name);
- if (i != return_ts_map.end())
- {
- provide (i->second);
- return;
- }
-
- // We've got to do several things here to handle target
- // variables in return probes.
-
- // (1) Synthesize a global array which is the cache of the
- // target variable value. We don't need a nesting level counter
- // like the dwarf_var_expanding_visitor::visit_target_symbol()
- // does since a particular thread can only be in one system
- // calls at a time. The array will look like this:
- //
- // _utrace_tvar_{name}_{num}
- string aname = (string("_utrace_tvar_")
- + e->base_name.substr(1)
- + "_" + lex_cast<string>(tick++));
- vardecl* vd = new vardecl;
- vd->name = aname;
- vd->tok = e->tok;
- sess.globals.push_back (vd);
-
- // (2) Create a new code block we're going to insert at the
- // beginning of this probe to get the cached value into a
- // temporary variable. We'll replace the target variable
- // reference with the temporary variable reference. The code
- // will look like this:
- //
- // _utrace_tvar_tid = tid()
- // _utrace_tvar_{name}_{num}_tmp
- // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
- // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
-
- // (2a) Synthesize the tid temporary expression, which will look
- // like this:
- //
- // _utrace_tvar_tid = tid()
- symbol* tidsym = new symbol;
- tidsym->name = string("_utrace_tvar_tid");
- tidsym->tok = e->tok;
-
- if (add_block == NULL)
- {
- add_block = new block;
- add_block->tok = e->tok;
-
- // Synthesize a functioncall to grab the thread id.
- functioncall* fc = new functioncall;
- fc->tok = e->tok;
- fc->function = string("tid");
-
- // Assign the tid to '_utrace_tvar_tid'.
- assignment* a = new assignment;
- a->tok = e->tok;
- a->op = "=";
- a->left = tidsym;
- a->right = fc;
-
- expr_statement* es = new expr_statement;
- es->tok = e->tok;
- es->value = a;
- add_block->statements.push_back (es);
- }
-
- // (2b) Synthesize an array reference and assign it to a
- // temporary variable (that we'll use as replacement for the
- // target variable reference). It will look like this:
- //
- // _utrace_tvar_{name}_{num}_tmp
- // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
-
- arrayindex* ai_tvar = new arrayindex;
- ai_tvar->tok = e->tok;
-
- symbol* sym = new symbol;
- sym->name = aname;
- sym->tok = e->tok;
- ai_tvar->base = sym;
-
- ai_tvar->indexes.push_back(tidsym);
-
- symbol* tmpsym = new symbol;
- tmpsym->name = aname + "_tmp";
- tmpsym->tok = e->tok;
-
- assignment* a = new assignment;
- a->tok = e->tok;
- a->op = "=";
- a->left = tmpsym;
- a->right = ai_tvar;
-
- expr_statement* es = new expr_statement;
- es->tok = e->tok;
- es->value = a;
-
- add_block->statements.push_back (es);
-
- // (2c) Delete the array value. It will look like this:
- //
- // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
-
- delete_statement* ds = new delete_statement;
- ds->tok = e->tok;
- ds->value = ai_tvar;
- add_block->statements.push_back (ds);
-
- // (3) We need an entry probe that saves the value for us in the
- // global array we created. Create the entry probe, which will
- // look like this:
- //
- // probe process(PATH_OR_PID).syscall {
- // _utrace_tvar_tid = tid()
- // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
- // }
- //
- // Why the temporary for tid()? If we end up caching more
- // than one target variable, we can reuse the temporary instead
- // of calling tid() multiple times.
-
- if (add_probe == NULL)
- {
- add_probe = new probe;
- add_probe->tok = e->tok;
-
- // We need the name of the current probe point, minus the
- // ".return". Create a new probe point, copying all the
- // components, stopping when we see the ".return"
- // component.
- probe_point* pp = new probe_point;
- for (unsigned c = 0; c < base_loc->components.size(); c++)
- {
- if (base_loc->components[c]->functor == "return")
- break;
- else
- pp->components.push_back(base_loc->components[c]);
- }
- pp->tok = e->tok;
- pp->optional = base_loc->optional;
- add_probe->locations.push_back(pp);
-
- add_probe->body = new block;
- add_probe->body->tok = e->tok;
-
- // Synthesize a functioncall to grab the thread id.
- functioncall* fc = new functioncall;
- fc->tok = e->tok;
- fc->function = string("tid");
-
- // Assign the tid to '_utrace_tvar_tid'.
- assignment* a = new assignment;
- a->tok = e->tok;
- a->op = "=";
- a->left = tidsym;
- a->right = fc;
-
- expr_statement* es = new expr_statement;
- es->tok = e->tok;
- es->value = a;
- add_probe->body = new block(add_probe->body, es);
-
- vardecl* vd = new vardecl;
- vd->tok = e->tok;
- vd->name = tidsym->name;
- vd->type = pe_long;
- vd->set_arity(0);
- add_probe->locals.push_back(vd);
- }
-
- // Save the value, like this:
- //
- // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
- a = new assignment;
- a->tok = e->tok;
- a->op = "=";
- a->left = ai_tvar;
- a->right = e;
-
- es = new expr_statement;
- es->tok = e->tok;
- es->value = a;
-
- add_probe->body = new block(add_probe->body, es);
-
- // (4) Provide the '_utrace_tvar_{name}_{num}_tmp' variable to
- // our parent so it can be used as a substitute for the target
- // symbol.
- provide (tmpsym);
-
- // (5) Remember this replacement since we might be able to reuse
- // it later if the same return probe references this target
- // symbol again.
- return_ts_map[ts_name] = tmpsym;
- return;
-}
-
-
-void
-utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
-{
- string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
- int argnum = lex_cast<int>(argnum_s);
-
- if (flags != UDPF_SYSCALL)
- throw semantic_error ("only \"process(PATH_OR_PID).syscall\" support $argN.", e->tok);
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("utrace target variable '$argN' may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("utrace target variable '$argN' may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid use of utrace target variable '$argN'",
- e->tok);
- break;
- }
- }
-
- // FIXME: max argnument number should not be hardcoded.
- if (argnum < 1 || argnum > 6)
- throw semantic_error ("invalid syscall argument number (1-6)", e->tok);
-
- bool lvalue = is_active_lvalue(e);
- if (lvalue)
- throw semantic_error("utrace '$argN' variable is read-only", e->tok);
-
- // Remember that we've seen a target variable.
- target_symbol_seen = true;
-
- // We're going to substitute a synthesized '_utrace_syscall_arg'
- // function call for the '$argN' reference.
- functioncall* n = new functioncall;
- n->tok = e->tok;
- n->function = "_utrace_syscall_arg";
- n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
-
- literal_number *num = new literal_number(argnum - 1);
- num->tok = e->tok;
- n->args.push_back(num);
-
- provide (n);
-}
-
-void
-utrace_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
-{
- string sname = e->base_name;
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("utrace target variable '" + sname + "' may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("utrace target variable '" + sname + "' may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid use of utrace target variable '" + sname + "'",
- e->tok);
- break;
- }
- }
-
- bool lvalue = is_active_lvalue(e);
- if (lvalue)
- throw semantic_error("utrace '" + sname + "' variable is read-only", e->tok);
-
- string fname;
- if (sname == "$return")
- {
- if (flags != UDPF_SYSCALL_RETURN)
- throw semantic_error ("only \"process(PATH_OR_PID).syscall.return\" support $return.", e->tok);
- fname = "_utrace_syscall_return";
- }
- else if (sname == "$syscall")
- {
- // If we've got a syscall entry probe, we can just call the
- // right function.
- if (flags == UDPF_SYSCALL) {
- fname = "_utrace_syscall_nr";
- }
- // If we're in a syscal return probe, we can't really access
- // $syscall. So, similar to what
- // dwarf_var_expanding_visitor::visit_target_symbol() does,
- // we'll create an syscall entry probe to cache $syscall, then
- // we'll access the cached value in the syscall return probe.
- else {
- visit_target_symbol_cached (e);
-
- // Remember that we've seen a target variable.
- target_symbol_seen = true;
- return;
- }
- }
- else
- {
- throw semantic_error ("unknown target variable", e->tok);
- }
-
- // Remember that we've seen a target variable.
- target_symbol_seen = true;
-
- // We're going to substitute a synthesized '_utrace_syscall_nr'
- // function call for the '$syscall' reference.
- functioncall* n = new functioncall;
- n->tok = e->tok;
- n->function = fname;
- n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
-
- provide (n);
-}
-
-void
-utrace_var_expanding_visitor::visit_target_symbol (target_symbol* e)
-{
- assert(e->base_name.size() > 0 && e->base_name[0] == '$');
-
- if (flags != UDPF_SYSCALL && flags != UDPF_SYSCALL_RETURN)
- throw semantic_error ("only \"process(PATH_OR_PID).syscall\" and \"process(PATH_OR_PID).syscall.return\" probes support target symbols",
- e->tok);
-
- if (e->base_name.substr(0,4) == "$arg")
- visit_target_symbol_arg(e);
- else if (e->base_name == "$syscall" || e->base_name == "$return")
- visit_target_symbol_context(e);
- else
- throw semantic_error ("invalid target symbol for utrace probe, $syscall, $return or $argN expected",
- e->tok);
-}
-
-
-struct utrace_builder: public derived_probe_builder
-{
- utrace_builder() {}
- virtual void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
- {
- string path;
- int64_t pid;
-
- bool has_path = get_param (parameters, TOK_PROCESS, path);
- bool has_pid = get_param (parameters, TOK_PROCESS, pid);
- enum utrace_derived_probe_flags flags = UDPF_NONE;
-
- if (has_null_param (parameters, TOK_THREAD))
- {
- if (has_null_param (parameters, TOK_BEGIN))
- flags = UDPF_THREAD_BEGIN;
- else if (has_null_param (parameters, TOK_END))
- flags = UDPF_THREAD_END;
- }
- else if (has_null_param (parameters, TOK_SYSCALL))
- {
- if (has_null_param (parameters, TOK_RETURN))
- flags = UDPF_SYSCALL_RETURN;
- else
- flags = UDPF_SYSCALL;
- }
- else if (has_null_param (parameters, TOK_BEGIN))
- flags = UDPF_BEGIN;
- else if (has_null_param (parameters, TOK_END))
- flags = UDPF_END;
-
- // If we didn't get a path or pid, this means to probe everything.
- // Convert this to a pid-based probe.
- if (! has_path && ! has_pid)
- {
- has_path = false;
- path.clear();
- has_pid = true;
- pid = 0;
- }
- else if (has_path)
- {
- path = find_executable (path);
- sess.unwindsym_modules.insert (path);
- }
- else if (has_pid)
- {
- // We can't probe 'init' (pid 1). XXX: where does this limitation come from?
- if (pid < 2)
- throw semantic_error ("process pid must be greater than 1",
- location->tok);
-
- // XXX: could we use /proc/$pid/exe in unwindsym_modules and elsewhere?
- }
-
- finished_results.push_back(new utrace_derived_probe(sess, base, location,
- has_path, path, pid,
- flags));
- }
-};
-
-
-void
-utrace_derived_probe_group::enroll (utrace_derived_probe* p)
-{
- if (p->has_path)
- probes_by_path[p->path].push_back(p);
- else
- probes_by_pid[p->pid].push_back(p);
- num_probes++;
- flags_seen[p->flags] = true;
-
- // XXX: multiple exec probes (for instance) for the same path (or
- // pid) should all share a utrace report function, and have their
- // handlers executed sequentially.
-}
-
-
-void
-utrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
- utrace_derived_probe *p)
-{
- s.op->newline() << "{";
- s.op->line() << " .tgt={";
-
- if (p->has_path)
- {
- s.op->line() << " .pathname=\"" << p->path << "\",";
- s.op->line() << " .pid=0,";
- }
- else
- {
- s.op->line() << " .pathname=NULL,";
- s.op->line() << " .pid=" << p->pid << ",";
- }
-
- s.op->line() << " .callback=&_stp_utrace_probe_cb,";
- s.op->line() << " .mmap_callback=NULL,";
- s.op->line() << " .munmap_callback=NULL,";
- s.op->line() << " .mprotect_callback=NULL,";
- s.op->line() << " },";
- s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
- s.op->line() << " .ph=&" << p->name << ",";
-
- // Handle flags
- switch (p->flags)
- {
- // Notice that we'll just call the probe directly when we get
- // notified, since the task_finder layer stops the thread for us.
- case UDPF_BEGIN: // process begin
- s.op->line() << " .flags=(UDPF_BEGIN),";
- break;
- case UDPF_THREAD_BEGIN: // thread begin
- s.op->line() << " .flags=(UDPF_THREAD_BEGIN),";
- break;
-
- // Notice we're not setting up a .ops/.report_death handler for
- // either UDPF_END or UDPF_THREAD_END. Instead, we'll just call
- // the probe directly when we get notified.
- case UDPF_END: // process end
- s.op->line() << " .flags=(UDPF_END),";
- break;
- case UDPF_THREAD_END: // thread end
- s.op->line() << " .flags=(UDPF_THREAD_END),";
- break;
-
- // For UDPF_SYSCALL/UDPF_SYSCALL_RETURN probes, the .report_death
- // handler isn't strictly necessary. However, it helps to keep
- // our attaches/detaches symmetrical. Since the task_finder layer
- // stops the thread, that works around bug 6841.
- case UDPF_SYSCALL:
- s.op->line() << " .flags=(UDPF_SYSCALL),";
- s.op->line() << " .ops={ .report_syscall_entry=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },";
- s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)),";
- break;
- case UDPF_SYSCALL_RETURN:
- s.op->line() << " .flags=(UDPF_SYSCALL_RETURN),";
- s.op->line() << " .ops={ .report_syscall_exit=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },";
- s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)),";
- break;
-
- case UDPF_NONE:
- s.op->line() << " .flags=(UDPF_NONE),";
- s.op->line() << " .ops={ },";
- s.op->line() << " .events=0,";
- break;
- default:
- throw semantic_error ("bad utrace probe flag");
- break;
- }
- s.op->line() << " .engine_attached=0,";
- s.op->line() << " },";
-}
-
-
-void
-utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes_by_path.empty() && probes_by_pid.empty())
- return;
-
- s.op->newline();
- s.op->newline() << "/* ---- utrace probes ---- */";
-
- s.op->newline() << "enum utrace_derived_probe_flags {";
- s.op->indent(1);
- s.op->newline() << "UDPF_NONE,";
- s.op->newline() << "UDPF_BEGIN,";
- s.op->newline() << "UDPF_END,";
- s.op->newline() << "UDPF_THREAD_BEGIN,";
- s.op->newline() << "UDPF_THREAD_END,";
- s.op->newline() << "UDPF_SYSCALL,";
- s.op->newline() << "UDPF_SYSCALL_RETURN,";
- s.op->newline() << "UDPF_NFLAGS";
- s.op->newline(-1) << "};";
-
- s.op->newline() << "struct stap_utrace_probe {";
- s.op->indent(1);
- s.op->newline() << "struct stap_task_finder_target tgt;";
- s.op->newline() << "const char *pp;";
- s.op->newline() << "void (*ph) (struct context*);";
- s.op->newline() << "enum utrace_derived_probe_flags flags;";
- s.op->newline() << "struct utrace_engine_ops ops;";
- s.op->newline() << "unsigned long events;";
- s.op->newline() << "int engine_attached;";
- s.op->newline(-1) << "};";
-
-
- // Output handler function for UDPF_BEGIN, UDPF_THREAD_BEGIN,
- // UDPF_END, and UDPF_THREAD_END
- if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN]
- || flags_seen[UDPF_END] || flags_seen[UDPF_THREAD_END])
- {
- s.op->newline() << "static void stap_utrace_probe_handler(struct task_struct *tsk, struct stap_utrace_probe *p) {";
- s.op->indent(1);
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->pp");
-
- // call probe function
- s.op->newline() << "(*p->ph) (c);";
- common_probe_entryfn_epilogue (s.op);
-
- s.op->newline() << "return;";
- s.op->newline(-1) << "}";
- }
-
- // Output handler function for SYSCALL_ENTRY and SYSCALL_EXIT events
- if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN])
- {
- s.op->newline() << "#ifdef UTRACE_ORIG_VERSION";
- s.op->newline() << "static u32 stap_utrace_probe_syscall(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {";
- s.op->newline() << "#else";
- s.op->newline() << "static u32 stap_utrace_probe_syscall(enum utrace_resume_action action, struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {";
- s.op->newline() << "#endif";
-
- s.op->indent(1);
- s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;";
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->pp");
- s.op->newline() << "c->regs = regs;";
-
- // call probe function
- s.op->newline() << "(*p->ph) (c);";
- common_probe_entryfn_epilogue (s.op);
-
- s.op->newline() << "if ((atomic_read (&session_state) != STAP_SESSION_STARTING) && (atomic_read (&session_state) != STAP_SESSION_RUNNING)) {";
- s.op->indent(1);
- s.op->newline() << "debug_task_finder_detach();";
- s.op->newline() << "return UTRACE_DETACH;";
- s.op->newline(-1) << "}";
- s.op->newline() << "return UTRACE_RESUME;";
- s.op->newline(-1) << "}";
- }
-
- // Output task_finder callback routine that gets called for all
- // utrace probe types.
- s.op->newline() << "static int _stp_utrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {";
- s.op->indent(1);
- s.op->newline() << "int rc = 0;";
- s.op->newline() << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);";
- s.op->newline() << "struct utrace_attached_engine *engine;";
-
- s.op->newline() << "if (register_p) {";
- s.op->indent(1);
-
- s.op->newline() << "switch (p->flags) {";
- s.op->indent(1);
-
- // When receiving a UTRACE_EVENT(CLONE) event, we can't call the
- // begin/thread.begin probe directly. So, we'll just attach an
- // engine that waits for the thread to quiesce. When the thread
- // quiesces, then call the probe.
- if (flags_seen[UDPF_BEGIN])
- {
- s.op->newline() << "case UDPF_BEGIN:";
- s.op->indent(1);
- s.op->newline() << "if (process_p) {";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
- s.op->newline(-1) << "}";
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
- if (flags_seen[UDPF_THREAD_BEGIN])
- {
- s.op->newline() << "case UDPF_THREAD_BEGIN:";
- s.op->indent(1);
- s.op->newline() << "if (! process_p) {";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
- s.op->newline(-1) << "}";
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
-
- // For end/thread_end probes, do nothing at registration time.
- // We'll handle these in the 'register_p == 0' case.
- if (flags_seen[UDPF_END] || flags_seen[UDPF_THREAD_END])
- {
- s.op->newline() << "case UDPF_END:";
- s.op->newline() << "case UDPF_THREAD_END:";
- s.op->indent(1);
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
-
- // Attach an engine for SYSCALL_ENTRY and SYSCALL_EXIT events.
- if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN])
- {
- s.op->newline() << "case UDPF_SYSCALL:";
- s.op->newline() << "case UDPF_SYSCALL_RETURN:";
- s.op->indent(1);
- s.op->newline() << "rc = stap_utrace_attach(tsk, &p->ops, p, p->events);";
- s.op->newline() << "if (rc == 0) {";
- s.op->indent(1);
- s.op->newline() << "p->engine_attached = 1;";
- s.op->newline(-1) << "}";
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
-
- s.op->newline() << "default:";
- s.op->indent(1);
- s.op->newline() << "_stp_error(\"unhandled flag value %d at %s:%d\", p->flags, __FUNCTION__, __LINE__);";
- s.op->newline() << "break;";
- s.op->indent(-1);
- s.op->newline(-1) << "}";
- s.op->newline(-1) << "}";
-
- // Since this engine could be attached to multiple threads, don't
- // call stap_utrace_detach_ops() here, only call
- // stap_utrace_detach() as necessary.
- s.op->newline() << "else {";
- s.op->indent(1);
- s.op->newline() << "switch (p->flags) {";
- s.op->indent(1);
- // For end probes, go ahead and call the probe directly.
- if (flags_seen[UDPF_END])
- {
- s.op->newline() << "case UDPF_END:";
- s.op->indent(1);
- s.op->newline() << "if (process_p) {";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
- s.op->newline(-1) << "}";
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
- if (flags_seen[UDPF_THREAD_END])
- {
- s.op->newline() << "case UDPF_THREAD_END:";
- s.op->indent(1);
- s.op->newline() << "if (! process_p) {";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
- s.op->newline(-1) << "}";
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
-
- // For begin/thread_begin probes, we don't need to do anything.
- if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN])
- {
- s.op->newline() << "case UDPF_BEGIN:";
- s.op->newline() << "case UDPF_THREAD_BEGIN:";
- s.op->indent(1);
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
-
- if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN])
- {
- s.op->newline() << "case UDPF_SYSCALL:";
- s.op->newline() << "case UDPF_SYSCALL_RETURN:";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_detach(tsk, &p->ops);";
- s.op->newline() << "break;";
- s.op->indent(-1);
- }
-
- s.op->newline() << "default:";
- s.op->indent(1);
- s.op->newline() << "_stp_error(\"unhandled flag value %d at %s:%d\", p->flags, __FUNCTION__, __LINE__);";
- s.op->newline() << "break;";
- s.op->indent(-1);
- s.op->newline(-1) << "}";
- s.op->newline(-1) << "}";
- s.op->newline() << "return rc;";
- s.op->newline(-1) << "}";
-
- // Emit vma callbacks.
- s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
- s.op->newline() << "static struct stap_task_finder_target stap_utrace_vmcbs[] = {";
- s.op->indent(1);
- if (! probes_by_path.empty())
- {
- for (p_b_path_iterator it = probes_by_path.begin();
- it != probes_by_path.end(); it++)
- emit_vma_callback_probe_decl (s, it->first, (int64_t)0);
- }
- if (! probes_by_pid.empty())
- {
- for (p_b_pid_iterator it = probes_by_pid.begin();
- it != probes_by_pid.end(); it++)
- emit_vma_callback_probe_decl (s, "", it->first);
- }
- s.op->newline(-1) << "};";
- s.op->newline() << "#endif";
-
- s.op->newline() << "static struct stap_utrace_probe stap_utrace_probes[] = {";
- s.op->indent(1);
-
- // Set up 'process(PATH)' probes
- if (! probes_by_path.empty())
- {
- for (p_b_path_iterator it = probes_by_path.begin();
- it != probes_by_path.end(); it++)
- {
- for (unsigned i = 0; i < it->second.size(); i++)
- {
- utrace_derived_probe *p = it->second[i];
- emit_probe_decl(s, p);
- }
- }
- }
-
- // Set up 'process(PID)' probes
- if (! probes_by_pid.empty())
- {
- for (p_b_pid_iterator it = probes_by_pid.begin();
- it != probes_by_pid.end(); it++)
- {
- for (unsigned i = 0; i < it->second.size(); i++)
- {
- utrace_derived_probe *p = it->second[i];
- emit_probe_decl(s, p);
- }
- }
- }
- s.op->newline(-1) << "};";
-}
-
-
-void
-utrace_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes_by_path.empty() && probes_by_pid.empty())
- return;
-
- s.op->newline();
- s.op->newline() << "#ifdef STP_NEED_VMA_TRACKER";
- s.op->newline() << "_stp_sym_init();";
- s.op->newline() << "/* ---- utrace vma callbacks ---- */";
- s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_utrace_vmcbs); i++) {";
- s.op->indent(1);
- s.op->newline() << "struct stap_task_finder_target *r = &stap_utrace_vmcbs[i];";
- s.op->newline() << "rc = stap_register_task_finder_target(r);";
- s.op->newline(-1) << "}";
- s.op->newline() << "#endif";
-
- s.op->newline() << "/* ---- utrace probes ---- */";
- s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_utrace_probes); i++) {";
- s.op->indent(1);
- s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];";
- s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);";
- s.op->newline(-1) << "}";
-
- // rollback all utrace probes
- s.op->newline() << "if (rc) {";
- s.op->indent(1);
- s.op->newline() << "for (j=i-1; j>=0; j--) {";
- s.op->indent(1);
- s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[j];";
-
- s.op->newline() << "if (p->engine_attached) {";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_detach_ops(&p->ops);";
- s.op->newline(-1) << "}";
- s.op->newline(-1) << "}";
-
- s.op->newline(-1) << "}";
-}
-
-
-void
-utrace_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes_by_path.empty() && probes_by_pid.empty()) return;
-
- s.op->newline();
- s.op->newline() << "/* ---- utrace probes ---- */";
- s.op->newline() << "for (i=0; i<ARRAY_SIZE(stap_utrace_probes); i++) {";
- s.op->indent(1);
- s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];";
-
- s.op->newline() << "if (p->engine_attached) {";
- s.op->indent(1);
- s.op->newline() << "stap_utrace_detach_ops(&p->ops);";
- s.op->newline(-1) << "}";
- s.op->newline(-1) << "}";
-}
-
-
// ------------------------------------------------------------------------
// user-space probes
// ------------------------------------------------------------------------
@@ -7958,7 +6296,7 @@ uprobe_derived_probe::join_group (systemtap_session& s)
if (! s.uprobe_derived_probes)
s.uprobe_derived_probes = new uprobe_derived_probe_group ();
s.uprobe_derived_probes->enroll (this);
- task_finder_derived_probe_group::create_session_group (s);
+ enable_task_finder(s);
// Ask buildrun.cxx to build extra module if needed, and
// signal staprun to load that module
@@ -8346,6 +6684,41 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
static string TOK_KPROBE("kprobe");
+struct kprobe_derived_probe: public derived_probe
+{
+ kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ int64_t stmt_addr,
+ bool has_return,
+ bool has_statement,
+ bool has_maxactive,
+ long maxactive_val
+ );
+ string symbol_name;
+ Dwarf_Addr addr;
+ bool has_return;
+ bool has_statement;
+ bool has_maxactive;
+ long maxactive_val;
+ bool access_var;
+ void printsig (std::ostream &o) const;
+ void join_group (systemtap_session& s);
+};
+
+struct kprobe_derived_probe_group: public derived_probe_group
+{
+private:
+ multimap<string,kprobe_derived_probe*> probes_by_module;
+ typedef multimap<string,kprobe_derived_probe*>::iterator p_b_m_iterator;
+
+public:
+ void enroll (kprobe_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
kprobe_derived_probe::kprobe_derived_probe (probe *base,
probe_point *location,
const string& name,
@@ -8752,1479 +7125,6 @@ kprobe_builder::build(systemtap_session & sess,
}
-// ------------------------------------------------------------------------
-// timer derived probes
-// ------------------------------------------------------------------------
-
-
-static string TOK_TIMER("timer");
-
-struct timer_derived_probe: public derived_probe
-{
- int64_t interval, randomize;
- bool time_is_msecs; // NB: hrtimers get ms-based probes on modern kernels instead
- timer_derived_probe (probe* p, probe_point* l, int64_t i, int64_t r, bool ms=false);
- virtual void join_group (systemtap_session& s);
-};
-
-
-struct timer_derived_probe_group: public generic_dpg<timer_derived_probe>
-{
- void emit_interval (translator_output* o);
-public:
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-timer_derived_probe::timer_derived_probe (probe* p, probe_point* l, int64_t i, int64_t r, bool ms):
- derived_probe (p, l), interval (i), randomize (r), time_is_msecs(ms)
-{
- if (interval <= 0 || interval > 1000000) // make i and r fit into plain ints
- throw semantic_error ("invalid interval for jiffies timer");
- // randomize = 0 means no randomization
- if (randomize < 0 || randomize > interval)
- throw semantic_error ("invalid randomize for jiffies timer");
-
- if (locations.size() != 1)
- throw semantic_error ("expect single probe point");
- // so we don't have to loop over them in the other functions
-}
-
-
-void
-timer_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.timer_derived_probes)
- s.timer_derived_probes = new timer_derived_probe_group ();
- s.timer_derived_probes->enroll (this);
-}
-
-
-void
-timer_derived_probe_group::emit_interval (translator_output* o)
-{
- o->line() << "({";
- o->newline(1) << "unsigned i = stp->intrv;";
- o->newline() << "if (stp->rnd != 0)";
- o->newline(1) << "i += _stp_random_pm(stp->rnd);";
- o->newline(-1) << "stp->ms ? msecs_to_jiffies(i) : i;";
- o->newline(-1) << "})";
-}
-
-
-void
-timer_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "/* ---- timer probes ---- */";
-
- s.op->newline() << "static struct stap_timer_probe {";
- s.op->newline(1) << "struct timer_list timer_list;";
- s.op->newline() << "const char *pp;";
- s.op->newline() << "void (*ph) (struct context*);";
- s.op->newline() << "unsigned intrv, ms, rnd;";
- s.op->newline(-1) << "} stap_timer_probes [" << probes.size() << "] = {";
- s.op->indent(1);
- for (unsigned i=0; i < probes.size(); i++)
- {
- s.op->newline () << "{";
- s.op->line() << " .pp="
- << lex_cast_qstring (*probes[i]->sole_location()) << ",";
- s.op->line() << " .ph=&" << probes[i]->name << ",";
- s.op->line() << " .intrv=" << probes[i]->interval << ",";
- s.op->line() << " .ms=" << probes[i]->time_is_msecs << ",";
- s.op->line() << " .rnd=" << probes[i]->randomize;
- s.op->line() << " },";
- }
- s.op->newline(-1) << "};";
- s.op->newline();
-
- s.op->newline() << "static void enter_timer_probe (unsigned long val) {";
- s.op->newline(1) << "struct stap_timer_probe* stp = & stap_timer_probes [val];";
- s.op->newline() << "if ((atomic_read (&session_state) == STAP_SESSION_STARTING) ||";
- s.op->newline() << " (atomic_read (&session_state) == STAP_SESSION_RUNNING))";
- s.op->newline(1) << "mod_timer (& stp->timer_list, jiffies + ";
- emit_interval (s.op);
- s.op->line() << ");";
- s.op->newline(-1) << "{";
- s.op->indent(1);
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "stp->pp");
- s.op->newline() << "(*stp->ph) (c);";
- common_probe_entryfn_epilogue (s.op);
- s.op->newline(-1) << "}";
- s.op->newline(-1) << "}";
-}
-
-
-void
-timer_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_timer_probe* stp = & stap_timer_probes [i];";
- s.op->newline() << "probe_point = stp->pp;";
- s.op->newline() << "init_timer (& stp->timer_list);";
- s.op->newline() << "stp->timer_list.function = & enter_timer_probe;";
- s.op->newline() << "stp->timer_list.data = i;"; // NB: important!
- // copy timer renew calculations from above :-(
- s.op->newline() << "stp->timer_list.expires = jiffies + ";
- emit_interval (s.op);
- s.op->line() << ";";
- s.op->newline() << "add_timer (& stp->timer_list);";
- // note: no partial failure rollback is needed: add_timer cannot fail.
- s.op->newline(-1) << "}"; // for loop
-}
-
-
-void
-timer_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)";
- s.op->newline(1) << "del_timer_sync (& stap_timer_probes[i].timer_list);";
- s.op->indent(-1);
-}
-
-
-
-// ------------------------------------------------------------------------
-// profile derived probes
-// ------------------------------------------------------------------------
-// On kernels < 2.6.10, this uses the register_profile_notifier API to
-// generate the timed events for profiling; on kernels >= 2.6.10 this
-// uses the register_timer_hook API. The latter doesn't currently allow
-// simultaneous users, so insertion will fail if the profiler is busy.
-// (Conflicting users may include OProfile, other SystemTap probes, etc.)
-
-
-struct profile_derived_probe: public derived_probe
-{
- profile_derived_probe (systemtap_session &s, probe* p, probe_point* l);
- void join_group (systemtap_session& s);
-};
-
-
-struct profile_derived_probe_group: public generic_dpg<profile_derived_probe>
-{
-public:
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-profile_derived_probe::profile_derived_probe (systemtap_session &, probe* p, probe_point* l):
- derived_probe(p, l)
-{
-}
-
-
-void
-profile_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.profile_derived_probes)
- s.profile_derived_probes = new profile_derived_probe_group ();
- s.profile_derived_probes->enroll (this);
-}
-
-
-struct profile_builder: public derived_probe_builder
-{
- profile_builder() {}
- virtual void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const &,
- vector<derived_probe *> & finished_results)
- {
- sess.unwindsym_modules.insert ("kernel");
- finished_results.push_back(new profile_derived_probe(sess, base, location));
- }
-};
-
-
-// timer.profile probe handlers are hooked up in an entertaining way
-// to the underlying kernel facility. The fact that 2.6.11+ era
-// "register_timer_hook" API allows only one consumer *system-wide*
-// will give a hint. We will have a single entry function (and thus
-// trivial registration / unregistration), and it will call all probe
-// handler functions in sequence.
-
-void
-profile_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- // kernels < 2.6.10: use register_profile_notifier API
- // kernels >= 2.6.10: use register_timer_hook API
- s.op->newline() << "/* ---- profile probes ---- */";
-
- // This function calls all the profiling probe handlers in sequence.
- // The only tricky thing is that the context will be reused amongst
- // them. While a simple sequence of calls to the individual probe
- // handlers is unlikely to go terribly wrong (with c->last_error
- // being set causing an early return), but for extra assurance, we
- // open-code the same logic here.
-
- s.op->newline() << "static void enter_all_profile_probes (struct pt_regs *regs) {";
- s.op->indent(1);
- string pp = lex_cast_qstring("timer.profile"); // hard-coded for convenience
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", pp);
- s.op->newline() << "c->regs = regs;";
-
- for (unsigned i=0; i<probes.size(); i++)
- {
- if (i > 0)
- {
- // Some lightweight inter-probe context resetting
- // XXX: not quite right: MAXERRORS not respected
- s.op->newline() << "c->actionremaining = MAXACTION;";
- }
- s.op->newline() << "if (c->last_error == NULL) " << probes[i]->name << " (c);";
- }
- common_probe_entryfn_epilogue (s.op);
- s.op->newline(-1) << "}";
-
- s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore
-
- s.op->newline() << "static int enter_profile_probes (struct notifier_block *self,"
- << " unsigned long val, void *data) {";
- s.op->newline(1) << "(void) self; (void) val;";
- s.op->newline() << "enter_all_profile_probes ((struct pt_regs *) data);";
- s.op->newline() << "return 0;";
- s.op->newline(-1) << "}";
- s.op->newline() << "struct notifier_block stap_profile_notifier = {"
- << " .notifier_call = & enter_profile_probes };";
-
- s.op->newline() << "#else";
-
- s.op->newline() << "static int enter_profile_probes (struct pt_regs *regs) {";
- s.op->newline(1) << "enter_all_profile_probes (regs);";
- s.op->newline() << "return 0;";
- s.op->newline(-1) << "}";
-
- s.op->newline() << "#endif";
-}
-
-
-void
-profile_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "probe_point = \"timer.profile\";"; // NB: hard-coded for convenience
- s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore
- s.op->newline() << "rc = register_profile_notifier (& stap_profile_notifier);";
- s.op->newline() << "#else";
- s.op->newline() << "rc = register_timer_hook (& enter_profile_probes);";
- s.op->newline() << "#endif";
-}
-
-
-void
-profile_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)";
- s.op->newline(1) << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore
- s.op->newline() << "unregister_profile_notifier (& stap_profile_notifier);";
- s.op->newline() << "#else";
- s.op->newline() << "unregister_timer_hook (& enter_profile_probes);";
- s.op->newline() << "#endif";
- s.op->indent(-1);
-}
-
-
-
-// ------------------------------------------------------------------------
-// procfs file derived probes
-// ------------------------------------------------------------------------
-
-
-static string TOK_PROCFS("procfs");
-static string TOK_READ("read");
-static string TOK_WRITE("write");
-
-struct procfs_derived_probe: public derived_probe
-{
- string path;
- bool write;
- bool target_symbol_seen;
-
- procfs_derived_probe (systemtap_session &, probe* p, probe_point* l, string ps, bool w);
- void join_group (systemtap_session& s);
-};
-
-
-struct procfs_probe_set
-{
- procfs_derived_probe* read_probe;
- procfs_derived_probe* write_probe;
-
- procfs_probe_set () : read_probe (NULL), write_probe (NULL) {}
-};
-
-
-struct procfs_derived_probe_group: public generic_dpg<procfs_derived_probe>
-{
-private:
- map<string, procfs_probe_set*> probes_by_path;
- typedef map<string, procfs_probe_set*>::iterator p_b_p_iterator;
- bool has_read_probes;
- bool has_write_probes;
-
-public:
- procfs_derived_probe_group () :
- has_read_probes(false), has_write_probes(false) {}
-
- void enroll (procfs_derived_probe* probe);
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-struct procfs_var_expanding_visitor: public var_expanding_visitor
-{
- procfs_var_expanding_visitor(systemtap_session& s, const string& pn,
- string path, bool write_probe):
- sess (s), probe_name (pn), path (path), write_probe (write_probe),
- target_symbol_seen (false) {}
-
- systemtap_session& sess;
- string probe_name;
- string path;
- bool write_probe;
- bool target_symbol_seen;
-
- void visit_target_symbol (target_symbol* e);
-};
-
-
-procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p,
- probe_point* l, string ps, bool w):
- derived_probe(p, l), path(ps), write(w), target_symbol_seen(false)
-{
- // Expand local variables in the probe body
- procfs_var_expanding_visitor v (s, name, path, write);
- this->body = v.require (this->body);
- target_symbol_seen = v.target_symbol_seen;
-}
-
-
-void
-procfs_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.procfs_derived_probes)
- s.procfs_derived_probes = new procfs_derived_probe_group ();
- s.procfs_derived_probes->enroll (this);
-}
-
-
-void
-procfs_derived_probe_group::enroll (procfs_derived_probe* p)
-{
- procfs_probe_set *pset;
-
- if (probes_by_path.count(p->path) == 0)
- {
- pset = new procfs_probe_set;
- probes_by_path[p->path] = pset;
- }
- else
- {
- pset = probes_by_path[p->path];
-
- // You can only specify 1 read and 1 write probe.
- if (p->write && pset->write_probe != NULL)
- throw semantic_error("only one write procfs probe can exist for procfs path \"" + p->path + "\"");
- else if (! p->write && pset->read_probe != NULL)
- throw semantic_error("only one read procfs probe can exist for procfs path \"" + p->path + "\"");
-
- // XXX: multiple writes should be acceptable
- }
-
- if (p->write)
- {
- pset->write_probe = p;
- has_write_probes = true;
- }
- else
- {
- pset->read_probe = p;
- has_read_probes = true;
- }
-}
-
-
-void
-procfs_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes_by_path.empty())
- return;
-
- s.op->newline() << "/* ---- procfs probes ---- */";
- s.op->newline() << "#include \"procfs.c\"";
-
- // Emit the procfs probe data list
- s.op->newline() << "static struct stap_procfs_probe {";
- s.op->newline(1)<< "const char *path;";
- s.op->newline() << "const char *read_pp;";
- s.op->newline() << "void (*read_ph) (struct context*);";
- s.op->newline() << "const char *write_pp;";
- s.op->newline() << "void (*write_ph) (struct context*);";
- s.op->newline(-1) << "} stap_procfs_probes[] = {";
- s.op->indent(1);
-
- for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end();
- it++)
- {
- procfs_probe_set *pset = it->second;
-
- s.op->newline() << "{";
- s.op->line() << " .path=" << lex_cast_qstring (it->first) << ",";
-
- if (pset->read_probe != NULL)
- {
- s.op->line() << " .read_pp="
- << lex_cast_qstring (*pset->read_probe->sole_location())
- << ",";
- s.op->line() << " .read_ph=&" << pset->read_probe->name << ",";
- }
- else
- {
- s.op->line() << " .read_pp=NULL,";
- s.op->line() << " .read_ph=NULL,";
- }
-
- if (pset->write_probe != NULL)
- {
- s.op->line() << " .write_pp="
- << lex_cast_qstring (*pset->write_probe->sole_location())
- << ",";
- s.op->line() << " .write_ph=&" << pset->write_probe->name;
- }
- else
- {
- s.op->line() << " .write_pp=NULL,";
- s.op->line() << " .write_ph=NULL";
- }
- s.op->line() << " },";
- }
- s.op->newline(-1) << "};";
-
- if (has_read_probes)
- {
- // Output routine to fill in 'page' with our data.
- s.op->newline();
-
- s.op->newline() << "static int _stp_procfs_read(char *page, char **start, off_t off, int count, int *eof, void *data) {";
-
- s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
- s.op->newline() << "int bytes = 0;";
- s.op->newline() << "string_t strdata = {'\\0'};";
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->read_pp");
-
- s.op->newline() << "if (c->data == NULL)";
- s.op->newline(1) << "c->data = &strdata;";
- s.op->newline(-1) << "else {";
-
- s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
- s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
- s.op->newline() << "_stp_exit ();";
- s.op->newline(-1) << "}";
- s.op->newline() << "atomic_dec (& c->busy);";
- s.op->newline() << "goto probe_epilogue;";
- s.op->newline(-1) << "}";
-
- // call probe function (which copies data into strdata)
- s.op->newline() << "(*spp->read_ph) (c);";
-
- // copy string data into 'page'
- s.op->newline() << "c->data = NULL;";
- s.op->newline() << "bytes = strnlen(strdata, MAXSTRINGLEN - 1);";
- s.op->newline() << "if (off >= bytes)";
- s.op->newline(1) << "*eof = 1;";
- s.op->newline(-1) << "else {";
- s.op->newline(1) << "bytes -= off;";
- s.op->newline() << "if (bytes > count)";
- s.op->newline(1) << "bytes = count;";
- s.op->newline(-1) << "memcpy(page, strdata + off, bytes);";
- s.op->newline() << "*start = page;";
- s.op->newline(-1) << "}";
-
- common_probe_entryfn_epilogue (s.op);
- s.op->newline() << "return bytes;";
-
- s.op->newline(-1) << "}";
- }
- if (has_write_probes)
- {
- s.op->newline() << "static int _stp_procfs_write(struct file *file, const char *buffer, unsigned long count, void *data) {";
-
- s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
- s.op->newline() << "string_t strdata = {'\\0'};";
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->write_pp");
-
- s.op->newline() << "if (count > (MAXSTRINGLEN - 1))";
- s.op->newline(1) << "count = MAXSTRINGLEN - 1;";
- s.op->newline(-1) << "_stp_copy_from_user(strdata, buffer, count);";
-
- s.op->newline() << "if (c->data == NULL)";
- s.op->newline(1) << "c->data = &strdata;";
- s.op->newline(-1) << "else {";
-
- s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
- s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
- s.op->newline() << "_stp_exit ();";
- s.op->newline(-1) << "}";
- s.op->newline() << "atomic_dec (& c->busy);";
- s.op->newline() << "goto probe_epilogue;";
- s.op->newline(-1) << "}";
-
- // call probe function (which copies data out of strdata)
- s.op->newline() << "(*spp->write_ph) (c);";
-
- s.op->newline() << "c->data = NULL;";
- common_probe_entryfn_epilogue (s.op);
-
- s.op->newline() << "return count;";
- s.op->newline(-1) << "}";
- }
-}
-
-
-void
-procfs_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes_by_path.empty())
- return;
-
- s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {";
- s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];";
-
- s.op->newline() << "if (spp->read_pp)";
- s.op->newline(1) << "probe_point = spp->read_pp;";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "probe_point = spp->write_pp;";
-
- s.op->newline(-1) << "rc = _stp_create_procfs(spp->path, i);";
-
- s.op->newline() << "if (rc) {";
- s.op->newline(1) << "_stp_close_procfs();";
- s.op->newline() << "break;";
- s.op->newline(-1) << "}";
-
- if (has_read_probes)
- {
- s.op->newline() << "if (spp->read_pp)";
- s.op->newline(1) << "_stp_procfs_files[i]->read_proc = &_stp_procfs_read;";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "_stp_procfs_files[i]->read_proc = NULL;";
- s.op->indent(-1);
- }
- else
- s.op->newline() << "_stp_procfs_files[i]->read_proc = NULL;";
-
- if (has_write_probes)
- {
- s.op->newline() << "if (spp->write_pp)";
- s.op->newline(1) << "_stp_procfs_files[i]->write_proc = &_stp_procfs_write;";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "_stp_procfs_files[i]->write_proc = NULL;";
- s.op->indent(-1);
- }
- else
- s.op->newline() << "_stp_procfs_files[i]->write_proc = NULL;";
-
- s.op->newline() << "_stp_procfs_files[i]->data = spp;";
- s.op->newline(-1) << "}"; // for loop
-}
-
-
-void
-procfs_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes_by_path.empty())
- return;
-
- s.op->newline() << "_stp_close_procfs();";
-}
-
-
-void
-procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e)
-{
- assert(e->base_name.size() > 0 && e->base_name[0] == '$');
-
- if (e->base_name != "$value")
- throw semantic_error ("invalid target symbol for procfs probe, $value expected",
- e->tok);
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("procfs target variable '$value' may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("procfs target variable '$value' may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid use of procfs target variable '$value'",
- e->tok);
- break;
- }
- }
-
- bool lvalue = is_active_lvalue(e);
- if (write_probe && lvalue)
- throw semantic_error("procfs $value variable is read-only in a procfs write probe", e->tok);
- else if (! write_probe && ! lvalue)
- throw semantic_error("procfs $value variable cannot be read in a procfs read probe", e->tok);
-
- // Remember that we've seen a target variable.
- target_symbol_seen = true;
-
- // Synthesize a function.
- functiondecl *fdecl = new functiondecl;
- fdecl->tok = e->tok;
- embeddedcode *ec = new embeddedcode;
- ec->tok = e->tok;
-
- string fname = (string(lvalue ? "_procfs_value_set" : "_procfs_value_get")
- + "_" + lex_cast<string>(tick++));
- string locvalue = "CONTEXT->data";
-
- if (! lvalue)
- ec->code = string("strlcpy (THIS->__retvalue, ") + locvalue
- + string(", MAXSTRINGLEN); /* pure */");
- else
- ec->code = string("strlcpy (") + locvalue
- + string(", THIS->value, MAXSTRINGLEN);");
-
- fdecl->name = fname;
- fdecl->body = ec;
- fdecl->type = pe_string;
-
- if (lvalue)
- {
- // Modify the fdecl so it carries a single pe_string formal
- // argument called "value".
-
- vardecl *v = new vardecl;
- v->type = pe_string;
- v->name = "value";
- v->tok = e->tok;
- fdecl->formal_args.push_back(v);
- }
- sess.functions[fdecl->name]=fdecl;
-
- // Synthesize a functioncall.
- functioncall* n = new functioncall;
- n->tok = e->tok;
- n->function = fname;
- n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
-
- if (lvalue)
- {
- // Provide the functioncall to our parent, so that it can be
- // used to substitute for the assignment node immediately above
- // us.
- assert(!target_symbol_setter_functioncalls.empty());
- *(target_symbol_setter_functioncalls.top()) = n;
- }
-
- provide (n);
-}
-
-
-struct procfs_builder: public derived_probe_builder
-{
- procfs_builder() {}
- virtual void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results);
-};
-
-
-void
-procfs_builder::build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
-{
- string path;
- bool has_procfs = get_param(parameters, TOK_PROCFS, path);
- bool has_read = (parameters.find(TOK_READ) != parameters.end());
- bool has_write = (parameters.find(TOK_WRITE) != parameters.end());
-
- // If no procfs path, default to "command". The runtime will do
- // this for us, but if we don't do it here, we'll think the
- // following 2 probes are attached to different paths:
- //
- // probe procfs("command").read {}"
- // probe procfs.write {}
-
- if (! has_procfs)
- path = "command";
- // If we have a path, we need to validate it.
- else
- {
- string::size_type start_pos, end_pos;
- string component;
- start_pos = 0;
- while ((end_pos = path.find('/', start_pos)) != string::npos)
- {
- // Make sure it doesn't start with '/'.
- if (end_pos == 0)
- throw semantic_error ("procfs path cannot start with a '/'",
- location->tok);
-
- component = path.substr(start_pos, end_pos - start_pos);
- // Make sure it isn't empty.
- if (component.size() == 0)
- throw semantic_error ("procfs path component cannot be empty",
- location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
-
- start_pos = end_pos + 1;
- }
- component = path.substr(start_pos);
- // Make sure it doesn't end with '/'.
- if (component.size() == 0)
- throw semantic_error ("procfs path cannot end with a '/'", location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
- }
-
- if (!(has_read ^ has_write))
- throw semantic_error ("need read/write component", location->tok);
-
- finished_results.push_back(new procfs_derived_probe(sess, base, location,
- path, has_write));
-}
-
-
-
-// ------------------------------------------------------------------------
-// statically inserted macro-based derived probes
-// ------------------------------------------------------------------------
-
-static string TOK_FORMAT("format");
-
-struct mark_arg
-{
- bool str;
- string c_type;
- exp_type stp_type;
-};
-
-struct mark_derived_probe: public derived_probe
-{
- mark_derived_probe (systemtap_session &s,
- const string& probe_name, const string& probe_format,
- probe* base_probe, probe_point* location);
-
- systemtap_session& sess;
- string probe_name, probe_format;
- vector <struct mark_arg *> mark_args;
- bool target_symbol_seen;
-
- void join_group (systemtap_session& s);
- void print_dupe_stamp (ostream& o);
- void emit_probe_context_vars (translator_output* o);
- void initialize_probe_context_vars (translator_output* o);
- void printargs (std::ostream &o) const;
-
- void parse_probe_format ();
-};
-
-
-struct mark_derived_probe_group: public generic_dpg<mark_derived_probe>
-{
-public:
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-struct mark_var_expanding_visitor: public var_expanding_visitor
-{
- mark_var_expanding_visitor(systemtap_session& s, const string& pn,
- vector <struct mark_arg *> &mark_args):
- sess (s), probe_name (pn), mark_args (mark_args),
- target_symbol_seen (false) {}
- systemtap_session& sess;
- string probe_name;
- vector <struct mark_arg *> &mark_args;
- bool target_symbol_seen;
-
- void visit_target_symbol (target_symbol* e);
- void visit_target_symbol_arg (target_symbol* e);
- void visit_target_symbol_context (target_symbol* e);
-};
-
-
-void
-hex_dump(unsigned char *data, size_t len)
-{
- // Dump data
- size_t idx = 0;
- while (idx < len)
- {
- string char_rep;
-
- clog << " 0x" << setfill('0') << setw(8) << hex << internal << idx;
-
- for (int i = 0; i < 4; i++)
- {
- clog << " ";
- size_t limit = idx + 4;
- while (idx < len && idx < limit)
- {
- clog << setfill('0') << setw(2)
- << ((unsigned) *((unsigned char *)data + idx));
- if (isprint(*((char *)data + idx)))
- char_rep += *((char *)data + idx);
- else
- char_rep += '.';
- idx++;
- }
- while (idx < limit)
- {
- clog << " ";
- idx++;
- }
- }
- clog << " " << char_rep << dec << setfill(' ') << endl;
- }
-}
-
-
-void
-mark_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
-{
- string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
- int argnum = atoi (argnum_s.c_str());
-
- if (argnum < 1 || argnum > (int)mark_args.size())
- throw semantic_error ("invalid marker argument number", e->tok);
-
- if (is_active_lvalue (e))
- throw semantic_error("write to marker parameter not permitted", e->tok);
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("marker argument may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("marker argument may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid marker argument use", e->tok);
- break;
- }
- }
-
- // Remember that we've seen a target variable.
- target_symbol_seen = true;
-
- e->probe_context_var = "__mark_arg" + lex_cast<string>(argnum);
- e->type = mark_args[argnum-1]->stp_type;
- provide (e);
-}
-
-
-void
-mark_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
-{
- string sname = e->base_name;
-
- if (is_active_lvalue (e))
- throw semantic_error("write to marker '" + sname + "' not permitted", e->tok);
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("marker '" + sname + "' may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("marker '" + sname + "' may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid marker '" + sname + "' use", e->tok);
- break;
- }
- }
-
- string fname;
- if (e->base_name == "$format") {
- fname = string("_mark_format_get");
- } else {
- fname = string("_mark_name_get");
- }
-
- // Synthesize a functioncall.
- functioncall* n = new functioncall;
- n->tok = e->tok;
- n->function = fname;
- n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
- provide (n);
-}
-
-void
-mark_var_expanding_visitor::visit_target_symbol (target_symbol* e)
-{
- assert(e->base_name.size() > 0 && e->base_name[0] == '$');
-
- if (e->base_name.substr(0,4) == "$arg")
- visit_target_symbol_arg (e);
- else if (e->base_name == "$format" || e->base_name == "$name")
- visit_target_symbol_context (e);
- else
- throw semantic_error ("invalid target symbol for marker, $argN, $name or $format expected",
- e->tok);
-}
-
-
-
-mark_derived_probe::mark_derived_probe (systemtap_session &s,
- const string& p_n,
- const string& p_f,
- probe* base, probe_point* loc):
- derived_probe (base, new probe_point(*loc) /* .components soon rewritten */),
- sess (s), probe_name (p_n), probe_format (p_f),
- target_symbol_seen (false)
-{
- // create synthetic probe point name; preserve condition
- vector<probe_point::component*> comps;
- comps.push_back (new probe_point::component (TOK_KERNEL));
- comps.push_back (new probe_point::component (TOK_MARK, new literal_string (probe_name)));
- comps.push_back (new probe_point::component (TOK_FORMAT, new literal_string (probe_format)));
- this->sole_location()->components = comps;
-
- // expand the marker format
- parse_probe_format();
-
- // Now expand the local variables in the probe body
- mark_var_expanding_visitor v (sess, name, mark_args);
- this->body = v.require (this->body);
- target_symbol_seen = v.target_symbol_seen;
-
- if (sess.verbose > 2)
- clog << "marker-based " << name << " mark=" << probe_name
- << " fmt='" << probe_format << "'" << endl;
-}
-
-
-static int
-skip_atoi(const char **s)
-{
- int i = 0;
- while (isdigit(**s))
- i = i * 10 + *((*s)++) - '0';
- return i;
-}
-
-
-void
-mark_derived_probe::parse_probe_format()
-{
- const char *fmt = probe_format.c_str();
- int qualifier; // 'h', 'l', or 'L' for integer fields
- mark_arg *arg;
-
- for (; *fmt ; ++fmt)
- {
- if (*fmt != '%')
- {
- /* Skip text */
- continue;
- }
-
-repeat:
- ++fmt;
-
- // skip conversion flags (if present)
- switch (*fmt)
- {
- case '-':
- case '+':
- case ' ':
- case '#':
- case '0':
- goto repeat;
- }
-
- // skip minimum field witdh (if present)
- if (isdigit(*fmt))
- skip_atoi(&fmt);
-
- // skip precision (if present)
- if (*fmt == '.')
- {
- ++fmt;
- if (isdigit(*fmt))
- skip_atoi(&fmt);
- }
-
- // get the conversion qualifier (if present)
- qualifier = -1;
- if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
- {
- qualifier = *fmt;
- ++fmt;
- if (qualifier == 'l' && *fmt == 'l')
- {
- qualifier = 'L';
- ++fmt;
- }
- }
-
- // get the conversion type
- switch (*fmt)
- {
- case 'c':
- arg = new mark_arg;
- arg->str = false;
- arg->c_type = "int";
- arg->stp_type = pe_long;
- mark_args.push_back(arg);
- continue;
-
- case 's':
- arg = new mark_arg;
- arg->str = true;
- arg->c_type = "char *";
- arg->stp_type = pe_string;
- mark_args.push_back(arg);
- continue;
-
- case 'p':
- arg = new mark_arg;
- arg->str = false;
- // This should really be 'void *'. But, then we'll get a
- // compile error when we assign the void pointer to an
- // integer without a cast. So, we use 'long' instead, since
- // it should have the same size as 'void *'.
- arg->c_type = "long";
- arg->stp_type = pe_long;
- mark_args.push_back(arg);
- continue;
-
- case '%':
- continue;
-
- case 'o':
- case 'X':
- case 'x':
- case 'd':
- case 'i':
- case 'u':
- // fall through...
- break;
-
- default:
- if (!*fmt)
- --fmt;
- continue;
- }
-
- arg = new mark_arg;
- arg->str = false;
- arg->stp_type = pe_long;
- switch (qualifier)
- {
- case 'L':
- arg->c_type = "long long";
- break;
-
- case 'l':
- arg->c_type = "long";
- break;
-
- case 'h':
- arg->c_type = "short";
- break;
-
- default:
- arg->c_type = "int";
- break;
- }
- mark_args.push_back(arg);
- }
-}
-
-
-void
-mark_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.mark_derived_probes)
- {
- s.mark_derived_probes = new mark_derived_probe_group ();
-
- // Make sure <linux/marker.h> is included early.
- embeddedcode *ec = new embeddedcode;
- ec->tok = NULL;
- ec->code = string("#if ! defined(CONFIG_MARKERS)\n")
- + string("#error \"Need CONFIG_MARKERS!\"\n")
- + string("#endif\n")
- + string("#include <linux/marker.h>\n");
-
- s.embeds.push_back(ec);
- }
- s.mark_derived_probes->enroll (this);
-}
-
-
-void
-mark_derived_probe::print_dupe_stamp (ostream& o)
-{
- if (target_symbol_seen)
- for (unsigned i = 0; i < mark_args.size(); i++)
- o << mark_args[i]->c_type << " __mark_arg" << (i+1) << endl;
-}
-
-
-void
-mark_derived_probe::emit_probe_context_vars (translator_output* o)
-{
- // If we haven't seen a target symbol for this probe, quit.
- if (! target_symbol_seen)
- return;
-
- for (unsigned i = 0; i < mark_args.size(); i++)
- {
- string localname = "__mark_arg" + lex_cast<string>(i+1);
- switch (mark_args[i]->stp_type)
- {
- case pe_long:
- o->newline() << "int64_t " << localname << ";";
- break;
- case pe_string:
- o->newline() << "string_t " << localname << ";";
- break;
- default:
- throw semantic_error ("cannot expand unknown type");
- break;
- }
- }
-}
-
-
-void
-mark_derived_probe::initialize_probe_context_vars (translator_output* o)
-{
- // If we haven't seen a target symbol for this probe, quit.
- if (! target_symbol_seen)
- return;
-
- bool deref_fault_needed = false;
- for (unsigned i = 0; i < mark_args.size(); i++)
- {
- string localname = "l->__mark_arg" + lex_cast<string>(i+1);
- switch (mark_args[i]->stp_type)
- {
- case pe_long:
- o->newline() << localname << " = va_arg(*c->mark_va_list, "
- << mark_args[i]->c_type << ");";
- break;
-
- case pe_string:
- // We're assuming that this is a kernel string (this code is
- // basically the guts of kernel_string), not a user string.
- o->newline() << "{ " << mark_args[i]->c_type
- << " tmp_str = va_arg(*c->mark_va_list, "
- << mark_args[i]->c_type << ");";
- o->newline() << "deref_string (" << localname
- << ", tmp_str, MAXSTRINGLEN); }";
- deref_fault_needed = true;
- break;
-
- default:
- throw semantic_error ("cannot expand unknown type");
- break;
- }
- }
- if (deref_fault_needed)
- // Need to report errors?
- o->newline() << "deref_fault: ;";
-}
-
-void
-mark_derived_probe::printargs(std::ostream &o) const
-{
- for (unsigned i = 0; i < mark_args.size(); i++)
- {
- string localname = "$arg" + lex_cast<string>(i+1);
- switch (mark_args[i]->stp_type)
- {
- case pe_long:
- o << " " << localname << ":long";
- break;
- case pe_string:
- o << " " << localname << ":string";
- break;
- default:
- o << " " << localname << ":unknown";
- break;
- }
- }
-}
-
-
-void
-mark_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes.empty())
- return;
-
- s.op->newline() << "/* ---- marker probes ---- */";
-
- s.op->newline() << "static struct stap_marker_probe {";
- s.op->newline(1) << "const char * const name;";
- s.op->newline() << "const char * const format;";
- s.op->newline() << "const char * const pp;";
- s.op->newline() << "void (* const ph) (struct context *);";
-
- s.op->newline(-1) << "} stap_marker_probes [" << probes.size() << "] = {";
- s.op->indent(1);
- for (unsigned i=0; i < probes.size(); i++)
- {
- s.op->newline () << "{";
- s.op->line() << " .name=" << lex_cast_qstring(probes[i]->probe_name)
- << ",";
- s.op->line() << " .format=" << lex_cast_qstring(probes[i]->probe_format)
- << ",";
- s.op->line() << " .pp=" << lex_cast_qstring (*probes[i]->sole_location())
- << ",";
- s.op->line() << " .ph=&" << probes[i]->name;
- s.op->line() << " },";
- }
- s.op->newline(-1) << "};";
- s.op->newline();
-
-
- // Emit the marker callback function
- s.op->newline();
- s.op->newline() << "static void enter_marker_probe (void *probe_data, void *call_data, const char *fmt, va_list *args) {";
- s.op->newline(1) << "struct stap_marker_probe *smp = (struct stap_marker_probe *)probe_data;";
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "smp->pp");
- s.op->newline() << "c->marker_name = smp->name;";
- s.op->newline() << "c->marker_format = smp->format;";
- s.op->newline() << "c->mark_va_list = args;";
- s.op->newline() << "(*smp->ph) (c);";
- s.op->newline() << "c->mark_va_list = NULL;";
- s.op->newline() << "c->data = NULL;";
-
- common_probe_entryfn_epilogue (s.op);
- s.op->newline(-1) << "}";
-
- return;
-}
-
-
-void
-mark_derived_probe_group::emit_module_init (systemtap_session &s)
-{
- if (probes.size () == 0)
- return;
-
- s.op->newline() << "/* init marker probes */";
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];";
- s.op->newline() << "probe_point = smp->pp;";
- s.op->newline() << "rc = marker_probe_register(smp->name, smp->format, enter_marker_probe, smp);";
- s.op->newline() << "if (rc) {";
- s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; // partial rollback
- s.op->newline(1) << "struct stap_marker_probe *smp2 = &stap_marker_probes[j];";
- s.op->newline() << "marker_probe_unregister(smp2->name, enter_marker_probe, smp2);";
- s.op->newline(-1) << "}";
- s.op->newline() << "break;"; // don't attempt to register any more probes
- s.op->newline(-1) << "}";
- s.op->newline(-1) << "}"; // for loop
-}
-
-
-void
-mark_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes.empty())
- return;
-
- s.op->newline() << "/* deregister marker probes */";
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];";
- s.op->newline() << "marker_probe_unregister(smp->name, enter_marker_probe, smp);";
- s.op->newline(-1) << "}"; // for loop
-}
-
-
-struct mark_builder: public derived_probe_builder
-{
-private:
- bool cache_initialized;
- typedef multimap<string, string> mark_cache_t;
- typedef multimap<string, string>::const_iterator mark_cache_const_iterator_t;
- typedef pair<mark_cache_const_iterator_t, mark_cache_const_iterator_t>
- mark_cache_const_iterator_pair_t;
- mark_cache_t mark_cache;
-
-public:
- mark_builder(): cache_initialized(false) {}
-
- void build_no_more (systemtap_session &s)
- {
- if (! mark_cache.empty())
- {
- if (s.verbose > 3)
- clog << "mark_builder releasing cache" << endl;
- mark_cache.clear();
- }
- }
-
- void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results);
-};
-
-
-void
-mark_builder::build(systemtap_session & sess,
- probe * base,
- probe_point *loc,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
-{
- string mark_str_val;
- bool has_mark_str = get_param (parameters, TOK_MARK, mark_str_val);
- string mark_format_val;
- bool has_mark_format = get_param (parameters, TOK_FORMAT, mark_format_val);
- assert (has_mark_str);
- (void) has_mark_str;
-
- if (! cache_initialized)
- {
- cache_initialized = true;
- string module_markers_path = sess.kernel_build_tree + "/Module.markers";
-
- ifstream module_markers;
- module_markers.open(module_markers_path.c_str(), ifstream::in);
- if (! module_markers)
- {
- if (sess.verbose>3)
- clog << module_markers_path << " cannot be opened: "
- << strerror(errno) << endl;
- return;
- }
-
- string name, module, format;
- do
- {
- module_markers >> name >> module;
- getline(module_markers, format);
-
- // trim leading whitespace
- string::size_type notwhite = format.find_first_not_of(" \t");
- format.erase(0, notwhite);
-
- // If the format is empty, make sure we add back a space
- // character, which is what MARK_NOARGS expands to.
- if (format.length() == 0)
- format = " ";
-
- if (sess.verbose>3)
- clog << "'" << name << "' '" << module << "' '" << format
- << "'" << endl;
-
- if (mark_cache.count(name) > 0)
- {
- // If we have 2 markers with the same we've got 2 cases:
- // different format strings or duplicate format strings.
- // If an existing marker in the cache doesn't have the
- // same format string, add this marker.
- mark_cache_const_iterator_pair_t ret;
- mark_cache_const_iterator_t it;
- bool matching_format_string = false;
-
- ret = mark_cache.equal_range(name);
- for (it = ret.first; it != ret.second; ++it)
- {
- if (format == it->second)
- {
- matching_format_string = true;
- break;
- }
- }
-
- if (! matching_format_string)
- mark_cache.insert(pair<string,string>(name, format));
- }
- else
- mark_cache.insert(pair<string,string>(name, format));
- }
- while (! module_markers.eof());
- module_markers.close();
- }
-
- // Search marker list for matching markers
- for (mark_cache_const_iterator_t it = mark_cache.begin();
- it != mark_cache.end(); it++)
- {
- // Below, "rc" has negative polarity: zero iff matching.
- int rc = fnmatch(mark_str_val.c_str(), it->first.c_str(), 0);
- if (! rc)
- {
- bool add_result = true;
-
- // Match format strings (if the user specified one)
- if (has_mark_format && fnmatch(mark_format_val.c_str(),
- it->second.c_str(), 0))
- add_result = false;
-
- if (add_result)
- {
- derived_probe *dp
- = new mark_derived_probe (sess,
- it->first, it->second,
- base, loc);
- finished_results.push_back (dp);
- }
- }
- }
-}
-
-
// ------------------------------------------------------------------------
// statically inserted kernel-tracepoint derived probes
@@ -11049,757 +7949,20 @@ tracepoint_builder::build(systemtap_session& s,
// ------------------------------------------------------------------------
-// hrtimer derived probes
-// ------------------------------------------------------------------------
-// This is a new timer interface that provides more flexibility in specifying
-// intervals, and uses the hrtimer APIs when available for greater precision.
-// While hrtimers were added in 2.6.16, the API's weren't exported until
-// 2.6.17, so we must check this kernel version before attempting to use
-// hrtimers.
-//
-// * hrtimer_derived_probe: creates a probe point based on the hrtimer APIs.
-
-
-struct hrtimer_derived_probe: public derived_probe
-{
- // set a (generous) maximum of one day in ns
- static const int64_t max_ns_interval = 1000000000LL * 60LL * 60LL * 24LL;
-
- // 100us seems like a reasonable minimum
- static const int64_t min_ns_interval = 100000LL;
-
- int64_t interval, randomize;
-
- hrtimer_derived_probe (probe* p, probe_point* l, int64_t i, int64_t r,
- int64_t scale):
- derived_probe (p, l), interval (i), randomize (r)
- {
- if ((i < min_ns_interval) || (i > max_ns_interval))
- throw semantic_error(string("interval value out of range (")
- + lex_cast<string>(scale < min_ns_interval
- ? min_ns_interval/scale : 1)
- + ","
- + lex_cast<string>(max_ns_interval/scale) + ")");
-
- // randomize = 0 means no randomization
- if ((r < 0) || (r > i))
- throw semantic_error("randomization value out of range");
- }
-
- void join_group (systemtap_session& s);
-};
-
-
-struct hrtimer_derived_probe_group: public generic_dpg<hrtimer_derived_probe>
-{
- void emit_interval (translator_output* o);
-public:
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-void
-hrtimer_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.hrtimer_derived_probes)
- s.hrtimer_derived_probes = new hrtimer_derived_probe_group ();
- s.hrtimer_derived_probes->enroll (this);
-}
-
-
-void
-hrtimer_derived_probe_group::emit_interval (translator_output* o)
-{
- o->line() << "({";
- o->newline(1) << "unsigned long nsecs;";
- o->newline() << "int64_t i = stp->intrv;";
- o->newline() << "if (stp->rnd != 0) {";
- // XXX: why not use stp_random_pm instead of this?
- o->newline(1) << "int64_t r;";
- o->newline() << "get_random_bytes(&r, sizeof(r));";
- // ensure that r is positive
- o->newline() << "r &= ((uint64_t)1 << (8*sizeof(r) - 1)) - 1;";
- o->newline() << "r = _stp_mod64(NULL, r, (2*stp->rnd+1));";
- o->newline() << "r -= stp->rnd;";
- o->newline() << "i += r;";
- o->newline(-1) << "}";
- o->newline() << "if (unlikely(i < stap_hrtimer_resolution))";
- o->newline(1) << "i = stap_hrtimer_resolution;";
- o->indent(-1);
- o->newline() << "nsecs = do_div(i, NSEC_PER_SEC);";
- o->newline() << "ktime_set(i, nsecs);";
- o->newline(-1) << "})";
-}
-
-
-void
-hrtimer_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "/* ---- hrtimer probes ---- */";
-
- s.op->newline() << "static unsigned long stap_hrtimer_resolution;"; // init later
- s.op->newline() << "static struct stap_hrtimer_probe {";
- s.op->newline(1) << "struct hrtimer hrtimer;";
- s.op->newline() << "const char *pp;";
- s.op->newline() << "void (*ph) (struct context*);";
- s.op->newline() << "int64_t intrv, rnd;";
- s.op->newline(-1) << "} stap_hrtimer_probes [" << probes.size() << "] = {";
- s.op->indent(1);
- for (unsigned i=0; i < probes.size(); i++)
- {
- s.op->newline () << "{";
- s.op->line() << " .pp=" << lex_cast_qstring (*probes[i]->sole_location()) << ",";
- s.op->line() << " .ph=&" << probes[i]->name << ",";
- s.op->line() << " .intrv=" << probes[i]->interval << "LL,";
- s.op->line() << " .rnd=" << probes[i]->randomize << "LL";
- s.op->line() << " },";
- }
- s.op->newline(-1) << "};";
- s.op->newline();
-
- // autoconf: add get/set expires if missing (pre 2.6.28-rc1)
- s.op->newline() << "#ifndef STAPCONF_HRTIMER_GETSET_EXPIRES";
- s.op->newline() << "#define hrtimer_get_expires(timer) ((timer)->expires)";
- s.op->newline() << "#define hrtimer_set_expires(timer, time) (void)((timer)->expires = (time))";
- s.op->newline() << "#endif";
-
- // autoconf: adapt to HRTIMER_REL -> HRTIMER_MODE_REL renaming near 2.6.21
- s.op->newline() << "#ifdef STAPCONF_HRTIMER_REL";
- s.op->newline() << "#define HRTIMER_MODE_REL HRTIMER_REL";
- s.op->newline() << "#endif";
-
- // The function signature changed in 2.6.21.
- s.op->newline() << "#ifdef STAPCONF_HRTIMER_REL";
- s.op->newline() << "static int ";
- s.op->newline() << "#else";
- s.op->newline() << "static enum hrtimer_restart ";
- s.op->newline() << "#endif";
- s.op->newline() << "enter_hrtimer_probe (struct hrtimer *timer) {";
-
- s.op->newline(1) << "int rc = HRTIMER_NORESTART;";
- s.op->newline() << "struct stap_hrtimer_probe *stp = container_of(timer, struct stap_hrtimer_probe, hrtimer);";
- s.op->newline() << "if ((atomic_read (&session_state) == STAP_SESSION_STARTING) ||";
- s.op->newline() << " (atomic_read (&session_state) == STAP_SESSION_RUNNING)) {";
- // Compute next trigger time
- s.op->newline(1) << "hrtimer_set_expires(timer, ktime_add (hrtimer_get_expires(timer),";
- emit_interval (s.op);
- s.op->line() << "));";
- s.op->newline() << "rc = HRTIMER_RESTART;";
- s.op->newline(-1) << "}";
- s.op->newline() << "{";
- s.op->indent(1);
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "stp->pp");
- s.op->newline() << "(*stp->ph) (c);";
- common_probe_entryfn_epilogue (s.op);
- s.op->newline(-1) << "}";
- s.op->newline() << "return rc;";
- s.op->newline(-1) << "}";
-}
-
-
-void
-hrtimer_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "{";
- s.op->newline(1) << "struct timespec res;";
- s.op->newline() << "hrtimer_get_res (CLOCK_MONOTONIC, &res);";
- s.op->newline() << "stap_hrtimer_resolution = timespec_to_ns (&res);";
- s.op->newline(-1) << "}";
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_hrtimer_probe* stp = & stap_hrtimer_probes [i];";
- s.op->newline() << "probe_point = stp->pp;";
- s.op->newline() << "hrtimer_init (& stp->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);";
- s.op->newline() << "stp->hrtimer.function = & enter_hrtimer_probe;";
- // There is no hrtimer field to identify *this* (i-th) probe handler
- // callback. So instead we'll deduce it at entry time.
- s.op->newline() << "(void) hrtimer_start (& stp->hrtimer, ";
- emit_interval (s.op);
- s.op->line() << ", HRTIMER_MODE_REL);";
- // Note: no partial failure rollback is needed: hrtimer_start only
- // "fails" if the timer was already active, which cannot be.
- s.op->newline(-1) << "}"; // for loop
-}
-
-
-void
-hrtimer_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes.empty()) return;
-
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)";
- s.op->newline(1) << "hrtimer_cancel (& stap_hrtimer_probes[i].hrtimer);";
- s.op->indent(-1);
-}
-
-
-
-struct timer_builder: public derived_probe_builder
-{
- virtual void build(systemtap_session & sess,
- probe * base, probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results);
-
- static void register_patterns(systemtap_session& s);
-};
-
-void
-timer_builder::build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
-{
- int64_t scale=1, period, rand=0;
-
- if (!get_param(parameters, "randomize", rand))
- rand = 0;
-
- if (get_param(parameters, "jiffies", period))
- {
- // always use basic timers for jiffies
- finished_results.push_back(
- new timer_derived_probe(base, location, period, rand, false));
- return;
- }
- else if (get_param(parameters, "hz", period))
- {
- if (period <= 0)
- throw semantic_error ("frequency must be greater than 0");
- period = (1000000000 + period - 1)/period;
- }
- else if (get_param(parameters, "s", period)
- || get_param(parameters, "sec", period))
- {
- scale = 1000000000;
- period *= scale;
- rand *= scale;
- }
- else if (get_param(parameters, "ms", period)
- || get_param(parameters, "msec", period))
- {
- scale = 1000000;
- period *= scale;
- rand *= scale;
- }
- else if (get_param(parameters, "us", period)
- || get_param(parameters, "usec", period))
- {
- scale = 1000;
- period *= scale;
- rand *= scale;
- }
- else if (get_param(parameters, "ns", period)
- || get_param(parameters, "nsec", period))
- {
- // ok
- }
- else
- throw semantic_error ("unrecognized timer variant");
-
- // Redirect wallclock-time based probes to hrtimer code on recent
- // enough kernels.
- if (strverscmp(sess.kernel_base_release.c_str(), "2.6.17") < 0)
- {
- // hrtimers didn't exist, so use the old-school timers
- period = (period + 1000000 - 1)/1000000;
- rand = (rand + 1000000 - 1)/1000000;
-
- finished_results.push_back(
- new timer_derived_probe(base, location, period, rand, true));
- }
- else
- finished_results.push_back(
- new hrtimer_derived_probe(base, location, period, rand, scale));
-}
-
-void
-timer_builder::register_patterns(systemtap_session& s)
-{
- match_node* root = s.pattern_root;
- derived_probe_builder *builder = new timer_builder();
-
- root = root->bind(TOK_TIMER);
-
- root->bind_num("s")->bind(builder);
- root->bind_num("s")->bind_num("randomize")->bind(builder);
- root->bind_num("sec")->bind(builder);
- root->bind_num("sec")->bind_num("randomize")->bind(builder);
-
- root->bind_num("ms")->bind(builder);
- root->bind_num("ms")->bind_num("randomize")->bind(builder);
- root->bind_num("msec")->bind(builder);
- root->bind_num("msec")->bind_num("randomize")->bind(builder);
-
- root->bind_num("us")->bind(builder);
- root->bind_num("us")->bind_num("randomize")->bind(builder);
- root->bind_num("usec")->bind(builder);
- root->bind_num("usec")->bind_num("randomize")->bind(builder);
-
- root->bind_num("ns")->bind(builder);
- root->bind_num("ns")->bind_num("randomize")->bind(builder);
- root->bind_num("nsec")->bind(builder);
- root->bind_num("nsec")->bind_num("randomize")->bind(builder);
-
- root->bind_num("jiffies")->bind(builder);
- root->bind_num("jiffies")->bind_num("randomize")->bind(builder);
-
- root->bind_num("hz")->bind(builder);
-}
-
-
-
-// ------------------------------------------------------------------------
-// perfmon derived probes
-// ------------------------------------------------------------------------
-// This is a new interface to the perfmon hw.
-//
-
-
-struct perfmon_var_expanding_visitor: public var_expanding_visitor
-{
- systemtap_session & sess;
- unsigned counter_number;
- perfmon_var_expanding_visitor(systemtap_session & s, unsigned c):
- sess(s), counter_number(c) {}
- void visit_target_symbol (target_symbol* e);
-};
-
-
-void
-perfmon_var_expanding_visitor::visit_target_symbol (target_symbol *e)
-{
- assert(e->base_name.size() > 0 && e->base_name[0] == '$');
-
- // Synthesize a function.
- functiondecl *fdecl = new functiondecl;
- fdecl->tok = e->tok;
- embeddedcode *ec = new embeddedcode;
- ec->tok = e->tok;
- bool lvalue = is_active_lvalue(e);
-
- if (lvalue )
- throw semantic_error("writes to $counter not permitted");
-
- string fname = string("_perfmon_tvar_get")
- + "_" + e->base_name.substr(1)
- + "_" + lex_cast<string>(counter_number);
-
- if (e->base_name != "$counter")
- throw semantic_error ("target variables not available to perfmon probes");
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("perfmon probe '$counter' variable may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("perfmon probe '$counter' variable may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid use of perfmon probe '$counter' variable",
- e->tok);
- break;
- }
- }
-
- ec->code = "THIS->__retvalue = _pfm_pmd_x[" +
- lex_cast<string>(counter_number) + "].reg_num;";
- ec->code += "/* pure */";
- fdecl->name = fname;
- fdecl->body = ec;
- fdecl->type = pe_long;
- sess.functions[fdecl->name]=fdecl;
-
- // Synthesize a functioncall.
- functioncall* n = new functioncall;
- n->tok = e->tok;
- n->function = fname;
- n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
-
- provide (n);
-}
-
-
-enum perfmon_mode
-{
- perfmon_count,
- perfmon_sample
-};
-
-
-struct perfmon_derived_probe: public derived_probe
-{
-protected:
- static unsigned probes_allocated;
-
-public:
- systemtap_session & sess;
- string event;
- perfmon_mode mode;
-
- perfmon_derived_probe (probe* p, probe_point* l, systemtap_session &s,
- string e, perfmon_mode m);
- virtual void join_group (systemtap_session& s);
-};
-
-
-struct perfmon_derived_probe_group: public generic_dpg<perfmon_derived_probe>
-{
-public:
- void emit_module_decls (systemtap_session&) {}
- void emit_module_init (systemtap_session&) {}
- void emit_module_exit (systemtap_session&) {}
-};
-
-
-struct perfmon_builder: public derived_probe_builder
-{
- perfmon_builder() {}
- virtual void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
- {
- string event;
- if (!get_param (parameters, "counter", event))
- throw semantic_error("perfmon requires an event");
-
- sess.perfmon++;
-
- // XXX: need to revise when doing sampling
- finished_results.push_back(new perfmon_derived_probe(base, location,
- sess, event,
- perfmon_count));
- }
-};
-
-
-unsigned perfmon_derived_probe::probes_allocated;
-
-perfmon_derived_probe::perfmon_derived_probe (probe* p, probe_point* l,
- systemtap_session &s,
- string e, perfmon_mode m)
- : derived_probe (p, l), sess(s), event(e), mode(m)
-{
- ++probes_allocated;
-
- // Now expand the local variables in the probe body
- perfmon_var_expanding_visitor v (sess, probes_allocated-1);
- this->body = v.require (this->body);
-
- if (sess.verbose > 1)
- clog << "perfmon-based probe" << endl;
-}
-
-
-void
-perfmon_derived_probe::join_group (systemtap_session& s)
-{
- throw semantic_error ("incomplete", this->tok);
-
- if (! s.perfmon_derived_probes)
- s.perfmon_derived_probes = new perfmon_derived_probe_group ();
- s.perfmon_derived_probes->enroll (this);
-}
-
-
-#if 0
-void
-perfmon_derived_probe::emit_registrations_start (translator_output* o,
- unsigned index)
-{
- for (unsigned i=0; i<locations.size(); i++)
- o->newline() << "enter_" << name << "_" << i << " ();";
-}
-
-
-void
-perfmon_derived_probe::emit_registrations_end (translator_output * o,
- unsigned index)
-{
-}
-
-
-void
-perfmon_derived_probe::emit_deregistrations (translator_output * o)
-{
-}
-
-
-void
-perfmon_derived_probe::emit_probe_entries (translator_output * o)
-{
- o->newline() << "#ifdef STP_TIMING";
- // NB: This variable may be multiply (but identically) defined.
- o->newline() << "static __cacheline_aligned Stat " << "time_" << basest()->name << ";";
- o->newline() << "#endif";
-
- for (unsigned i=0; i<locations.size(); i++)
- {
- probe_point *l = locations[i];
- o->newline() << "/* location " << i << ": " << *l << " */";
- o->newline() << "static void enter_" << name << "_" << i << " (void) {";
-
- o->indent(1);
- o->newline() << "const char* probe_point = "
- << lex_cast_qstring(*l) << ";";
- emit_probe_prologue (o,
- (mode == perfmon_count ?
- "STAP_SESSION_STARTING" :
- "STAP_SESSION_RUNNING"),
- "probe_point");
-
- // NB: locals are initialized by probe function itself
- o->newline() << name << " (c);";
-
- emit_probe_epilogue (o);
-
- o->newline(-1) << "}\n";
- }
-}
-#endif
-
-
-#if 0
-void no_pfm_event_error (string s)
-{
- string msg(string("Cannot find event:" + s));
- throw semantic_error(msg);
-}
-
-
-void no_pfm_mask_error (string s)
-{
- string msg(string("Cannot find mask:" + s));
- throw semantic_error(msg);
-}
-
-
-void
-split(const string& s, vector<string>& v, const string & separator)
-{
- string::size_type last_pos = s.find_first_not_of(separator, 0);
- string::size_type pos = s.find_first_of(separator, last_pos);
-
- while (string::npos != pos || string::npos != last_pos) {
- v.push_back(s.substr(last_pos, pos - last_pos));
- last_pos = s.find_first_not_of(separator, pos);
- pos = s.find_first_of(separator, last_pos);
- }
-}
-
-
-void
-perfmon_derived_probe_group::emit_probes (translator_output* op, unparser* up)
-{
- for (unsigned i=0; i < probes.size(); i++)
- {
- op->newline ();
- up->emit_probe (probes[i]);
- }
-}
-
-
-void
-perfmon_derived_probe_group::emit_module_init (translator_output* o)
-{
- int ret;
- pfmlib_input_param_t inp;
- pfmlib_output_param_t outp;
- pfarg_pmd_t pd[PFMLIB_MAX_PMDS];
- pfarg_pmc_t pc[PFMLIB_MAX_PMCS];
- pfarg_ctx_t ctx;
- pfarg_load_t load_args;
- pfmlib_options_t pfmlib_options;
- unsigned int max_counters;
-
- if ( probes.size() == 0)
- return;
- ret = pfm_initialize();
- if (ret != PFMLIB_SUCCESS)
- throw semantic_error("Unable to generate performance monitoring events (no libpfm)");
-
- pfm_get_num_counters(&max_counters);
-
- memset(&pfmlib_options, 0, sizeof(pfmlib_options));
- pfmlib_options.pfm_debug = 0; /* set to 1 for debug */
- pfmlib_options.pfm_verbose = 0; /* set to 1 for debug */
- pfm_set_options(&pfmlib_options);
-
- memset(pd, 0, sizeof(pd));
- memset(pc, 0, sizeof(pc));
- memset(&ctx, 0, sizeof(ctx));
- memset(&load_args, 0, sizeof(load_args));
-
- /*
- * prepare parameters to library.
- */
- memset(&inp,0, sizeof(inp));
- memset(&outp,0, sizeof(outp));
-
- /* figure out the events */
- for (unsigned i=0; i<probes.size(); ++i)
- {
- if (probes[i]->event == "cycles") {
- if (pfm_get_cycle_event( &inp.pfp_events[i].event) != PFMLIB_SUCCESS)
- no_pfm_event_error(probes[i]->event);
- } else if (probes[i]->event == "instructions") {
- if (pfm_get_inst_retired_event( &inp.pfp_events[i].event) !=
- PFMLIB_SUCCESS)
- no_pfm_event_error(probes[i]->event);
- } else {
- unsigned int event_id = 0;
- unsigned int mask_id = 0;
- vector<string> event_spec;
- split(probes[i]->event, event_spec, ":");
- int num = event_spec.size();
- int masks = num - 1;
-
- if (num == 0)
- throw semantic_error("No events found");
-
- /* setup event */
- if (pfm_find_event(event_spec[0].c_str(), &event_id) != PFMLIB_SUCCESS)
- no_pfm_event_error(event_spec[0]);
- inp.pfp_events[i].event = event_id;
-
- /* set up masks */
- if (masks > PFMLIB_MAX_MASKS_PER_EVENT)
- throw semantic_error("Too many unit masks specified");
-
- for (int j=0; j < masks; j++) {
- if (pfm_find_event_mask(event_id, event_spec[j+1].c_str(),
- &mask_id) != PFMLIB_SUCCESS)
- no_pfm_mask_error(string(event_spec[j+1]));
- inp.pfp_events[i].unit_masks[j] = mask_id;
- }
- inp.pfp_events[i].num_masks = masks;
- }
- }
-
- /* number of counters in use */
- inp.pfp_event_count = probes.size();
-
- // XXX: no elimination of duplicated counters
- if (inp.pfp_event_count>max_counters)
- throw semantic_error("Too many performance monitoring events.");
-
- /* count events both in kernel and user-space */
- inp.pfp_dfl_plm = PFM_PLM0 | PFM_PLM3;
-
- /* XXX: some cases a perfmon register might be used of watch dog
- this code doesn't handle that case */
-
- /* figure out the pmcs for the events */
- if ((ret=pfm_dispatch_events(&inp, NULL, &outp, NULL)) != PFMLIB_SUCCESS)
- throw semantic_error("Cannot configure events");
-
- for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
- pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
- pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
- }
-
- /*
- * There could be more pmc settings than pmd.
- * Figure out the actual pmds to use.
- */
- for (unsigned i=0, j=0; i < inp.pfp_event_count; i++) {
- pd[i].reg_num = outp.pfp_pmcs[j].reg_pmd_num;
- for(; j < outp.pfp_pmc_count; j++)
- if (outp.pfp_pmcs[j].reg_evt_idx != i) break;
- }
-
- // Output the be probes create function
- o->newline() << "static int register_perfmon_probes (void) {";
- o->newline(1) << "int rc = 0;";
-
- o->newline() << "/* data for perfmon */";
- o->newline() << "static int _pfm_num_pmc = " << outp.pfp_pmc_count << ";";
- o->newline() << "static struct pfarg_pmc _pfm_pmc[" << outp.pfp_pmc_count
- << "] = {";
- /* output the needed bits for pmc here */
- for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
- o->newline() << "{.reg_num=" << pc[i].reg_num << ", "
- << ".reg_value=" << lex_cast_hex<string>(pc[i].reg_value)
- << "},";
- }
-
- o->newline() << "};";
- o->newline() << "static int _pfm_num_pmd = " << inp.pfp_event_count << ";";
- o->newline() << "static struct pfarg_pmd _pfm_pmd[" << inp.pfp_event_count
- << "] = {";
- /* output the needed bits for pmd here */
- for (unsigned i=0; i < inp.pfp_event_count; i++) {
- o->newline() << "{.reg_num=" << pd[i].reg_num << ", "
- << ".reg_value=" << pd[i].reg_value << "},";
- }
- o->newline() << "};";
- o->newline();
-
- o->newline() << "_pfm_pmc_x=_pfm_pmc;";
- o->newline() << "_pfm_num_pmc_x=_pfm_num_pmc;";
- o->newline() << "_pfm_pmd_x=_pfm_pmd;";
- o->newline() << "_pfm_num_pmd_x=_pfm_num_pmd;";
-
- // call all the function bodies associated with perfcounters
- for (unsigned i=0; i < probes.size (); i++)
- probes[i]->emit_registrations_start (o,i);
-
- /* generate call to turn on instrumentation */
- o->newline() << "_pfm_context.ctx_flags |= PFM_FL_SYSTEM_WIDE;";
- o->newline() << "rc = rc || _stp_perfmon_setup(&_pfm_desc, &_pfm_context,";
- o->newline(1) << "_pfm_pmc, _pfm_num_pmc,";
- o->newline() << "_pfm_pmd, _pfm_num_pmd);";
- o->newline(-1);
-
- o->newline() << "return rc;";
- o->newline(-1) << "}\n";
-
- // Output the be probes destroy function
- o->newline() << "static void unregister_perfmon_probes (void) {";
- o->newline(1) << "_stp_perfmon_shutdown(_pfm_desc);";
- o->newline(-1) << "}\n";
-}
-#endif
-
-// ------------------------------------------------------------------------
-// kprobes-based probes,which postpone symbol resolution until runtime.
-// ------------------------------------------------------------------------
-
-
-// ------------------------------------------------------------------------
// Standard tapset registry.
// ------------------------------------------------------------------------
void
register_standard_tapsets(systemtap_session & s)
{
- s.pattern_root->bind(TOK_BEGIN)->bind(new be_builder(BEGIN));
- s.pattern_root->bind_num(TOK_BEGIN)->bind(new be_builder(BEGIN));
- s.pattern_root->bind(TOK_END)->bind(new be_builder(END));
- s.pattern_root->bind_num(TOK_END)->bind(new be_builder(END));
- s.pattern_root->bind(TOK_ERROR)->bind(new be_builder(ERROR));
- s.pattern_root->bind_num(TOK_ERROR)->bind(new be_builder(ERROR));
-
- s.pattern_root->bind(TOK_NEVER)->bind(new never_builder());
+ register_tapset_been(s);
+ register_tapset_itrace(s);
+ register_tapset_mark(s);
+ register_tapset_perfmon(s);
+ register_tapset_procfs(s);
+ register_tapset_timers(s);
+ register_tapset_utrace(s);
- timer_builder::register_patterns(s);
- s.pattern_root->bind(TOK_TIMER)->bind("profile")->bind(new profile_builder());
- s.pattern_root->bind("perfmon")->bind_str("counter")
- ->bind(new perfmon_builder());
// dwarf-based kprobe/uprobe parts
dwarf_derived_probe::register_patterns(s);
@@ -11812,72 +7975,10 @@ register_standard_tapsets(systemtap_session & s)
->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)->bind(TOK_RETURN)
->bind(new uprobe_builder ());
- // utrace user-space probes
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_BEGIN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_BEGIN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind(TOK_PROCESS)->bind(TOK_BEGIN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_END)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_END)
- ->bind(new utrace_builder ());
- s.pattern_root->bind(TOK_PROCESS)->bind(TOK_END)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_BEGIN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_BEGIN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_BEGIN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END)
- ->bind(new utrace_builder ());
- s.pattern_root->bind(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL)
- ->bind(new utrace_builder ());
- s.pattern_root->bind(TOK_PROCESS)->bind(TOK_SYSCALL)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN)
- ->bind(new utrace_builder ());
- s.pattern_root->bind(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN)
- ->bind(new utrace_builder ());
-
- // itrace user-space probes
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_INSN)
- ->bind(new itrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_INSN)
- ->bind(new itrace_builder ());
- s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK)
- ->bind(new itrace_builder ());
- s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK)
- ->bind(new itrace_builder ());
-
- // marker-based parts
- s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_MARK)
- ->bind(new mark_builder());
- s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_MARK)->bind_str(TOK_FORMAT)
- ->bind(new mark_builder());
-
// kernel tracepoint probes
s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_TRACE)
->bind(new tracepoint_builder());
- // procfs parts
- s.pattern_root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(new procfs_builder());
- s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_READ)
- ->bind(new procfs_builder());
- s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder());
- s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)
- ->bind(new procfs_builder());
-
// Kprobe based probe
s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)
->bind(new kprobe_builder());
@@ -11901,7 +8002,10 @@ vector<derived_probe_group*>
all_session_groups(systemtap_session& s)
{
vector<derived_probe_group*> g;
-#define DOONE(x) if (s. x##_derived_probes) g.push_back (s. x##_derived_probes)
+
+#define DOONE(x) \
+ if (s. x##_derived_probes) \
+ g.push_back ((derived_probe_group*)(s. x##_derived_probes))
// Note that order *is* important here. We want to make sure we
// register (actually run) begin probes before any other probe type
diff --git a/tapsets.h b/tapsets.h
index 406a69d9..7a74ad31 100644
--- a/tapsets.h
+++ b/tapsets.h
@@ -1,5 +1,5 @@
// -*- C++ -*-
-// Copyright (C) 2005 Red Hat Inc.
+// Copyright (C) 2005, 2009 Red Hat Inc.
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
@@ -13,12 +13,47 @@
#include "staptree.h"
#include "elaborate.h"
-struct derived_probe_group;
-
void register_standard_tapsets(systemtap_session& sess);
std::vector<derived_probe_group*> all_session_groups(systemtap_session& s);
int dwfl_report_offline_predicate (const char* modname, const char* filename);
+void common_probe_entryfn_prologue (translator_output* o, std::string statestr,
+ std::string new_pp, bool overload_processing = true);
+void common_probe_entryfn_epilogue (translator_output* o, bool overload_processing = true);
+
+void register_tapset_been(systemtap_session& sess);
+void register_tapset_itrace(systemtap_session& sess);
+void register_tapset_mark(systemtap_session& sess);
+void register_tapset_perfmon(systemtap_session& sess);
+void register_tapset_procfs(systemtap_session& sess);
+void register_tapset_timers(systemtap_session& sess);
+void register_tapset_utrace(systemtap_session& sess);
+
+
+// ------------------------------------------------------------------------
+// Generic derived_probe_group: contains an ordinary vector of the
+// given type. It provides only the enrollment function.
+
+template <class DP> struct generic_dpg: public derived_probe_group
+{
+protected:
+ std::vector <DP*> probes;
+public:
+ generic_dpg () {}
+ void enroll (DP* probe) { probes.push_back (probe); }
+};
+
+
+// ------------------------------------------------------------------------
+// An update visitor that allows replacing assignments with a function call
+
+struct var_expanding_visitor: public update_visitor
+{
+ static unsigned tick;
+ std::stack<functioncall**> target_symbol_setter_functioncalls;
+ var_expanding_visitor() {}
+ void visit_assignment (assignment* e);
+};
#endif // TAPSETS_H
diff --git a/task_finder.cxx b/task_finder.cxx
new file mode 100644
index 00000000..de1c2208
--- /dev/null
+++ b/task_finder.cxx
@@ -0,0 +1,103 @@
+// task finder for user tapsets
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+
+#include "session.h"
+#include "tapsets.h"
+#include "task_finder.h"
+#include "translate.h"
+#include "util.h"
+
+#include <cstring>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+// ------------------------------------------------------------------------
+// task_finder derived 'probes': These don't really exist. The whole
+// purpose of the task_finder_derived_probe_group is to make sure that
+// stap_start_task_finder()/stap_stop_task_finder() get called only
+// once and in the right place.
+// ------------------------------------------------------------------------
+
+struct task_finder_derived_probe: public derived_probe
+{
+ // Dummy constructor for gcc 3.4 compatibility
+ task_finder_derived_probe (): derived_probe (0) { assert(0); }
+};
+
+
+struct task_finder_derived_probe_group: public generic_dpg<task_finder_derived_probe>
+{
+public:
+ void emit_module_decls (systemtap_session& ) { }
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+void
+task_finder_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ s.op->newline();
+ s.op->newline() << "/* ---- task finder ---- */";
+ s.op->newline() << "rc = stap_start_task_finder();";
+
+ s.op->newline() << "if (rc) {";
+ s.op->newline(1) << "stap_stop_task_finder();";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+task_finder_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ s.op->newline();
+ s.op->newline() << "/* ---- task finder ---- */";
+ s.op->newline() << "stap_stop_task_finder();";
+}
+
+
+// Declare that task_finder is needed in this session
+void
+enable_task_finder(systemtap_session& s)
+{
+ if (! s.task_finder_derived_probes)
+ s.task_finder_derived_probes = new task_finder_derived_probe_group();
+}
+
+// Helper function to emit vma tracker callbacks.
+void
+emit_vma_callback_probe_decl (systemtap_session& s,
+ string path,
+ int64_t pid)
+{
+ s.op->newline() << "{";
+ if (pid == 0)
+ {
+ s.op->line() << " .pathname=\"" << path << "\",";
+ s.op->line() << " .pid=0,";
+ }
+ else
+ {
+ s.op->line() << " .pathname=NULL,";
+ s.op->line() << " .pid=" << pid << ",";
+ }
+ s.op->line() << " .callback=NULL,";
+ s.op->line() << " .mmap_callback=&_stp_tf_mmap_cb,";
+ s.op->line() << " .munmap_callback=&_stp_tf_munmap_cb,";
+ s.op->line() << " .mprotect_callback=NULL,";
+ s.op->line() << " },";
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/task_finder.h b/task_finder.h
new file mode 100644
index 00000000..99419e32
--- /dev/null
+++ b/task_finder.h
@@ -0,0 +1,20 @@
+// -*- C++ -*-
+// Copyright (C) 2009 Red Hat Inc.
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#ifndef TASK_FINDER_H
+#define TASK_FINDER_H
+
+// Declare that task_finder is needed in this session
+void enable_task_finder(systemtap_session& s);
+
+// Helper function to emit vma tracker callbacks.
+void emit_vma_callback_probe_decl (systemtap_session& s, std::string path, int64_t pid);
+
+#endif // TASK_FINDER_H
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */