diff options
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | Makefile.in | 130 | ||||
-rw-r--r-- | tapset-been.cxx | 228 | ||||
-rw-r--r-- | tapset-itrace.cxx | 337 | ||||
-rw-r--r-- | tapset-mark.cxx | 712 | ||||
-rw-r--r-- | tapset-perfmon.cxx | 463 | ||||
-rw-r--r-- | tapset-procfs.cxx | 521 | ||||
-rw-r--r-- | tapset-timers.cxx | 626 | ||||
-rw-r--r-- | tapset-utrace.cxx | 1061 | ||||
-rw-r--r-- | tapsets.cxx | 3996 | ||||
-rw-r--r-- | tapsets.h | 41 | ||||
-rw-r--r-- | task_finder.cxx | 103 | ||||
-rw-r--r-- | task_finder.h | 20 |
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 @@ -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 : */ |