diff options
43 files changed, 2649 insertions, 1153 deletions
@@ -4,7 +4,6 @@ .#* autom4te.* cscope*out -Makefile config.h config.log config.status @@ -18,6 +17,12 @@ systemtap.spec testresults stapio stap_merge +CVS +.checkstyle +.cproject +.metadata +.project +.settings SNAPSHOT *.o git_version.h @@ -1,3 +1,36 @@ +2008-04-14 David Smith <dsmith@redhat.com> + + * elaborate.h (struct derived_probe_group): Removed + emit_module_header virtual function. + * translate.cxx (c_unparser::emit_common_header): Removed calls to + emit_module_header function. + * tapsets.cxx (be_derived_probe>): Removed emit_module_header function. + (struct timer_derived_probe_group): Ditto. + (struct profile_derived_probe_group): Ditto. + (struct procfs_derived_probe_group): Ditto. + (struct hrtimer_derived_probe_group): Ditto. + (struct perfmon_derived_probe_group): Ditto. + (dwarf_derived_probe_group::emit_module_decls): Moved kernel check + back from deleted emit_module_header function. + (uprobe_derived_probe_group::emit_module_decls): Ditto. + (mark_derived_probe_group::join_group): Moved marker + kernel check (to a new embedded code section) from deleted + emit_module_header function. + +2008-04-14 Frank Ch. Eigler <fche@elastic.org> + + * Makefile.am (stapio_*): Become able to link/compile against + bundled elfutils. + * Makefile.in: Regenerated. + +2008-04-09 Martin Hunt <hunt@dragon> + + * buildrun.cxx (run_pass): Remove unused "-d" option + passed to staprun. + + * translate.cxx (emit_symbol_data): When available, + grab symbols from debuginfo instead of /proc/kallsyms. + 2008-04-11 David Smith <dsmith@redhat.com> * elaborate.h (struct derived_probe_group): Added diff --git a/Makefile.am b/Makefile.am index 19451834..f6061da0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,9 @@ stap_CXXFLAGS = $(AM_CXXFLAGS) stap_CPPFLAGS = $(AM_CPPFLAGS) stap_LDFLAGS = $(AM_LDFLAGS) +stapio_CPPFLAGS = $(AM_CPPFLAGS) +stapio_LDFLAGS = $(AM_LDFLAGS) + if BUILD_ELFUTILS # This tells automake's "make distcheck" what we need to compile. DISTCHECK_CONFIGURE_FLAGS = --with-elfutils=$(elfutils_abs_srcdir) @@ -72,6 +75,11 @@ DISTCHECK_CONFIGURE_FLAGS = --with-elfutils=$(elfutils_abs_srcdir) stap_CPPFLAGS += -Iinclude-elfutils stap_LDFLAGS += -Llib-elfutils -Wl,-rpath-link,lib-elfutils \ -Wl,--enable-new-dtags,-rpath,$(pkglibdir) +stapio_CPPFLAGS += -Iinclude-elfutils +stapio_LDFLAGS += -Llib-elfutils -Wl,-rpath-link,lib-elfutils \ + -Wl,--enable-new-dtags,-rpath,$(pkglibdir) + + BUILT_SOURCES += stamp-elfutils CLEANFILES += stamp-elfutils stamp-elfutils: config.status @@ -94,19 +102,18 @@ endif staprun_SOURCES = runtime/staprun/staprun.c runtime/staprun/staprun_funcs.c\ runtime/staprun/ctl.c runtime/staprun/common.c \ - runtime/staprun/cap.c runtime/staprun/symbols.c + runtime/staprun/cap.c staprun_CPPFLAGS = $(AM_CPPFLAGS) -staprun_CFLAGS = @PROCFLAGS@ $(AM_CFLAGS) -staprun_LDADD = @PROCFLAGS@ @cap_LIBS@ -lpthread +staprun_CFLAGS = @PROCFLAGS@ $(AM_CFLAGS) -DSINGLE_THREADED +staprun_LDADD = @PROCFLAGS@ @cap_LIBS@ stapio_SOURCES = runtime/staprun/stapio.c \ runtime/staprun/mainloop.c runtime/staprun/common.c \ - runtime/staprun/ctl.c \ + runtime/staprun/ctl.c runtime/staprun/unwind_data.c \ runtime/staprun/relay.c runtime/staprun/relay_old.c - stapio_CFLAGS = @PROCFLAGS@ $(AM_CFLAGS) -stapio_LDADD = @PROCFLAGS@ -lpthread +stapio_LDADD = @PROCFLAGS@ -ldw -lpthread install-exec-hook: if [ `id -u` -eq 0 ]; then chmod 04111 "$(DESTDIR)$(bindir)/staprun"; fi @@ -169,8 +176,9 @@ dist-hook: dist-add-samples dist-gitversion find $(distdir) -name 'stap' -o -name '*.log' -o -name '*.sum' -o -name 'site.exp' | xargs rm -rf install-data-local: -# mkdir -p $(DESTDIR)$(pkgdatadir)/runtime/transport $(DESTDIR)$(pkgdatadir)/tapset (cd $(srcdir)/runtime; for f in *.[ch]; do $(INSTALL_DATA) -D $$f $(DESTDIR)$(pkgdatadir)/runtime/$$f; done) + (cd $(srcdir)/runtime/unwind; find . \( -name '*.[ch]' \) -print \ + | while read f; do $(INSTALL_DATA) -D $$f $(DESTDIR)$(pkgdatadir)/runtime/unwind/$$f; done) (cd $(srcdir)/runtime/transport; for f in *.[ch]; \ do $(INSTALL_DATA) -D $$f $(DESTDIR)$(pkgdatadir)/runtime/transport/$$f; done) (cd $(srcdir)/runtime/uprobes; for f in Makefile *.[ch]; \ diff --git a/Makefile.in b/Makefile.in index 74ed1bea..26080a85 100644 --- a/Makefile.in +++ b/Makefile.in @@ -37,8 +37,12 @@ bin_PROGRAMS = stap$(EXEEXT) staprun$(EXEEXT) @BUILD_ELFUTILS_TRUE@am__append_2 = -Llib-elfutils -Wl,-rpath-link,lib-elfutils \ @BUILD_ELFUTILS_TRUE@ -Wl,--enable-new-dtags,-rpath,$(pkglibdir) -@BUILD_ELFUTILS_TRUE@am__append_3 = stamp-elfutils -@BUILD_ELFUTILS_TRUE@am__append_4 = stamp-elfutils +@BUILD_ELFUTILS_TRUE@am__append_3 = -Iinclude-elfutils +@BUILD_ELFUTILS_TRUE@am__append_4 = -Llib-elfutils -Wl,-rpath-link,lib-elfutils \ +@BUILD_ELFUTILS_TRUE@ -Wl,--enable-new-dtags,-rpath,$(pkglibdir) + +@BUILD_ELFUTILS_TRUE@am__append_5 = stamp-elfutils +@BUILD_ELFUTILS_TRUE@am__append_6 = stamp-elfutils @BUILD_ELFUTILS_FALSE@stap_DEPENDENCIES = pkglibexec_PROGRAMS = stapio$(EXEEXT) noinst_PROGRAMS = loc2c-test$(EXEEXT) @@ -103,15 +107,15 @@ stap_LINK = $(CXXLD) $(stap_CXXFLAGS) $(CXXFLAGS) $(stap_LDFLAGS) \ $(LDFLAGS) -o $@ am_stapio_OBJECTS = stapio-stapio.$(OBJEXT) stapio-mainloop.$(OBJEXT) \ stapio-common.$(OBJEXT) stapio-ctl.$(OBJEXT) \ - stapio-relay.$(OBJEXT) stapio-relay_old.$(OBJEXT) + stapio-unwind_data.$(OBJEXT) stapio-relay.$(OBJEXT) \ + stapio-relay_old.$(OBJEXT) stapio_OBJECTS = $(am_stapio_OBJECTS) stapio_DEPENDENCIES = -stapio_LINK = $(CCLD) $(stapio_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ +stapio_LINK = $(CCLD) $(stapio_CFLAGS) $(CFLAGS) $(stapio_LDFLAGS) \ $(LDFLAGS) -o $@ am_staprun_OBJECTS = staprun-staprun.$(OBJEXT) \ staprun-staprun_funcs.$(OBJEXT) staprun-ctl.$(OBJEXT) \ - staprun-common.$(OBJEXT) staprun-cap.$(OBJEXT) \ - staprun-symbols.$(OBJEXT) + staprun-common.$(OBJEXT) staprun-cap.$(OBJEXT) staprun_OBJECTS = $(am_staprun_OBJECTS) staprun_DEPENDENCIES = staprun_LINK = $(CCLD) $(staprun_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ @@ -285,30 +289,32 @@ stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@ # of foo-bar.c if it is newer than the foo-bar.o file. Using noinst_foo_SOURCES # instead of foo_SOURCES prevents shipping git_version.h in dist tarballs, # which may cause false GIT_FOO readings. -BUILT_SOURCES = git_version.stamp $(am__append_3) -CLEANFILES = git_version.h $(am__append_4) $(pkglibexec_PROGRAMS) +BUILT_SOURCES = git_version.stamp $(am__append_5) +CLEANFILES = git_version.h $(am__append_6) $(pkglibexec_PROGRAMS) GIT_VERSION_CMD = $(SHELL) $(top_srcdir)/git_version.sh stap_CXXFLAGS = $(AM_CXXFLAGS) stap_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_1) stap_LDFLAGS = $(AM_LDFLAGS) $(am__append_2) +stapio_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_3) +stapio_LDFLAGS = $(AM_LDFLAGS) $(am__append_4) # This tells automake's "make distcheck" what we need to compile. @BUILD_ELFUTILS_TRUE@DISTCHECK_CONFIGURE_FLAGS = --with-elfutils=$(elfutils_abs_srcdir) @BUILD_ELFUTILS_TRUE@stap_DEPENDENCIES = lib-elfutils/libdw.so staprun_SOURCES = runtime/staprun/staprun.c runtime/staprun/staprun_funcs.c\ runtime/staprun/ctl.c runtime/staprun/common.c \ - runtime/staprun/cap.c runtime/staprun/symbols.c + runtime/staprun/cap.c staprun_CPPFLAGS = $(AM_CPPFLAGS) -staprun_CFLAGS = @PROCFLAGS@ $(AM_CFLAGS) -staprun_LDADD = @PROCFLAGS@ @cap_LIBS@ -lpthread +staprun_CFLAGS = @PROCFLAGS@ $(AM_CFLAGS) -DSINGLE_THREADED +staprun_LDADD = @PROCFLAGS@ @cap_LIBS@ stapio_SOURCES = runtime/staprun/stapio.c \ runtime/staprun/mainloop.c runtime/staprun/common.c \ - runtime/staprun/ctl.c \ + runtime/staprun/ctl.c runtime/staprun/unwind_data.c \ runtime/staprun/relay.c runtime/staprun/relay_old.c stapio_CFLAGS = @PROCFLAGS@ $(AM_CFLAGS) -stapio_LDADD = @PROCFLAGS@ -lpthread +stapio_LDADD = @PROCFLAGS@ -ldw -lpthread loc2c_test_SOURCES = loc2c-test.c loc2c.c loc2c_test_CPPFLAGS = $(stap_CPPFLAGS) loc2c_test_LDFLAGS = $(stap_LDFLAGS) @@ -517,12 +523,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stapio-relay.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stapio-relay_old.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stapio-stapio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stapio-unwind_data.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staprun-cap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staprun-common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staprun-ctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staprun-staprun.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staprun-staprun_funcs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/staprun-symbols.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -595,88 +601,102 @@ stap-mdfour.obj: mdfour.c @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o stap-mdfour.obj `if test -f 'mdfour.c'; then $(CYGPATH_W) 'mdfour.c'; else $(CYGPATH_W) '$(srcdir)/mdfour.c'; fi` stapio-stapio.o: runtime/staprun/stapio.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-stapio.o -MD -MP -MF $(DEPDIR)/stapio-stapio.Tpo -c -o stapio-stapio.o `test -f 'runtime/staprun/stapio.c' || echo '$(srcdir)/'`runtime/staprun/stapio.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-stapio.o -MD -MP -MF $(DEPDIR)/stapio-stapio.Tpo -c -o stapio-stapio.o `test -f 'runtime/staprun/stapio.c' || echo '$(srcdir)/'`runtime/staprun/stapio.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-stapio.Tpo $(DEPDIR)/stapio-stapio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/stapio.c' object='stapio-stapio.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-stapio.o `test -f 'runtime/staprun/stapio.c' || echo '$(srcdir)/'`runtime/staprun/stapio.c +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-stapio.o `test -f 'runtime/staprun/stapio.c' || echo '$(srcdir)/'`runtime/staprun/stapio.c stapio-stapio.obj: runtime/staprun/stapio.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-stapio.obj -MD -MP -MF $(DEPDIR)/stapio-stapio.Tpo -c -o stapio-stapio.obj `if test -f 'runtime/staprun/stapio.c'; then $(CYGPATH_W) 'runtime/staprun/stapio.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/stapio.c'; fi` +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-stapio.obj -MD -MP -MF $(DEPDIR)/stapio-stapio.Tpo -c -o stapio-stapio.obj `if test -f 'runtime/staprun/stapio.c'; then $(CYGPATH_W) 'runtime/staprun/stapio.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/stapio.c'; fi` @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-stapio.Tpo $(DEPDIR)/stapio-stapio.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/stapio.c' object='stapio-stapio.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-stapio.obj `if test -f 'runtime/staprun/stapio.c'; then $(CYGPATH_W) 'runtime/staprun/stapio.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/stapio.c'; fi` +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-stapio.obj `if test -f 'runtime/staprun/stapio.c'; then $(CYGPATH_W) 'runtime/staprun/stapio.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/stapio.c'; fi` stapio-mainloop.o: runtime/staprun/mainloop.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-mainloop.o -MD -MP -MF $(DEPDIR)/stapio-mainloop.Tpo -c -o stapio-mainloop.o `test -f 'runtime/staprun/mainloop.c' || echo '$(srcdir)/'`runtime/staprun/mainloop.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-mainloop.o -MD -MP -MF $(DEPDIR)/stapio-mainloop.Tpo -c -o stapio-mainloop.o `test -f 'runtime/staprun/mainloop.c' || echo '$(srcdir)/'`runtime/staprun/mainloop.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-mainloop.Tpo $(DEPDIR)/stapio-mainloop.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/mainloop.c' object='stapio-mainloop.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-mainloop.o `test -f 'runtime/staprun/mainloop.c' || echo '$(srcdir)/'`runtime/staprun/mainloop.c +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-mainloop.o `test -f 'runtime/staprun/mainloop.c' || echo '$(srcdir)/'`runtime/staprun/mainloop.c stapio-mainloop.obj: runtime/staprun/mainloop.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-mainloop.obj -MD -MP -MF $(DEPDIR)/stapio-mainloop.Tpo -c -o stapio-mainloop.obj `if test -f 'runtime/staprun/mainloop.c'; then $(CYGPATH_W) 'runtime/staprun/mainloop.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/mainloop.c'; fi` +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-mainloop.obj -MD -MP -MF $(DEPDIR)/stapio-mainloop.Tpo -c -o stapio-mainloop.obj `if test -f 'runtime/staprun/mainloop.c'; then $(CYGPATH_W) 'runtime/staprun/mainloop.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/mainloop.c'; fi` @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-mainloop.Tpo $(DEPDIR)/stapio-mainloop.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/mainloop.c' object='stapio-mainloop.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-mainloop.obj `if test -f 'runtime/staprun/mainloop.c'; then $(CYGPATH_W) 'runtime/staprun/mainloop.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/mainloop.c'; fi` +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-mainloop.obj `if test -f 'runtime/staprun/mainloop.c'; then $(CYGPATH_W) 'runtime/staprun/mainloop.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/mainloop.c'; fi` stapio-common.o: runtime/staprun/common.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-common.o -MD -MP -MF $(DEPDIR)/stapio-common.Tpo -c -o stapio-common.o `test -f 'runtime/staprun/common.c' || echo '$(srcdir)/'`runtime/staprun/common.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-common.o -MD -MP -MF $(DEPDIR)/stapio-common.Tpo -c -o stapio-common.o `test -f 'runtime/staprun/common.c' || echo '$(srcdir)/'`runtime/staprun/common.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-common.Tpo $(DEPDIR)/stapio-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/common.c' object='stapio-common.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-common.o `test -f 'runtime/staprun/common.c' || echo '$(srcdir)/'`runtime/staprun/common.c +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-common.o `test -f 'runtime/staprun/common.c' || echo '$(srcdir)/'`runtime/staprun/common.c stapio-common.obj: runtime/staprun/common.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-common.obj -MD -MP -MF $(DEPDIR)/stapio-common.Tpo -c -o stapio-common.obj `if test -f 'runtime/staprun/common.c'; then $(CYGPATH_W) 'runtime/staprun/common.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/common.c'; fi` +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-common.obj -MD -MP -MF $(DEPDIR)/stapio-common.Tpo -c -o stapio-common.obj `if test -f 'runtime/staprun/common.c'; then $(CYGPATH_W) 'runtime/staprun/common.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/common.c'; fi` @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-common.Tpo $(DEPDIR)/stapio-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/common.c' object='stapio-common.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-common.obj `if test -f 'runtime/staprun/common.c'; then $(CYGPATH_W) 'runtime/staprun/common.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/common.c'; fi` +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-common.obj `if test -f 'runtime/staprun/common.c'; then $(CYGPATH_W) 'runtime/staprun/common.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/common.c'; fi` stapio-ctl.o: runtime/staprun/ctl.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-ctl.o -MD -MP -MF $(DEPDIR)/stapio-ctl.Tpo -c -o stapio-ctl.o `test -f 'runtime/staprun/ctl.c' || echo '$(srcdir)/'`runtime/staprun/ctl.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-ctl.o -MD -MP -MF $(DEPDIR)/stapio-ctl.Tpo -c -o stapio-ctl.o `test -f 'runtime/staprun/ctl.c' || echo '$(srcdir)/'`runtime/staprun/ctl.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-ctl.Tpo $(DEPDIR)/stapio-ctl.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/ctl.c' object='stapio-ctl.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-ctl.o `test -f 'runtime/staprun/ctl.c' || echo '$(srcdir)/'`runtime/staprun/ctl.c +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-ctl.o `test -f 'runtime/staprun/ctl.c' || echo '$(srcdir)/'`runtime/staprun/ctl.c stapio-ctl.obj: runtime/staprun/ctl.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-ctl.obj -MD -MP -MF $(DEPDIR)/stapio-ctl.Tpo -c -o stapio-ctl.obj `if test -f 'runtime/staprun/ctl.c'; then $(CYGPATH_W) 'runtime/staprun/ctl.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/ctl.c'; fi` +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-ctl.obj -MD -MP -MF $(DEPDIR)/stapio-ctl.Tpo -c -o stapio-ctl.obj `if test -f 'runtime/staprun/ctl.c'; then $(CYGPATH_W) 'runtime/staprun/ctl.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/ctl.c'; fi` @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-ctl.Tpo $(DEPDIR)/stapio-ctl.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/ctl.c' object='stapio-ctl.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-ctl.obj `if test -f 'runtime/staprun/ctl.c'; then $(CYGPATH_W) 'runtime/staprun/ctl.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/ctl.c'; fi` +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-ctl.obj `if test -f 'runtime/staprun/ctl.c'; then $(CYGPATH_W) 'runtime/staprun/ctl.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/ctl.c'; fi` + +stapio-unwind_data.o: runtime/staprun/unwind_data.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-unwind_data.o -MD -MP -MF $(DEPDIR)/stapio-unwind_data.Tpo -c -o stapio-unwind_data.o `test -f 'runtime/staprun/unwind_data.c' || echo '$(srcdir)/'`runtime/staprun/unwind_data.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-unwind_data.Tpo $(DEPDIR)/stapio-unwind_data.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/unwind_data.c' object='stapio-unwind_data.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-unwind_data.o `test -f 'runtime/staprun/unwind_data.c' || echo '$(srcdir)/'`runtime/staprun/unwind_data.c + +stapio-unwind_data.obj: runtime/staprun/unwind_data.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-unwind_data.obj -MD -MP -MF $(DEPDIR)/stapio-unwind_data.Tpo -c -o stapio-unwind_data.obj `if test -f 'runtime/staprun/unwind_data.c'; then $(CYGPATH_W) 'runtime/staprun/unwind_data.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/unwind_data.c'; fi` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-unwind_data.Tpo $(DEPDIR)/stapio-unwind_data.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/unwind_data.c' object='stapio-unwind_data.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-unwind_data.obj `if test -f 'runtime/staprun/unwind_data.c'; then $(CYGPATH_W) 'runtime/staprun/unwind_data.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/unwind_data.c'; fi` stapio-relay.o: runtime/staprun/relay.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay.o -MD -MP -MF $(DEPDIR)/stapio-relay.Tpo -c -o stapio-relay.o `test -f 'runtime/staprun/relay.c' || echo '$(srcdir)/'`runtime/staprun/relay.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay.o -MD -MP -MF $(DEPDIR)/stapio-relay.Tpo -c -o stapio-relay.o `test -f 'runtime/staprun/relay.c' || echo '$(srcdir)/'`runtime/staprun/relay.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-relay.Tpo $(DEPDIR)/stapio-relay.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/relay.c' object='stapio-relay.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay.o `test -f 'runtime/staprun/relay.c' || echo '$(srcdir)/'`runtime/staprun/relay.c +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay.o `test -f 'runtime/staprun/relay.c' || echo '$(srcdir)/'`runtime/staprun/relay.c stapio-relay.obj: runtime/staprun/relay.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay.obj -MD -MP -MF $(DEPDIR)/stapio-relay.Tpo -c -o stapio-relay.obj `if test -f 'runtime/staprun/relay.c'; then $(CYGPATH_W) 'runtime/staprun/relay.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay.c'; fi` +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay.obj -MD -MP -MF $(DEPDIR)/stapio-relay.Tpo -c -o stapio-relay.obj `if test -f 'runtime/staprun/relay.c'; then $(CYGPATH_W) 'runtime/staprun/relay.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay.c'; fi` @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-relay.Tpo $(DEPDIR)/stapio-relay.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/relay.c' object='stapio-relay.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay.obj `if test -f 'runtime/staprun/relay.c'; then $(CYGPATH_W) 'runtime/staprun/relay.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay.c'; fi` +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay.obj `if test -f 'runtime/staprun/relay.c'; then $(CYGPATH_W) 'runtime/staprun/relay.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay.c'; fi` stapio-relay_old.o: runtime/staprun/relay_old.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay_old.o -MD -MP -MF $(DEPDIR)/stapio-relay_old.Tpo -c -o stapio-relay_old.o `test -f 'runtime/staprun/relay_old.c' || echo '$(srcdir)/'`runtime/staprun/relay_old.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay_old.o -MD -MP -MF $(DEPDIR)/stapio-relay_old.Tpo -c -o stapio-relay_old.o `test -f 'runtime/staprun/relay_old.c' || echo '$(srcdir)/'`runtime/staprun/relay_old.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-relay_old.Tpo $(DEPDIR)/stapio-relay_old.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/relay_old.c' object='stapio-relay_old.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay_old.o `test -f 'runtime/staprun/relay_old.c' || echo '$(srcdir)/'`runtime/staprun/relay_old.c +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay_old.o `test -f 'runtime/staprun/relay_old.c' || echo '$(srcdir)/'`runtime/staprun/relay_old.c stapio-relay_old.obj: runtime/staprun/relay_old.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay_old.obj -MD -MP -MF $(DEPDIR)/stapio-relay_old.Tpo -c -o stapio-relay_old.obj `if test -f 'runtime/staprun/relay_old.c'; then $(CYGPATH_W) 'runtime/staprun/relay_old.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay_old.c'; fi` +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -MT stapio-relay_old.obj -MD -MP -MF $(DEPDIR)/stapio-relay_old.Tpo -c -o stapio-relay_old.obj `if test -f 'runtime/staprun/relay_old.c'; then $(CYGPATH_W) 'runtime/staprun/relay_old.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay_old.c'; fi` @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/stapio-relay_old.Tpo $(DEPDIR)/stapio-relay_old.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/relay_old.c' object='stapio-relay_old.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay_old.obj `if test -f 'runtime/staprun/relay_old.c'; then $(CYGPATH_W) 'runtime/staprun/relay_old.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay_old.c'; fi` +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stapio_CPPFLAGS) $(CPPFLAGS) $(stapio_CFLAGS) $(CFLAGS) -c -o stapio-relay_old.obj `if test -f 'runtime/staprun/relay_old.c'; then $(CYGPATH_W) 'runtime/staprun/relay_old.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/relay_old.c'; fi` staprun-staprun.o: runtime/staprun/staprun.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(staprun_CPPFLAGS) $(CPPFLAGS) $(staprun_CFLAGS) $(CFLAGS) -MT staprun-staprun.o -MD -MP -MF $(DEPDIR)/staprun-staprun.Tpo -c -o staprun-staprun.o `test -f 'runtime/staprun/staprun.c' || echo '$(srcdir)/'`runtime/staprun/staprun.c @@ -748,20 +768,6 @@ staprun-cap.obj: runtime/staprun/cap.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(staprun_CPPFLAGS) $(CPPFLAGS) $(staprun_CFLAGS) $(CFLAGS) -c -o staprun-cap.obj `if test -f 'runtime/staprun/cap.c'; then $(CYGPATH_W) 'runtime/staprun/cap.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/cap.c'; fi` -staprun-symbols.o: runtime/staprun/symbols.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(staprun_CPPFLAGS) $(CPPFLAGS) $(staprun_CFLAGS) $(CFLAGS) -MT staprun-symbols.o -MD -MP -MF $(DEPDIR)/staprun-symbols.Tpo -c -o staprun-symbols.o `test -f 'runtime/staprun/symbols.c' || echo '$(srcdir)/'`runtime/staprun/symbols.c -@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/staprun-symbols.Tpo $(DEPDIR)/staprun-symbols.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/symbols.c' object='staprun-symbols.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(staprun_CPPFLAGS) $(CPPFLAGS) $(staprun_CFLAGS) $(CFLAGS) -c -o staprun-symbols.o `test -f 'runtime/staprun/symbols.c' || echo '$(srcdir)/'`runtime/staprun/symbols.c - -staprun-symbols.obj: runtime/staprun/symbols.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(staprun_CPPFLAGS) $(CPPFLAGS) $(staprun_CFLAGS) $(CFLAGS) -MT staprun-symbols.obj -MD -MP -MF $(DEPDIR)/staprun-symbols.Tpo -c -o staprun-symbols.obj `if test -f 'runtime/staprun/symbols.c'; then $(CYGPATH_W) 'runtime/staprun/symbols.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/symbols.c'; fi` -@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/staprun-symbols.Tpo $(DEPDIR)/staprun-symbols.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/staprun/symbols.c' object='staprun-symbols.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(staprun_CPPFLAGS) $(CPPFLAGS) $(staprun_CFLAGS) $(CFLAGS) -c -o staprun-symbols.obj `if test -f 'runtime/staprun/symbols.c'; then $(CYGPATH_W) 'runtime/staprun/symbols.c'; else $(CYGPATH_W) '$(srcdir)/runtime/staprun/symbols.c'; fi` - .cxx.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @@ -1535,8 +1541,9 @@ dist-hook: dist-add-samples dist-gitversion find $(distdir) -name 'stap' -o -name '*.log' -o -name '*.sum' -o -name 'site.exp' | xargs rm -rf install-data-local: -# mkdir -p $(DESTDIR)$(pkgdatadir)/runtime/transport $(DESTDIR)$(pkgdatadir)/tapset (cd $(srcdir)/runtime; for f in *.[ch]; do $(INSTALL_DATA) -D $$f $(DESTDIR)$(pkgdatadir)/runtime/$$f; done) + (cd $(srcdir)/runtime/unwind; find . \( -name '*.[ch]' \) -print \ + | while read f; do $(INSTALL_DATA) -D $$f $(DESTDIR)$(pkgdatadir)/runtime/unwind/$$f; done) (cd $(srcdir)/runtime/transport; for f in *.[ch]; \ do $(INSTALL_DATA) -D $$f $(DESTDIR)$(pkgdatadir)/runtime/transport/$$f; done) (cd $(srcdir)/runtime/uprobes; for f in Makefile *.[ch]; \ @@ -1,5 +1,9 @@ * What's new in version 0.7 +- Stack backtraces for x86 and x86-64 are generated by a dwarf + debuginfo-based unwinder based on the code from <jbeulich@novell.com>. + This should give more accurate backtraces. + - A probe listing mode is available. % stap -l vm.* vm.brk diff --git a/buildrun.cxx b/buildrun.cxx index f3e72272..a186326f 100644 --- a/buildrun.cxx +++ b/buildrun.cxx @@ -254,8 +254,6 @@ run_pass (systemtap_session& s) + (s.verbose>2 ? "-v " : "") + (s.output_file.empty() ? "" : "-o " + s.output_file + " "); - staprun_cmd += "-d " + stringify(getpid()) + " "; - if (s.cmd != "") staprun_cmd += "-c " + cmdstr_quoted(s.cmd) + " "; diff --git a/doc/tutorial.tex b/doc/tutorial.tex index 58673467..3a04900b 100644 --- a/doc/tutorial.tex +++ b/doc/tutorial.tex @@ -175,8 +175,10 @@ in a source file, say \verb+net/socket.c+ in the kernel. The systemtap examines the kernel's debugging information to relate object code to source code. It works like a debugger: if you can name or place it, you can probe it. Use -\verb+kernel.function("*@net/socket.c")+ for the function entries, and -\verb+kernel.function("*@net/socket.c").return+ for the exits. Note +\verb+kernel.function("*@net/socket.c").call+ for the function +entries\footnote{Without the {\tt .call} qualifier, inlined function +instances are also probed, but they have no corresponding {\tt .return}.}, +and \verb+kernel.function("*@net/socket.c").return+ for matching exits. Note the use of wildcards in the function name part, and the subsequent \verb+@FILENAME+ part. You can also put wildcards into the file name, and even add a colon (\verb+:+) and a line number, if you want to diff --git a/elaborate.h b/elaborate.h index f53f3870..30bf5bce 100644 --- a/elaborate.h +++ b/elaborate.h @@ -150,12 +150,6 @@ struct derived_probe_group { virtual ~derived_probe_group () {} - virtual void emit_module_header (systemtap_session& s) = 0; - // The _header-generated code may assume that only basic includes - // have been generated. _header is called near the start of the - // code generation process, before the context, embedded-C code, - // etc. are generated. - virtual void emit_module_decls (systemtap_session& s) = 0; // The _decls-generated code may assume that declarations such as // the context, embedded-C code, function and probe handler bodies diff --git a/runtime/.gitignore b/runtime/.gitignore new file mode 100644 index 00000000..ceddd64c --- /dev/null +++ b/runtime/.gitignore @@ -0,0 +1 @@ +!staprun diff --git a/runtime/ChangeLog b/runtime/ChangeLog index ee191022..09a3e35d 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,13 @@ +2008-04-13 Frank Ch. Eigler <fche@elastic.org> + + * print.c (_stp_pbuf_full): New function to note full print buffer. + * stack-{i386,x86_64}.c: Use it in all stack-searching loops, to + impose another limit against unbounded iteration. + +2008-03-31 Martin Hunt <hunt@redhat.com> + + * runtime.h (STP_USE_DWARF_UNWINDER): Define. + 2008-04-04 Masami Hiramatsu <mhiramat@redhat.com> PR 6028 @@ -6,11 +16,37 @@ * regs-ia64.c (ia64_fetch_register): Don't unwind stack if it has already unwound stack in same probe. +2008-03-30 Martin Hunt <hunt@redhat.com> + + * runtime.h (STP_USE_FRAME_POINTER): Define when frame pointers + are available in the kernel and can be used. + * stack-arm.c: Use STP_USE_FRAME_POINTER. + * stack-i386.c: Ditto. + * unwind/i386.h: Ditto. + * unwind/x86_64.h: Ditto. + 2008-04-04 David Smith <dsmith@redhat.com> PR 5961 (partial) * task_finder.c: New file. +2008-03-28 Martin Hunt <hunt@redhat.com> + + * copy.c (_stp_read_address): New function. Safely read + kernel or userspace. + +2008-03-26 Martin Hunt <hunt@redhat.com> + Fixes to get i386 working. + * unwind.c (unwind): Fix types in debug print. + * stack-i386.c (_stp_stack_print_fallback): New function. + (__stp_stack_print): Call _stp_stack_print_fallback() if unwinder + appears to fail. + +2008-03-25 Martin Hunt <hunt@redhat.com> + + * unwind.c (unwind): Return a positive number to indicate + that unwinding is done. + 2008-04-01 Frank Ch. Eigler <fche@elastic.org> * lket/*: Belatedly remove retired LKET code. diff --git a/runtime/copy.c b/runtime/copy.c index ef3fd223..6bb22762 100644 --- a/runtime/copy.c +++ b/runtime/copy.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * Copy from user space functions - * Copyright (C) 2005, 2006, 2007 Red Hat Inc. + * Copyright (C) 2005-2008 Red Hat Inc. * Copyright (C) 2005 Intel Corporation. * * This file is part of systemtap, and is free software. You can @@ -9,11 +9,10 @@ * later version. */ -#ifndef _COPY_C_ /* -*- linux-c -*- */ +#ifndef _COPY_C_ /* -*- linux-c -*- */ #define _COPY_C_ #include "string.c" - /** @file copy.c * @brief Functions to copy from user space. */ @@ -26,6 +25,28 @@ * @{ */ +/** Safely read from userspace or kernelspace. + * On success, returns 0. Returns -EFAULT on error. + * + * This uses __get_user() to read from userspace or + * kernelspace. Will not sleep or cause pagefaults when + * called from within a kprobe context. + * + * @param segment . KERNEL_DS for kernel access + * USER_DS for userspace. + */ + +#define _stp_read_address(x, ptr, segment) \ + ({ \ + long ret; \ + mm_segment_t ofs = get_fs(); \ + set_fs(segment); \ + ret = __stp_get_user(x, ptr); \ + set_fs(ofs); \ + ret; \ + }) + + long _stp_strncpy_from_user(char *dst, const char __user *src, long count); //static long __stp_strncpy_from_user(char *dst, const char __user *src, long count); @@ -110,8 +131,7 @@ do { \ * <i>count</i> bytes and returns <i>count</i>. */ -long -_stp_strncpy_from_user(char *dst, const char __user *src, long count) +long _stp_strncpy_from_user(char *dst, const char __user *src, long count) { long res = -EFAULT; if (access_ok(VERIFY_READ, src, count)) @@ -119,7 +139,6 @@ _stp_strncpy_from_user(char *dst, const char __user *src, long count) return res; } - /** Copy a block of data from user space. * * If some data could not be copied, this function will pad the copied @@ -133,8 +152,7 @@ _stp_strncpy_from_user(char *dst, const char __user *src, long count) * */ -unsigned long -_stp_copy_from_user (char *dst, const char __user *src, unsigned long count) +unsigned long _stp_copy_from_user(char *dst, const char __user *src, unsigned long count) { if (count) { if (access_ok(VERIFY_READ, src, count)) diff --git a/runtime/debug.h b/runtime/debug.h index 8f877ede..9b2fe5c5 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -14,6 +14,8 @@ * _dbug() writes to systemtap stderr. * errk() writes to the system log. */ +int _stp_transport_state = 0; + #define _dbug(args...) _stp_dbug(__FUNCTION__, __LINE__, args) #define errk(args...) do { \ @@ -21,46 +23,42 @@ printk(args); \ } while (0) -#ifdef DEBUG_TRANSPORT -#undef DEBUG_TRANSPORT -#define DEBUG_TRANSPORT 1 -#else -#define DEBUG_TRANSPORT 0 -#endif +/* + * To use these, enable them from the command line when compiling. + * For example, "stap -DDEBUG_UNWIND=3" + * will activate dbug_unwind() and print messages with level <= 3. + */ -#ifdef DEBUG_UNWIND -#undef DEBUG_UNWIND -#define DEBUG_UNWIND 2 -#else -#define DEBUG_UNWIND 0 -#endif +/* Note: DEBUG_MEM is implemented in alloc.c */ -#ifdef DEBUG_SYMBOLS -#undef DEBUG_SYMBOLS -#define DEBUG_SYMBOLS 4 +#ifdef DEBUG_TRANS /* transport */ +/* Note: transport is debugged using printk() */ +#define dbug_trans(level, args...) do { \ + if ((level) <= DEBUG_TRANS) { \ + printk("%s:%d ",__FUNCTION__, __LINE__); \ + printk(args); \ + } \ + } while (0) #else -#define DEBUG_SYMBOLS 0 +#define dbug_trans(level, args...) ; #endif -#define DEBUG_TYPE (DEBUG_TRANSPORT|DEBUG_UNWIND|DEBUG_SYMBOLS) - -#if DEBUG_TYPE > 0 - -#define dbug(type, args...) do { \ - if ((type) & DEBUG_TYPE) \ +#ifdef DEBUG_UNWIND /* stack unwinder */ +#define dbug_unwind(level, args...) do { \ + if ((level) <= DEBUG_UNWIND) \ _stp_dbug(__FUNCTION__, __LINE__, args); \ } while (0) +#else +#define dbug_unwind(level, args...) ; +#endif -#define kbug(type, args...) do { \ - if ((type) & DEBUG_TYPE) { \ - printk("%s:%d ",__FUNCTION__, __LINE__); \ - printk(args); \ - } \ +#ifdef DEBUG_SYMBOLS +#define dbug_sym(level, args...) do { \ + if ((level) <= DEBUG_SYMBOLS) \ + _stp_dbug(__FUNCTION__, __LINE__, args); \ } while (0) - #else -#define dbug(type, args...) ; -#define kbug(type, args...) ; -#endif /* DEBUG_TYPE > 0 */ +#define dbug_sym(level, args...) ; +#endif #endif /* _STP_DEBUG_H_ */ diff --git a/runtime/map.c b/runtime/map.c index 513e27df..a436d7ed 100644 --- a/runtime/map.c +++ b/runtime/map.c @@ -15,7 +15,6 @@ * @brief Implements maps (associative arrays) and lists */ -#include "alloc.c" #include "sym.c" #include "stat-common.c" #include "map-stat.c" diff --git a/runtime/print.c b/runtime/print.c index 0442ba09..4f99be7b 100644 --- a/runtime/print.c +++ b/runtime/print.c @@ -243,6 +243,16 @@ void _stp_print_char (const char c) pb->len ++; } +/** Check whether the print buffer is full. + * @return non-zero if full + */ + +static int _stp_pbuf_full (void) +{ + _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); + return (pb->len >= STP_BUFFER_SIZE); +} + /* This function is used when printing maps or stats. */ /* Probably belongs elsewhere, but is here for now. */ diff --git a/runtime/probes.c b/runtime/probes.c index 19539044..6fe844fb 100644 --- a/runtime/probes.c +++ b/runtime/probes.c @@ -25,7 +25,7 @@ void _stp_unregister_jprobes (struct jprobe *probes, int num_probes) int i; for (i = 0; i < num_probes; i++) unregister_jprobe(&probes[i]); - dbug("All jprobes removed\n"); + // dbug("All jprobes removed\n"); } /** Register a group of jprobes. @@ -46,7 +46,7 @@ int _stp_register_jprobes (struct jprobe *probes, int num_probes) ret = -1; /* FIXME */ goto out; } - dbug("inserting jprobe at %s (%p)\n", probes[i].kp.addr, addr); + // dbug("inserting jprobe at %s (%p)\n", probes[i].kp.addr, addr); probes[i].kp.addr = (kprobe_opcode_t *)addr; ret = register_jprobe(&probes[i]); if (ret) @@ -69,7 +69,7 @@ void _stp_unregister_kprobes (struct kprobe *probes, int num_probes) int i; for (i = 0; i < num_probes; i++) unregister_kprobe(&probes[i]); - dbug("All kprobes removed\n"); + // dbug("All kprobes removed\n"); } @@ -83,7 +83,7 @@ void _stp_unregister_kretprobes (struct kretprobe *probes, int num_probes) int i; for (i = 0; i < num_probes; i++) unregister_kretprobe(&probes[i]); - dbug("All return probes removed\n"); + // dbug("All return probes removed\n"); } #endif @@ -104,7 +104,7 @@ int _stp_register_kprobes (struct kprobe *probes, int num_probes) ret = -1; goto out; } - dbug("inserting kprobe at %s (%p)\n", probes[i].addr, addr); + // dbug("inserting kprobe at %s (%p)\n", probes[i].addr, addr); probes[i].addr = (kprobe_opcode_t *)addr; ret = register_kprobe(&probes[i]); if (ret) @@ -136,7 +136,7 @@ int _stp_register_kretprobes (struct kretprobe *probes, int num_probes) ret = -1; /* FIXME */ goto out; } - dbug("inserting kretprobe at %s (%p)\n", probes[i].kp.addr, addr); + // dbug("inserting kretprobe at %s (%p)\n", probes[i].kp.addr, addr); probes[i].kp.addr = (kprobe_opcode_t *)addr; ret = register_kretprobe(&probes[i]); if (ret) diff --git a/runtime/runtime.h b/runtime/runtime.h index 318d3038..8d267173 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -70,13 +70,24 @@ static struct #define MAXSTRINGLEN 128 #endif +#ifdef CONFIG_FRAME_POINTER +/* Just because frame pointers are available does not mean we can trust them. */ +#if defined (__i386__) || defined (__arm__) +#define STP_USE_FRAME_POINTER +#endif +#endif + +/* dwarf unwinder only tested so far on i386 and x86_64 */ +#if !defined(STP_USE_FRAME_BUFFER) && (defined(__i386__) || defined(__x86_64__)) +#define STP_USE_DWARF_UNWINDER +#endif + #include "alloc.c" #include "print.c" #include "string.c" #include "io.c" #include "arith.c" #include "copy.c" -#include "sym.h" #include "sym.c" #ifdef STP_PERFMON #include "perf.c" diff --git a/runtime/stack-arm.c b/runtime/stack-arm.c index 0c8ce450..9b0b772d 100644 --- a/runtime/stack-arm.c +++ b/runtime/stack-arm.c @@ -33,7 +33,7 @@ static int __init find_str_pc_offset(void) static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels) { -#if defined(CONFIG_FRAME_POINTER) +#ifdef STP_USE_FRAME_POINTER int pc_offset = find_str_pc_offset(); unsigned long *fp = (unsigned long *)regs->ARM_fp; unsigned long *next_fp, *pc; @@ -68,5 +68,5 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels) fp = next_fp; } -#endif +#endif /* STP_USE_FRAME_POINTER */ } diff --git a/runtime/stack-i386.c b/runtime/stack-i386.c index b46ff06b..78f89b0d 100644 --- a/runtime/stack-i386.c +++ b/runtime/stack-i386.c @@ -8,40 +8,72 @@ * later version. */ -static inline int _stp_valid_stack_ptr(unsigned long context, unsigned long p) +static int _stp_valid_stack_ptr(unsigned long context, unsigned long p) { return p > context && p < context + THREAD_SIZE - 3; } +/* DWARF unwinder failed. Just dump intereting addresses on kernel stack. */ +static void _stp_stack_print_fallback(unsigned long context, unsigned long stack, int verbose) +{ + unsigned long addr; + while (_stp_valid_stack_ptr(context, stack) && + !_stp_pbuf_full()) { + if (unlikely(_stp_read_address(addr, (unsigned long *)stack, KERNEL_DS))) { + /* cannot access stack. give up. */ + return; + } + _stp_func_print(addr, verbose, 0); + stack++; + } +} + static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels) { unsigned long *stack = (unsigned long *)®_SP(regs); unsigned long context = (unsigned long)stack & ~(THREAD_SIZE - 1); - unsigned long addr; -#ifdef CONFIG_FRAME_POINTER - { - #ifdef STAPCONF_X86_UNIREGS - unsigned long ebp = regs->bp; - #else - unsigned long ebp = regs->ebp; - #endif - - while (_stp_valid_stack_ptr(context, (unsigned long)ebp)) { - addr = *(unsigned long *)(ebp + 4); - if (verbose) { - _stp_print_char(' '); - _stp_symbol_print (addr); - _stp_print_char('\n'); - } else - _stp_printf ("0x%08lx ", addr); - ebp = *(unsigned long *)ebp; +#ifdef STP_USE_FRAME_POINTER + /* FIXME: need to use _stp_func_print() and safe copy */ + unsigned long addr; + +#ifdef STAPCONF_X86_UNIREGS + unsigned long ebp = regs->bp; +#else + unsigned long ebp = regs->ebp; +#endif /* STAPCONF_X86_UNIREGS */ + + while (_stp_valid_stack_ptr(context, (unsigned long)ebp) && + !_stp_pbuf_full()) { + if (unlikely(_stp_read_address(addr, (unsigned long *)(ebp + 4), KERNEL_DS))) { + /* cannot access stack. give up. */ + return; } + if (verbose) { + _stp_print_char(' '); + _stp_symbol_print (addr); + _stp_print_char('\n'); + } else + _stp_printf ("0x%08lx ", addr); + ebp = *(unsigned long *)ebp; } #else - while (_stp_valid_stack_ptr(context, (unsigned long)stack)) { - addr = *stack++; - _stp_func_print(addr, verbose, 1); + struct unwind_frame_info info; + arch_unw_init_frame_info(&info, regs); + + while (!arch_unw_user_mode(&info) && + !_stp_pbuf_full ()) { + int ret = unwind(&info); + dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); + if (ret == 0) { + _stp_func_print(UNW_PC(&info), verbose, 1); + continue; + } + /* If an error happened or we hit a kretprobe trampoline, use fallback backtrace */ + /* FIXME: is there a way to unwind across kretprobe trampolines? */ + if (ret < 0 || (ret > 0 && UNW_PC(&info) == _stp_kretprobe_trampoline)) + _stp_stack_print_fallback(context, UNW_SP(&info), verbose); + break; } -#endif +#endif /* STP_USE_FRAME_POINTER */ } diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c index 186b2ad4..9915c594 100644 --- a/runtime/stack-x86_64.c +++ b/runtime/stack-x86_64.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * x86_64 stack tracing functions - * Copyright (C) 2005, 2006, 2007 Red Hat Inc. + * Copyright (C) 2005-2008 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 @@ -8,13 +8,38 @@ * later version. */ -static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels) +/* DWARF unwinder failed. Just dump intereting addresses on kernel stack. */ +static void _stp_stack_print_fallback(unsigned long stack, int verbose) { - unsigned long *stack = (unsigned long *)REG_SP(regs); unsigned long addr; + while (stack & (THREAD_SIZE - 1) && + !_stp_pbuf_full()) { + if (unlikely(_stp_read_address(addr, (unsigned long *)stack, KERNEL_DS))) { + /* cannot access stack. give up. */ + return; + } + _stp_func_print(addr, verbose, 0); + stack++; + } +} + +static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels) +{ + // FIXME: large stack allocation + struct unwind_frame_info info; + arch_unw_init_frame_info(&info, regs); - while ((long)stack & (THREAD_SIZE-1)) { - addr = *stack++; - _stp_func_print(addr, verbose, 1); + while (!arch_unw_user_mode(&info)) { + int ret = unwind(&info); + dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); + if (ret == 0) { + _stp_func_print(UNW_PC(&info), verbose, 1); + continue; + } + /* If an error happened or we hit a kretprobe trampoline, use fallback backtrace */ + /* FIXME: is there a way to unwind across kretprobe trampolines? */ + if (ret < 0 || (ret > 0 && UNW_PC(&info) == _stp_kretprobe_trampoline)) + _stp_stack_print_fallback(UNW_SP(&info), verbose); + break; } } diff --git a/runtime/stack.c b/runtime/stack.c index 9c01d65c..772c5baf 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * Stack tracing functions - * Copyright (C) 2005, 2006, 2007 Red Hat Inc. + * Copyright (C) 2005-2008 Red Hat Inc. * Copyright (C) 2005 Intel Corporation. * * This file is part of systemtap, and is free software. You can @@ -23,6 +23,7 @@ #include "sym.c" #include "regs.h" +#include "unwind.c" #define MAXBACKTRACE 20 @@ -57,12 +58,15 @@ void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instan _stp_symbol_print((unsigned long)_stp_ret_addr_r(pi)); } else { _stp_print_char(' '); - _stp_symbol_print (REG_IP(regs)); + _stp_symbol_print(REG_IP(regs)); } _stp_print_char('\n'); - } else - _stp_printf ("%p ", (int64_t)REG_IP(regs)); - __stp_stack_print (regs, verbose, 0); + } else if (pi) + _stp_printf("%p %p ", (int64_t)(long)_stp_ret_addr_r(pi), (int64_t) REG_IP(regs)); + else + _stp_printf("%p ", (int64_t) REG_IP(regs)); + + __stp_stack_print(regs, verbose, 0); } /** Writes stack backtrace to a string @@ -71,7 +75,7 @@ void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instan * @param regs A pointer to the struct pt_regs. * @returns void */ -void _stp_stack_snprint (char *str, int size, struct pt_regs *regs, int verbose, struct kretprobe_instance *pi) +void _stp_stack_snprint(char *str, int size, struct pt_regs *regs, int verbose, struct kretprobe_instance *pi) { /* To get a string, we use a simple trick. First flush the print buffer, */ /* then call _stp_stack_print, then copy the result into the output string */ @@ -83,19 +87,18 @@ void _stp_stack_snprint (char *str, int size, struct pt_regs *regs, int verbose, pb->len = 0; } - /** Prints the user stack backtrace * @param str string * @returns Same string as was input with trace info appended, * @note Currently limited to a depth of two. Works from jprobes and kprobes. */ #if 0 -void _stp_ustack_print (char *str) +void _stp_ustack_print(char *str) { - struct pt_regs *nregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) current->thread_info)) - 1; - _stp_printf ("%p : [user]\n", (int64_t)REG_IP(nregs)); + struct pt_regs *nregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)current->thread_info)) - 1; + _stp_printf("%p : [user]\n", (int64_t) REG_IP(nregs)); if (REG_SP(nregs)) - _stp_printf ("%p : [user]\n", (int64_t)(*(unsigned long *)REG_SP(nregs))); + _stp_printf("%p : [user]\n", (int64_t) (*(unsigned long *)REG_SP(nregs))); } #endif /* 0 */ diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index 2e0c3c5c..1dcb6b00 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -27,7 +27,7 @@ static void sigproc(int signum) return; send_request(STP_EXIT, NULL, 0); } else if (signum == SIGQUIT) - cleanup_and_exit(2); + cleanup_and_exit(2); else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) send_request(STP_EXIT, NULL, 0); } @@ -40,7 +40,7 @@ static void setup_main_signals(int cleanup) if (cleanup == 0) { a.sa_handler = sigproc; sigaction(SIGCHLD, &a, NULL); - } else + } else a.sa_handler = SIG_IGN; sigaction(SIGINT, &a, NULL); sigaction(SIGTERM, &a, NULL); @@ -48,7 +48,6 @@ static void setup_main_signals(int cleanup) sigaction(SIGQUIT, &a, NULL); } - /* * start_cmd forks the command given on the command line * with the "-c" option. It will not exec that command @@ -75,7 +74,7 @@ void start_cmd(void) a.sa_handler = SIG_IGN; sigaction(SIGINT, &a, NULL); - dbug (1, "execing target_cmd %s\n", target_cmd); + dbug(1, "execing target_cmd %s\n", target_cmd); if ((pid = fork()) < 0) { _perr("fork"); exit(1); @@ -86,8 +85,8 @@ void start_cmd(void) sigaction(SIGINT, &a, NULL); /* commands we fork need to run at normal priority */ - setpriority (PRIO_PROCESS, 0, 0); - + setpriority(PRIO_PROCESS, 0, 0); + /* wait here until signaled */ sigwait(&usrset, &signum); @@ -107,11 +106,11 @@ void system_cmd(char *cmd) { pid_t pid; - dbug (2, "system %s\n", cmd); + dbug(2, "system %s\n", cmd); if ((pid = fork()) < 0) { _perr("fork"); } else if (pid == 0) { - setpriority (PRIO_PROCESS, 0, 0); + setpriority(PRIO_PROCESS, 0, 0); if (execl("/bin/sh", "sh", "-c", cmd, NULL) < 0) perr("%s", cmd); _exit(1); @@ -128,7 +127,7 @@ static void read_buffer_info(void) if (!use_old_transport) return; - if (statfs("/sys/kernel/debug", &st) == 0 && (int) st.f_type == (int) DEBUGFS_MAGIC) + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) return; if (sprintf_chk(buf, "/proc/systemtap/%s/bufsize", modname)) @@ -152,7 +151,6 @@ static void read_buffer_info(void) return; } - /** * init_stapio - initialize the app * @print_summary: boolean, print summary or not at end of run @@ -177,7 +175,7 @@ int init_stapio(void) if (init_oldrelayfs() < 0) { close_ctl_channel(); return -1; - } + } } else { if (init_relayfs() < 0) { close_ctl_channel(); @@ -192,7 +190,6 @@ int init_stapio(void) if (target_cmd) start_cmd(); - return 0; } @@ -202,7 +199,7 @@ int init_stapio(void) * 2 - disconnected * 3 - initialized */ -void cleanup_and_exit (int closed) +void cleanup_and_exit(int closed) { pid_t err; static int exiting = 0; @@ -219,7 +216,7 @@ void cleanup_and_exit (int closed) err = waitpid(-1, NULL, WNOHANG); if (err >= 0) err("\nWaiting for processes to exit\n"); - while(wait(NULL) > 0) ; + while (wait(NULL) > 0) ; if (use_old_transport) close_oldrelayfs(closed == 2); @@ -230,8 +227,7 @@ void cleanup_and_exit (int closed) close_ctl_channel(); if (initialized == 2 && closed == 2) { - err("\nDisconnecting from systemtap module.\n" \ - "To reconnect, type \"staprun -A %s\"\n", modname); + err("\nDisconnecting from systemtap module.\n" "To reconnect, type \"staprun -A %s\"\n", modname); } else if (initialized) closed = 3; else @@ -247,7 +243,7 @@ int stp_main_loop(void) { ssize_t nb; void *data; - int type; + uint32_t type; FILE *ofp = stdout; char recvbuf[8196]; @@ -257,80 +253,93 @@ int stp_main_loop(void) dbug(2, "in main loop\n"); send_request(STP_READY, NULL, 0); - while (1) { /* handle messages from control channel */ + while (1) { /* handle messages from control channel */ nb = read(control_channel, recvbuf, sizeof(recvbuf)); if (nb <= 0) { if (errno != EINTR) _perr("Unexpected EOF in read (nb=%ld)", (long)nb); continue; } - - type = *(int *)recvbuf; - data = (void *)(recvbuf + sizeof(int)); - switch (type) { + type = *(uint32_t *)recvbuf; + data = (void *)(recvbuf + sizeof(uint32_t)); + nb -= sizeof(uint32_t); + + switch (type) { #ifdef STP_OLD_TRANSPORT case STP_REALTIME_DATA: - { - ssize_t bw = write(out_fd[0], data, nb - sizeof(int)); - if (bw >= 0 && bw != (nb - (ssize_t)sizeof(int))) { - nb = nb - bw; - bw = write(out_fd[0], data, nb - sizeof(int)); - } - if (bw != (nb - (ssize_t)sizeof(int))) { - _perr("write error (nb=%ld)", (long)nb); - cleanup_and_exit(1); + { + ssize_t bw = write(out_fd[0], data, nb); + if (bw >= 0 && bw != nb) { + nb = nb - bw; + bw = write(out_fd[0], data, nb); + } + if (bw != nb) { + _perr("write error (nb=%ld)", (long)nb); + cleanup_and_exit(1); + } + break; } - break; - } #endif case STP_OOB_DATA: - fputs ((char *)data, stderr); - break; - case STP_EXIT: - { - /* module asks us to unload it and exit */ - int *closed = (int *)data; - dbug(2, "got STP_EXIT, closed=%d\n", *closed); - cleanup_and_exit(*closed); - break; - } - case STP_START: - { - struct _stp_msg_start *t = (struct _stp_msg_start *)data; - dbug(2, "probe_start() returned %d\n", t->res); - if (t->res < 0) { - if (target_cmd) - kill (target_pid, SIGKILL); - cleanup_and_exit(1); - } else if (target_cmd) - kill (target_pid, SIGUSR1); + fputs((char *)data, stderr); break; - } + case STP_EXIT: + { + /* module asks us to unload it and exit */ + int *closed = (int *)data; + dbug(2, "got STP_EXIT, closed=%d\n", *closed); + cleanup_and_exit(*closed); + break; + } + case STP_START: + { + struct _stp_msg_start *t = (struct _stp_msg_start *)data; + dbug(2, "probe_start() returned %d\n", t->res); + if (t->res < 0) { + if (target_cmd) + kill(target_pid, SIGKILL); + cleanup_and_exit(1); + } else if (target_cmd) + kill(target_pid, SIGUSR1); + break; + } case STP_SYSTEM: - { - struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; - dbug(2, "STP_SYSTEM: %s\n", c->cmd); - system_cmd(c->cmd); - break; - } + { + struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; + dbug(2, "STP_SYSTEM: %s\n", c->cmd); + system_cmd(c->cmd); + break; + } case STP_TRANSPORT: - { - struct _stp_msg_start ts; - if (use_old_transport) { - if (init_oldrelayfs() < 0) - cleanup_and_exit(1); - } else { - if (init_relayfs() < 0) - cleanup_and_exit(1); + { + struct _stp_msg_start ts; + if (use_old_transport) { + if (init_oldrelayfs() < 0) + cleanup_and_exit(1); + } else { + if (init_relayfs() < 0) + cleanup_and_exit(1); + } + ts.target = target_pid; + initialized = 2; + send_request(STP_START, &ts, sizeof(ts)); + if (load_only) + cleanup_and_exit(2); + break; + } + case STP_UNWIND: + { + int len; + char *ptr = (char *)data; + while (nb > 0) { + send_unwind_data(ptr); + len = strlen(ptr) + 1; + ptr += len; + nb -= len; + } + break; } - ts.target = target_pid; - initialized = 2; - send_request(STP_START, &ts, sizeof(ts)); - if (load_only) - cleanup_and_exit(2); - break; - } default: err("WARNING: ignored message of type %d\n", (type)); } diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index f4e67fdb..8db2ac5b 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -181,8 +181,6 @@ static void cleanup(int rc) if (setpriority (PRIO_PROCESS, 0, 0) < 0) _perr("setpriority"); - stop_symbol_thread(); - /* rc == 2 means disconnected */ if (rc == 2) return; @@ -278,7 +276,6 @@ int main(int argc, char **argv) exit(1); setup_staprun_signals(); - start_symbol_thread(); rc = run_stapio(argv); cleanup(rc); diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 1128fb4c..08797a37 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -118,8 +118,7 @@ int init_stapio(void); int stp_main_loop(void); int send_request(int type, void *data, int len); void cleanup_and_exit (int); -int do_module(void *); -int do_kernel_symbols(void); +void send_unwind_data(const char *name); int init_ctl_channel(int); void close_ctl_channel(void); int init_relayfs(void); diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index 34e12c25..b95a9a5a 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -401,95 +401,3 @@ int check_permissions(void) * is in that directory. */ return check_path(); } - -pthread_t symbol_thread_id = (pthread_t)0; -int kernel_ptr_size = 0; - -/* Symbol handling thread */ -void *handle_symbols(void __attribute__((unused)) *arg) -{ - ssize_t nb; - void *data; - int32_t type; - char recvbuf[8192]; - - dbug(2, "waiting for symbol requests\n"); - - /* handle messages from control channel */ - while (1) { - nb = read(control_channel, recvbuf, sizeof(recvbuf)); - if (nb <= 0) { - if (errno != EINTR) - _perr("Unexpected EOF in read (nb=%ld)", (long)nb); - continue; - } - - type = *(int32_t *)recvbuf; - data = (void *)(recvbuf + sizeof(int32_t)); - - switch (type) { - case STP_MODULE: - { - dbug(2, "STP_MODULES request received\n"); - if (do_module(data) < 0) - goto done; - break; - } - case STP_SYMBOLS: - { - struct _stp_msg_symbol *req = (struct _stp_msg_symbol *)data; - dbug(2, "STP_SYMBOLS request received\n"); - if (req->endian != 0x1234) { - err("ERROR: staprun is compiled with different endianess than the kernel!\n"); - goto done; - } - kernel_ptr_size = req->ptr_size; - if (kernel_ptr_size != 4 && kernel_ptr_size != 8) { - err("ERROR: invalid kernel pointer size %d\n", kernel_ptr_size); - goto done; - } - if (do_kernel_symbols() < 0) - goto done; - break; - } - default: - err("WARNING: ignored message of type %d\n", (type)); - } - } - -done: - /* signal stapio we're done */ - kill(0, SIGINT); - - return NULL; -} - -void start_symbol_thread(void) -{ - int status; - - /* create symbol control channel */ - status = do_cap(CAP_DAC_OVERRIDE, init_ctl_channel, 1); - drop_cap(CAP_DAC_OVERRIDE); - if (status < 0) { - err("Failed to initialize control channel.\n"); - exit(1); - } - status = pthread_create(&symbol_thread_id, NULL, handle_symbols, NULL); - if (status) { - perr("Failed to create symbol thread.\n"); - exit(1); - } -} - -void stop_symbol_thread(void) -{ - - if (symbol_thread_id) { - dbug(2, "Stopping symbol thread.\n"); - pthread_cancel(symbol_thread_id); - pthread_join(symbol_thread_id, NULL); - } - close_ctl_channel(); -} - diff --git a/runtime/staprun/unwind_data.c b/runtime/staprun/unwind_data.c new file mode 100644 index 00000000..ed27cc20 --- /dev/null +++ b/runtime/staprun/unwind_data.c @@ -0,0 +1,97 @@ +/* -*- linux-c -*- + * Unwind data functions for staprun. + * + * Copyright (C) 2008 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. + */ + +#include "staprun.h" +#include <elfutils/libdwfl.h> +#include <dwarf.h> + +static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug"; +static char *debuginfo_path = debuginfo_path_arr; +static const Dwfl_Callbacks kernel_callbacks = { + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + .find_elf = dwfl_linux_kernel_find_elf, + .section_address = dwfl_linux_kernel_module_section_address, +}; + +void *get_module_unwind_data(Dwfl * dwfl, const char *name, int *len) +{ + Dwarf_Addr bias = 0; + Dwarf *dw; + GElf_Ehdr *ehdr, ehdr_mem; + GElf_Shdr *shdr, shdr_mem; + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + + Dwfl_Module *mod = dwfl_report_module(dwfl, name, 0, 0); + dwfl_report_end(dwfl, NULL, NULL); + dw = dwfl_module_getdwarf(mod, &bias); + Elf *elf = dwarf_getelf(dw); + ehdr = gelf_getehdr(elf, &ehdr_mem); + while ((scn = elf_nextscn(elf, scn))) { + shdr = gelf_getshdr(scn, &shdr_mem); + if (strcmp(elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name), ".debug_frame") == 0) { + data = elf_rawdata(scn, NULL); + break; + } + } + + if (data == NULL) { + *len = 0; + dbug(2, "module %s returns NULL\n", name); + return NULL; + } + dbug(2, "module %s returns %d\n", name, (int)data->d_size); + *len = data->d_size; + return data->d_buf; +} + +void send_unwind_data(const char *name) +{ + struct _stp_msg_unwind *un; + int unwind_data_len = 0; + void *unwind_data = NULL; + char *buf; + + dbug(2, "module %s\n", name); + if (strcmp(name, "*")) { + Dwfl *dwfl = dwfl_begin(&kernel_callbacks); + + if (name[0] == 0) + unwind_data = get_module_unwind_data(dwfl, "kernel", &unwind_data_len); + else + unwind_data = get_module_unwind_data(dwfl, name, &unwind_data_len); + + /* yuck */ + buf = (char *)malloc(unwind_data_len + sizeof(*un) + sizeof(uint32_t)); + if (!buf) { + err("malloc failed\n"); + return; + } + memcpy(buf + sizeof(*un) + sizeof(uint32_t), unwind_data, unwind_data_len); + dwfl_end(dwfl); + } else { + buf = (char *)malloc(sizeof(*un) + sizeof(uint32_t)); + if (!buf) { + err("malloc failed\n"); + return; + } + } + + un = (struct _stp_msg_unwind *)(buf + sizeof(uint32_t)); + strncpy(un->name, name, sizeof(un->name)); + un->unwind_len = unwind_data_len; + *(uint32_t *) buf = STP_UNWIND; + + /* send unwind data */ + if (write(control_channel, buf, unwind_data_len + sizeof(*un) + sizeof(uint32_t)) <= 0) + err("write failed\n"); +} diff --git a/runtime/sym.c b/runtime/sym.c index 3c2f859a..3d5ff01d 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -33,7 +33,7 @@ unsigned long _stp_module_relocate(const char *module, const char *section, unsi return 0; } - dbug(DEBUG_SYMBOLS, "%s, %s, %lx\n", module, section, offset); + dbug_sym(1, "%s, %s, %lx\n", module, section, offset); STP_RLOCK_MODULES; if (!module || !strcmp(section, "") /* absolute, unrelocated address */ @@ -47,7 +47,7 @@ unsigned long _stp_module_relocate(const char *module, const char *section, unsi if (!strcmp(module, last->name) && !strcmp(section, last_sec->symbol)) { offset += last_sec->addr; STP_RUNLOCK_MODULES; - dbug(DEBUG_SYMBOLS, "offset = %lx\n", offset); + dbug_sym(1, "offset = %lx\n", offset); return offset; } } @@ -72,7 +72,7 @@ unsigned long _stp_module_relocate(const char *module, const char *section, unsi if (!strcmp(section, last_sec->symbol)) { offset += last_sec->addr; STP_RUNLOCK_MODULES; - dbug(DEBUG_SYMBOLS, "offset = %lx\n", offset); + dbug_sym(1, "offset = %lx\n", offset); return offset; } } diff --git a/runtime/sym.h b/runtime/sym.h index b124882a..0bb64c13 100644 --- a/runtime/sym.h +++ b/runtime/sym.h @@ -7,8 +7,8 @@ * later version. */ -#ifndef _STAP_SYMBOLS_H_ -#define _STAP_SYMBOLS_H_ +#ifndef _STP_SYM_H_ +#define _STP_SYM_H_ #define STP_MODULE_NAME_LEN 64 @@ -16,11 +16,6 @@ struct _stp_symbol { unsigned long addr; const char *symbol; }; -struct stap_symbol { - unsigned long addr; - const char *symbol; - const char *module; -}; DEFINE_RWLOCK(_stp_module_lock); #define STP_RLOCK_MODULES read_lock_irqsave(&_stp_module_lock, flags) @@ -50,8 +45,14 @@ struct _stp_module { /* how many sections this module has */ uint32_t num_sections; - /* how the symbol_data below was allocated */ - int32_t allocated; /* 0 = kmalloc, 1 = vmalloc */ + /* how the data below was allocated */ + /* 0 = kmalloc, 1 = vmalloc */ + struct { + unsigned symbols :1; + unsigned symbol_data :1; + unsigned unwind_data :1; + unsigned unwind_hdr :1; + } allocated; struct _stp_symbol *sections; @@ -63,7 +64,10 @@ struct _stp_module { /* the stack unwind data for this module */ void *unwind_data; + void *unwind_hdr; uint32_t unwind_data_len; + uint32_t unwind_hdr_len; + uint32_t unwind_is_ehframe; /* unwind data comes from .eh_frame */ rwlock_t lock; /* lock while unwinding is happening */ }; @@ -80,7 +84,8 @@ struct _stp_module *_stp_modules_by_addr[STP_MAX_MODULES]; /* the number of modules in the arrays */ int _stp_num_modules = 0; +static unsigned long _stp_kretprobe_trampoline = 0; unsigned long _stp_module_relocate (const char *module, const char *section, unsigned long offset); static struct _stp_module *_stp_get_unwind_info (unsigned long addr); -#endif /* _STAP_SYMBOLS_H_ */ +#endif /* _STP_SYM_H_ */ diff --git a/runtime/transport/ChangeLog b/runtime/transport/ChangeLog index c3837f86..b3a159e3 100644 --- a/runtime/transport/ChangeLog +++ b/runtime/transport/ChangeLog @@ -1,3 +1,28 @@ +2008-04-09 Martin Hunt <hunt@dragon> + + * symbols.c (_stp_init_kernel_symbols): Print error + messages and exit if symbol lookups fail. + (_stp_init_modules): Lookup modules_op. + +2008-03-31 Martin Hunt <hunt@redhat.com> + + * symbols.c (_stp_init_modules): Use STP_USE_DWARF_UNWINDER. + + * transport.c (_stp_get_root_dir): Remove misleading error message. + +2008-03-30 Martin Hunt <hunt@redhat.com> + + * symbols.c (_stp_init_modules): If using frames, don't + request unwind info. + +2008-03-25 Martin Hunt <hunt@redhat.com> + + * control.c (_stp_ctl_write_dbug): Insert missing break. + + 32-bit systems can't do 64-bit get_user(), so + * symbols.c (_stp_do_unwind_data): Change unwind_len to a u32. + * transport_msgs.h (struct _stp_msg_unwind): Ditto. + 2008-02-27 Martin Hunt <hunt@redhat.com> * symbols.c: Use rwlocks. Use new dbug macros. Handle diff --git a/runtime/transport/control.c b/runtime/transport/control.c index 6a5b272d..92334b9c 100644 --- a/runtime/transport/control.c +++ b/runtime/transport/control.c @@ -14,80 +14,31 @@ static int _stp_current_buffers = STP_DEFAULT_BUFFERS; static _stp_mempool_t *_stp_pool_q; static struct list_head _stp_ctl_ready_q; -static struct list_head _stp_sym_ready_q; DEFINE_SPINLOCK(_stp_ctl_ready_lock); -DEFINE_SPINLOCK(_stp_sym_ready_lock); -static ssize_t _stp_sym_write_cmd(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - static int saved_type = 0; - int type; - - if (count < sizeof(int32_t)) - return 0; - - /* Allow sending of packet type followed by data in the next packet. */ - if (count == sizeof(int32_t)) { - if (get_user(saved_type, (int __user *)buf)) - return -EFAULT; - return count; - } else if (saved_type) { - type = saved_type; - saved_type = 0; - } else { - if (get_user(type, (int __user *)buf)) - return -EFAULT; - count -= sizeof(int); - buf += sizeof(int); - } - -#if DEBUG_TRANSPORT > 0 - if (type < STP_MAX_CMD) - _dbug("Got %s. len=%d\n", _stp_command_name[type], (int)count); -#endif - - switch (type) { - case STP_SYMBOLS: - count = _stp_do_symbols(buf, count); - break; - case STP_MODULE: - if (count > 1) - count = _stp_do_module(buf, count); - else { - /* count == 1 indicates end of initial modules list */ - _stp_ctl_send(STP_TRANSPORT, NULL, 0); - } - break; - case STP_EXIT: - _stp_exit_flag = 1; - break; - default: - errk("invalid symbol command type %d\n", type); - return -EINVAL; - } - - return count; -} static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - int type; + u32 type; static int started = 0; - if (count < sizeof(int)) + if (count < sizeof(u32)) return 0; - if (get_user(type, (int __user *)buf)) + if (get_user(type, (u32 __user *)buf)) return -EFAULT; -#if DEBUG_TRANSPORT > 0 + count -= sizeof(u32); + buf += sizeof(u32); + +#ifdef DEBUG_TRANS if (type < STP_MAX_CMD) _dbug("Got %s. len=%d\n", _stp_command_name[type], (int)count); #endif - count -= sizeof(int); - buf += sizeof(int); - switch (type) { + case STP_UNWIND: + _stp_do_unwind_data(buf, count); + break; case STP_START: if (started == 0) { struct _stp_msg_start st; @@ -110,7 +61,7 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz #endif case STP_READY: /* request symbolic information */ - _stp_ask_for_symbols(); + /* _stp_ask_for_symbols(); */ break; default: @@ -121,8 +72,6 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz return count; } -#define STP_CTL_BUFFER_SIZE 256 - struct _stp_buffer { struct list_head list; int len; @@ -131,9 +80,8 @@ struct _stp_buffer { }; static DECLARE_WAIT_QUEUE_HEAD(_stp_ctl_wq); -static DECLARE_WAIT_QUEUE_HEAD(_stp_sym_wq); -#if DEBUG_TRANSPORT > 0 +#ifdef DEBUG_TRANS static void _stp_ctl_write_dbug(int type, void *data, int len) { char buf[64]; @@ -155,19 +103,9 @@ static void _stp_ctl_write_dbug(int type, void *data, int len) case STP_TRANSPORT: _dbug("sending STP_TRANSPORT\n"); break; - default: - _dbug("ERROR: unknown message type: %d\n", type); - break; - } -} -static void _stp_sym_write_dbug(int type, void *data, int len) -{ - switch (type) { - case STP_SYMBOLS: - _dbug("sending STP_SYMBOLS\n"); - break; - case STP_MODULE: - _dbug("sending STP_MODULE\n"); + case STP_UNWIND: + snprintf(buf, sizeof(buf), "%s", (char *)data); + _dbug("sending STP_UNWIND %s [len=%d]\n", buf, len); break; default: _dbug("ERROR: unknown message type: %d\n", type); @@ -181,7 +119,7 @@ static int _stp_ctl_write(int type, void *data, unsigned len) struct _stp_buffer *bptr; unsigned long flags; -#if DEBUG_TRANSPORT > 0 +#ifdef DEBUG_TRANS _stp_ctl_write_dbug(type, data, len); #endif @@ -206,96 +144,19 @@ static int _stp_ctl_write(int type, void *data, unsigned len) return len; } -static int _stp_sym_write(int type, void *data, unsigned len) -{ - struct _stp_buffer *bptr; - unsigned long flags; - -#if DEBUG_TRANSPORT > 0 - _stp_sym_write_dbug(type, data, len); -#endif - - /* make sure we won't overflow the buffer */ - if (unlikely(len > STP_CTL_BUFFER_SIZE)) - return 0; - - /* get a buffer from the free pool */ - bptr = _stp_mempool_alloc(_stp_pool_q); - if (unlikely(bptr == NULL)) - return -1; - - bptr->type = type; - memcpy(bptr->buf, data, len); - bptr->len = len; - - /* put it on the pool of ready buffers */ - spin_lock_irqsave(&_stp_sym_ready_lock, flags); - list_add_tail(&bptr->list, &_stp_sym_ready_q); - spin_unlock_irqrestore(&_stp_sym_ready_lock, flags); - - /* OK, it's queued. Now signal any waiters. */ - wake_up_interruptible(&_stp_sym_wq); - - return len; -} - /* send commands with timeout and retry */ static int _stp_ctl_send(int type, void *data, int len) { int err, trylimit = 50; - kbug(DEBUG_TRANSPORT, "ctl_send: type=%d len=%d\n", type, len); - if (unlikely(type == STP_SYMBOLS || type == STP_MODULE)) { - while ((err = _stp_sym_write(type, data, len)) < 0 && trylimit--) - msleep(5); - } else { - while ((err = _stp_ctl_write(type, data, len)) < 0 && trylimit--) - msleep(5); - if (err > 0) - wake_up_interruptible(&_stp_ctl_wq); - } - kbug(DEBUG_TRANSPORT, "returning %d\n", err); + dbug_trans(1, "ctl_send: type=%d len=%d\n", type, len); + while ((err = _stp_ctl_write(type, data, len)) < 0 && trylimit--) + msleep(5); + if (err > 0) + wake_up_interruptible(&_stp_ctl_wq); + dbug_trans(1, "returning %d\n", err); return err; } -static ssize_t _stp_sym_read_cmd(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct _stp_buffer *bptr; - int len; - unsigned long flags; - - /* wait for nonempty ready queue */ - spin_lock_irqsave(&_stp_sym_ready_lock, flags); - while (list_empty(&_stp_sym_ready_q)) { - spin_unlock_irqrestore(&_stp_sym_ready_lock, flags); - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - if (wait_event_interruptible(_stp_sym_wq, !list_empty(&_stp_sym_ready_q))) - return -ERESTARTSYS; - spin_lock_irqsave(&_stp_sym_ready_lock, flags); - } - - /* get the next buffer off the ready list */ - bptr = (struct _stp_buffer *)_stp_sym_ready_q.next; - list_del_init(&bptr->list); - spin_unlock_irqrestore(&_stp_sym_ready_lock, flags); - - /* write it out */ - len = bptr->len + 4; - if (len > count || copy_to_user(buf, &bptr->type, len)) { - /* now what? We took it off the queue then failed to send it */ - /* we can't put it back on the queue because it will likely be out-of-order */ - /* fortunately this should never happen */ - /* FIXME need to mark this as a transport failure */ - errk("Supplied buffer too small. count:%d len:%d\n", (int)count, len); - return -EFAULT; - } - - /* put it on the pool of free buffers */ - _stp_mempool_free(bptr); - - return len; -} - static ssize_t _stp_ctl_read_cmd(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct _stp_buffer *bptr; @@ -335,29 +196,10 @@ static ssize_t _stp_ctl_read_cmd(struct file *file, char __user *buf, size_t cou return len; } -static int _stp_sym_opens = 0; -static int _stp_sym_open_cmd(struct inode *inode, struct file *file) -{ - /* only allow one reader */ - if (_stp_sym_opens) - return -1; - - _stp_sym_opens++; - return 0; -} - -static int _stp_sym_close_cmd(struct inode *inode, struct file *file) -{ - if (_stp_sym_opens) - _stp_sym_opens--; - return 0; -} - static int _stp_ctl_open_cmd(struct inode *inode, struct file *file) { if (_stp_attached) return -1; - _stp_attach(); return 0; } @@ -377,16 +219,7 @@ static struct file_operations _stp_ctl_fops_cmd = { .release = _stp_ctl_close_cmd, }; -static struct file_operations _stp_sym_fops_cmd = { - .owner = THIS_MODULE, - .read = _stp_sym_read_cmd, - .write = _stp_sym_write_cmd, - .open = _stp_sym_open_cmd, - .release = _stp_sym_close_cmd, -}; - static struct dentry *_stp_cmd_file = NULL; -static struct dentry *_stp_sym_file = NULL; static int _stp_register_ctl_channel(void) { @@ -400,7 +233,6 @@ static int _stp_register_ctl_channel(void) } INIT_LIST_HEAD(&_stp_ctl_ready_q); - INIT_LIST_HEAD(&_stp_sym_ready_q); /* allocate buffers */ _stp_pool_q = _stp_mempool_init(sizeof(struct _stp_buffer), STP_DEFAULT_BUFFERS); @@ -415,15 +247,9 @@ static int _stp_register_ctl_channel(void) _stp_cmd_file->d_inode->i_uid = _stp_uid; _stp_cmd_file->d_inode->i_gid = _stp_gid; - /* create [debugfs]/systemtap/module_name/.symbols */ - _stp_sym_file = debugfs_create_file(".symbols", 0600, _stp_utt->dir, NULL, &_stp_sym_fops_cmd); - if (_stp_sym_file == NULL) - goto err0; return 0; err0: - if (_stp_cmd_file) - debugfs_remove(_stp_cmd_file); _stp_mempool_destroy(_stp_pool_q); errk("Error creating systemtap debugfs entries.\n"); return -1; @@ -432,16 +258,10 @@ err0: static void _stp_unregister_ctl_channel(void) { struct list_head *p, *tmp; - if (_stp_sym_file) - debugfs_remove(_stp_sym_file); if (_stp_cmd_file) debugfs_remove(_stp_cmd_file); /* Return memory to pool and free it. */ - list_for_each_safe(p, tmp, &_stp_sym_ready_q) { - list_del(p); - _stp_mempool_free(p); - } list_for_each_safe(p, tmp, &_stp_ctl_ready_q) { list_del(p); _stp_mempool_free(p); diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c index 2afea1c9..750e1994 100644 --- a/runtime/transport/procfs.c +++ b/runtime/transport/procfs.c @@ -161,7 +161,7 @@ struct _stp_buffer { struct list_head list; int len; int type; - char buf[STP_BUFFER_SIZE]; + char buf[STP_CTL_BUFFER_SIZE]; }; static DECLARE_WAIT_QUEUE_HEAD(_stp_ctl_wq); diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c index 8c453a55..b0e7c319 100644 --- a/runtime/transport/symbols.c +++ b/runtime/transport/symbols.c @@ -12,8 +12,8 @@ * lib/sort.c of kernel 2.6.22-rc5. It was written by Matt Mackall. */ -#ifndef _SYMBOLS_C_ -#define _SYMBOLS_C_ +#ifndef _STP_SYMBOLS_C_ +#define _STP_SYMBOLS_C_ #include "../sym.h" static char *_stp_symbol_data = NULL; @@ -21,17 +21,12 @@ static int _stp_symbol_state = 0; static char *_stp_module_data = NULL; static int _stp_module_state = 0; - /* these are all the symbol types we are interested in */ static int _stp_sym_type_ok(int type) { - switch (type) { - case 'T': - case 't': + /* we only care about function symbols, which are in the text section */ + if (type == 'T' || type == 't') return 1; - default: - return 0; - } return 0; } @@ -41,10 +36,10 @@ static unsigned _stp_get_sym_sizes(struct module *m, unsigned *dsize) { unsigned int i; unsigned num = 0, datasize = 0; - for (i=0; i < m->num_symtab; i++) { + for (i = 0; i < m->num_symtab; i++) { char *str = (char *)(m->strtab + m->symtab[i].st_name); if (*str != '\0' && _stp_sym_type_ok(m->symtab[i].st_info)) { - datasize += strlen(str)+1; + datasize += strlen(str) + 1; num++; } } @@ -52,19 +47,23 @@ static unsigned _stp_get_sym_sizes(struct module *m, unsigned *dsize) return num; } -/* allocate space for a module and symbols */ -static struct _stp_module * _stp_alloc_module(unsigned num, unsigned datasize, unsigned unwindsize) +/* allocate space for a module, sections, and symbols */ +static struct _stp_module *_stp_alloc_module(unsigned sectsize, unsigned num, unsigned datasize) { struct _stp_module *mod = (struct _stp_module *)_stp_kzalloc(sizeof(struct _stp_module)); if (mod == NULL) goto bad; + mod->sections = (struct _stp_symbol *)_stp_kmalloc(sectsize); + if (mod->sections == NULL) + goto bad; + mod->symbols = (struct _stp_symbol *)_stp_kmalloc(num * sizeof(struct _stp_symbol)); if (mod->symbols == NULL) { mod->symbols = (struct _stp_symbol *)_stp_vmalloc(num * sizeof(struct _stp_symbol)); if (mod->symbols == NULL) goto bad; - mod->allocated = 1; + mod->allocated.symbols = 1; } mod->symbol_data = _stp_kmalloc(datasize); @@ -72,91 +71,63 @@ static struct _stp_module * _stp_alloc_module(unsigned num, unsigned datasize, u mod->symbol_data = _stp_vmalloc(datasize); if (mod->symbol_data == NULL) goto bad; - mod->allocated |= 2; + mod->allocated.symbol_data = 1; } - mod->unwind_data = _stp_kmalloc(unwindsize); - if (mod->unwind_data == NULL) { - mod->unwind_data = _stp_vmalloc(unwindsize); - if (mod->unwind_data == NULL) - goto bad; - mod->allocated |= 4; - } - mod->num_symbols = num; return mod; bad: if (mod) { + if (mod->sections) + _stp_kfree(mod->sections); if (mod->symbols) { - if (mod->allocated & 1) + if (mod->allocated.symbols) _stp_vfree(mod->symbols); else _stp_kfree(mod->symbols); - mod->symbols = NULL; } - if (mod->symbol_data) { - if (mod->allocated & 2) - _stp_vfree(mod->symbol_data); - else - _stp_kfree(mod->symbol_data); - mod->symbol_data = NULL; - } - _stp_kfree(mod); - if (mod->symbols) { - if (mod->allocated & 1) - _stp_vfree(mod->symbols); - else - _stp_kfree(mod->symbols); - mod->symbols = NULL; - } - _stp_kfree(mod); + _stp_kfree(mod); } return NULL; } -static struct _stp_module * _stp_alloc_module_from_module (struct module *m, uint32_t unwind_len) -{ - unsigned datasize, num = _stp_get_sym_sizes(m, &datasize); - return _stp_alloc_module(num, datasize, unwind_len); -} - static void _stp_free_module(struct _stp_module *mod) { /* The module write lock is held. Any prior readers of this */ /* module's data will have read locks and need to finish before */ /* the memory is freed. */ write_lock(&mod->lock); - write_unlock(&mod->lock); /* there will be no more readers */ + write_unlock(&mod->lock); /* there will be no more readers */ - /* free symbol memory */ - if (mod->symbols) { - if (mod->allocated & 1) - _stp_vfree(mod->symbols); - else - _stp_kfree(mod->symbols); - mod->symbols = NULL; - } + /* Free symbol memory */ + /* If symbol_data wasn't allocated, then symbols weren't either. */ if (mod->symbol_data) { - if (mod->allocated & 2) + if (mod->symbols) { + if (mod->allocated.symbols) + _stp_vfree(mod->symbols); + else + _stp_kfree(mod->symbols); + } + if (mod->allocated.symbol_data) _stp_vfree(mod->symbol_data); else _stp_kfree(mod->symbol_data); - mod->symbol_data = NULL; - } if (mod->unwind_data) { - if (mod->allocated & 4) + if (mod->allocated.unwind_data) _stp_vfree(mod->unwind_data); else _stp_kfree(mod->unwind_data); - mod->unwind_data = NULL; - } - if (mod->sections) { - _stp_kfree(mod->sections); - mod->sections = NULL; + if (mod->unwind_hdr) { + if (mod->allocated.unwind_hdr) + _stp_vfree(mod->unwind_hdr); + else + _stp_kfree(mod->unwind_hdr); } + if (mod->sections) + _stp_kfree(mod->sections); /* free module memory */ _stp_kfree(mod); @@ -168,7 +139,7 @@ static void _stp_del_module(struct _stp_module *mod) { int i, num; - // kbug(DEBUG_SYMBOLS, "deleting %s\n", mod->name); + dbug_sym(1, "deleting module %s\n", mod->name); /* signal relocation code to clear its cache */ _stp_module_relocate((char *)-1, NULL, 0); @@ -181,15 +152,15 @@ static void _stp_del_module(struct _stp_module *mod) if (num >= _stp_num_modules) return; - for (i = num; i < _stp_num_modules-1; i++) - _stp_modules[i] = _stp_modules[i+1]; + for (i = num; i < _stp_num_modules - 1; i++) + _stp_modules[i] = _stp_modules[i + 1]; for (num = 0; num < _stp_num_modules; num++) { if (_stp_modules_by_addr[num] == mod) break; } - for (i = num; i < _stp_num_modules-1; i++) - _stp_modules_by_addr[i] = _stp_modules_by_addr[i+1]; + for (i = num; i < _stp_num_modules - 1; i++) + _stp_modules_by_addr[i] = _stp_modules_by_addr[i + 1]; _stp_num_modules--; @@ -197,10 +168,8 @@ static void _stp_del_module(struct _stp_module *mod) } static void _stp_free_modules(void) -{ +{ int i; - unsigned long flags; - /* This only happens when the systemtap module unloads */ /* so there is no need for locks. */ for (i = _stp_num_modules - 1; i >= 0; i--) @@ -208,82 +177,132 @@ static void _stp_free_modules(void) } static unsigned long _stp_kallsyms_lookup_name(const char *name); +static void _stp_create_unwind_hdr(struct _stp_module *m); + +extern unsigned _stp_num_kernel_symbols; +extern struct _stp_symbol _stp_kernel_symbols[]; -/* process the KERNEL symbols */ -static int _stp_do_symbols(const char __user *buf, int count) +/* initialize the kernel symbols */ +static int _stp_init_kernel_symbols(void) { - struct _stp_symbol *s; - unsigned datasize, num, unwindsize; + _stp_modules[0] = (struct _stp_module *)_stp_kzalloc(sizeof(struct _stp_module)); + if (_stp_modules[0] == NULL) { + _dbug("cannot allocate memory\n"); + return -1; + } + _stp_modules[0]->symbols = _stp_kernel_symbols; + _stp_modules[0]->num_symbols = _stp_num_kernel_symbols; + rwlock_init(&_stp_modules[0]->lock); + _stp_num_modules = 1; + + /* Note: this mapping is used by kernel/_stext pseudo-relocations. */ + _stp_modules[0]->text = _stp_kallsyms_lookup_name("_stext"); + if (_stp_modules[0]->text == 0) { + _dbug("Lookup of _stext failed. Exiting.\n"); + return -1; + } + _stp_modules[0]->data = _stp_kallsyms_lookup_name("_etext"); + if (_stp_modules[0]->data == 0) { + _dbug("Lookup of _etext failed. Exiting.\n"); + return -1; + } + _stp_modules[0]->text_size = _stp_modules[0]->data - _stp_modules[0]->text; + _stp_modules_by_addr[0] = _stp_modules[0]; + + _stp_kretprobe_trampoline = _stp_kallsyms_lookup_name("kretprobe_trampoline"); + /* Lookup failure is not fatal */ + + return 0; +} + +static void _stp_do_unwind_data(const char __user *buf, size_t count) +{ + u32 unwind_len; + unsigned long flags; + char name[STP_MODULE_NAME_LEN]; int i; + struct _stp_module *m; + + dbug_unwind(1, "got unwind data, count=%d\n", count); - switch (_stp_symbol_state) { - case 0: - if (count != sizeof(struct _stp_msg_symbol_hdr)) { - errk("count=%d\n", count); - return -EFAULT; - } - if (get_user(num, (unsigned __user *)buf)) - return -EFAULT; - if (get_user(datasize, (unsigned __user *)(buf+4))) - return -EFAULT; - if (get_user(unwindsize, (unsigned __user *)(buf+8))) - return -EFAULT; - dbug(DEBUG_UNWIND, "num=%d datasize=%d unwindsize=%d\n", num, datasize, unwindsize); - - _stp_modules[0] = _stp_alloc_module(num, datasize, unwindsize); - if (_stp_modules[0] == NULL) { - errk("cannot allocate memory\n"); - return -EFAULT; + if (count < STP_MODULE_NAME_LEN + sizeof(unwind_len)) { + dbug_unwind(1, "unwind message too short\n"); + return; + } + if (strncpy_from_user(name, buf, STP_MODULE_NAME_LEN) < 0) { + errk("userspace copy failed\n"); + return; + } + dbug_unwind(1, "name=%s\n", name); + if (!strcmp(name,"*")) { + /* OK, all initial unwind data received. Ready to go. */ + _stp_ctl_send(STP_TRANSPORT, NULL, 0); + return; + } + count -= STP_MODULE_NAME_LEN; + buf += STP_MODULE_NAME_LEN; + + if (get_user(unwind_len, (u32 __user *)buf)) { + errk("userspace copy failed\n"); + return; + } + count -= sizeof(unwind_len); + buf += sizeof(unwind_len); + if (count != unwind_len) { + dbug_unwind(1, "count=%d unwind_len=%d\n", (int)count, (int)unwind_len); + return; + } + + STP_RLOCK_MODULES; + for (i = 0; i < _stp_num_modules; i++) { + if (strcmp(name, _stp_modules[i]->name) == 0) + break; + } + if (unlikely(i == _stp_num_modules)) { + dbug_unwind(1, "module %s not found!\n", name); + STP_RUNLOCK_MODULES; + return; + } + m = _stp_modules[i]; + write_lock(&m->lock); + STP_RUNLOCK_MODULES; + + /* allocate space for unwind data */ + m->unwind_data = _stp_kmalloc(count); + if (unlikely(m->unwind_data == NULL)) { + m->unwind_data = _stp_vmalloc(count); + if (m->unwind_data == NULL) { + errk("kmalloc failed\n"); + goto done; } - rwlock_init(&_stp_modules[0]->lock); - _stp_symbol_state = 1; - break; - case 1: - dbug(DEBUG_SYMBOLS, "got stap_symbols, count=%d\n", count); - if (copy_from_user ((char *)_stp_modules[0]->symbols, buf, count)) - return -EFAULT; - _stp_symbol_state = 2; - break; - case 2: - dbug(DEBUG_SYMBOLS, "got symbol data, count=%d buf=%p\n", count, buf); - if (copy_from_user (_stp_modules[0]->symbol_data, buf, count)) - return -EFAULT; - _stp_num_modules = 1; - - s = _stp_modules[0]->symbols; - for (i = 0; i < _stp_modules[0]->num_symbols; i++) - s[i].symbol += (long)_stp_modules[0]->symbol_data; - - _stp_symbol_state = 3; - /* NB: this mapping is used by kernel/_stext pseudo-relocations. */ - _stp_modules[0]->text = _stp_kallsyms_lookup_name("_stext"); - _stp_modules[0]->data = _stp_kallsyms_lookup_name("_etext"); - _stp_modules[0]->text_size = _stp_modules[0]->data - _stp_modules[0]->text; - _stp_modules_by_addr[0] = _stp_modules[0]; - dbug(DEBUG_SYMBOLS, "Got kernel symbols. text=%p len=%u\n", - (int64_t)_stp_modules[0]->text, _stp_modules[0]->text_size); - break; - case 3: - dbug(DEBUG_UNWIND, "got unwind data, count=%d\n", count); - _stp_symbol_state = 4; - if (copy_from_user (_stp_modules[0]->unwind_data, buf, count)) { - _dbug("cfu failed\n"); - return -EFAULT; + m->allocated.unwind_data = 1; + } + + if (unlikely(copy_from_user(m->unwind_data, buf, count))) { + errk("userspace copy failed\n"); + if (m->unwind_data) { + if (m->allocated.unwind_data) + _stp_vfree(m->unwind_data); + else + _stp_kfree(m->unwind_data); + m->unwind_data = NULL; } - _stp_modules[0]->unwind_data_len = count; - break; - default: - errk("unexpected symbol data of size %d.\n", count); + goto done; } - return count; + m->unwind_data_len = count; + _stp_create_unwind_hdr(m); +done: + write_unlock(&m->lock); } static int _stp_compare_addr(const void *p1, const void *p2) { struct _stp_symbol *s1 = (struct _stp_symbol *)p1; struct _stp_symbol *s2 = (struct _stp_symbol *)p2; - if (s1->addr == s2->addr) return 0; - if (s1->addr < s2->addr) return -1; + if (s1->addr == s2->addr) + return 0; + if (s1->addr < s2->addr) + return -1; return 1; } @@ -332,18 +351,17 @@ static void generic_swap(void *a, void *b, int size) * it less suitable for kernel use. */ void _stp_sort(void *base, size_t num, size_t size, - int (*cmp)(const void *, const void *), - void (*swap)(void *, void *, int size)) + int (*cmp) (const void *, const void *), void (*swap) (void *, void *, int size)) { /* pre-scale counters for performance */ - int i = (num/2 - 1) * size, n = num * size, c, r; + int i = (num / 2 - 1) * size, n = num * size, c, r; if (!swap) swap = (size == 4 ? u32_swap : generic_swap); /* heapify */ - for ( ; i >= 0; i -= size) { - for (r = i; r * 2 + size < n; r = c) { + for (; i >= 0; i -= size) { + for (r = i; r * 2 + size < n; r = c) { c = r * 2 + size; if (c < n - size && cmp(base + c, base + c + size) < 0) c += size; @@ -367,65 +385,114 @@ void _stp_sort(void *base, size_t num, size_t size, } } +/* filter out section names we don't care about */ +static int _stp_section_is_interesting(const char *name) +{ + int ret = 1; + if (!strncmp("__", name, 2) + || !strncmp(".note", name, 5) + || !strncmp(".gnu", name, 4) + || !strncmp(".mod", name, 4)) + ret = 0; + return ret; +} + /* Create a new _stp_module and load the symbols */ -static struct _stp_module *_stp_load_module_symbols (struct _stp_module *imod, uint32_t unwind_len) +static struct _stp_module *_stp_load_module_symbols(struct module *mod) { - unsigned i, num=0; - struct module *m = (struct module *)imod->module; - struct _stp_module *mod = NULL; - char *dataptr; + int i, num, overflow = 0; + struct module_sect_attrs *sa; + unsigned sect_size = 0, sect_num = 0, sym_size, sym_num; + struct _stp_module *sm; + char *dataptr, *endptr; + + sa = mod->sect_attrs; + /* calculate how much space to allocate for section strings */ + for (i = 0; i < sa->nsections; i++) { + if (_stp_section_is_interesting(sa->attrs[i].name)) { + sect_num++; + sect_size += strlen(sa->attrs[i].name) + 1; + dbug_sym(2, "\t%s\t%lx\n", sa->attrs[i].name, sa->attrs[i].address); + } + } + sect_size += sect_num * sizeof(struct _stp_symbol); - if (m == NULL) { - kbug(DEBUG_SYMBOLS, "imod->module is NULL\n"); + /* and how much space for symbols */ + sym_num = _stp_get_sym_sizes(mod, &sym_size); + + sm = _stp_alloc_module(sect_size, sym_num, sym_size); + if (!sm) { + errk("failed to allocate memory for module.\n"); return NULL; } - if (try_module_get(m)) { - mod = _stp_alloc_module_from_module(m, unwind_len); - if (mod == NULL) { - module_put(m); - errk("failed to allocate memory for module.\n"); - return NULL; - } + strlcpy(sm->name, mod->name, STP_MODULE_NAME_LEN); + sm->module = (unsigned long)mod; + sm->text = (unsigned long)mod->module_core; + sm->text_size = mod->core_text_size; + sm->data = 0; /* fixme */ + sm->num_sections = sect_num; + rwlock_init(&sm->lock); - strlcpy(mod->name, imod->name, STP_MODULE_NAME_LEN); - mod->module = imod->module; - mod->text = imod->text; - mod->data = imod->data; - mod->num_sections = imod->num_sections; - mod->sections = imod->sections; - mod->text_size = m->core_text_size; - rwlock_init(&mod->lock); - - /* now copy all the symbols we are interested in */ - dataptr = mod->symbol_data; - for (i=0; i < m->num_symtab; i++) { - char *str = (char *)(m->strtab + m->symtab[i].st_name); - if (*str != '\0' && _stp_sym_type_ok(m->symtab[i].st_info)) { - mod->symbols[num].symbol = dataptr; - mod->symbols[num].addr = m->symtab[i].st_value; - while (*str) *dataptr++ = *str++; - *dataptr++ = 0; - num++; + /* copy in section data */ + dataptr = (char *)((long)sm->sections + sect_num * sizeof(struct _stp_symbol)); + endptr = (char *)((long)sm->sections + sect_size); + num = 0; + for (i = 0; i < sa->nsections; i++) { + size_t len, maxlen; + if (_stp_section_is_interesting(sa->attrs[i].name)) { + sm->sections[num].addr = sa->attrs[i].address; + sm->sections[num].symbol = dataptr; + maxlen = (size_t) (endptr - dataptr); + len = strlcpy(dataptr, sa->attrs[i].name, maxlen); + if (unlikely(len >= maxlen)) { + _dbug("dataptr=%lx endptr=%lx len=%d maxlen=%d\n", dataptr, endptr, len, maxlen); + overflow = 1; } + dataptr += len + 1; + num++; } - module_put(m); + } + if (unlikely(overflow)) { + errk("Section names truncated!!! Should never happen!!\n"); + *endptr = 0; + overflow = 0; + } - /* sort symbols by address */ - _stp_sort (mod->symbols, num, sizeof(struct _stp_symbol), _stp_compare_addr, _stp_swap_symbol); + /* now copy all the symbols we are interested in */ + dataptr = sm->symbol_data; + endptr = dataptr + sym_size - 1; + num = 0; + for (i = 0; i < mod->num_symtab; i++) { + char *str = (char *)(mod->strtab + mod->symtab[i].st_name); + if (*str != '\0' && _stp_sym_type_ok(mod->symtab[i].st_info)) { + sm->symbols[num].symbol = dataptr; + sm->symbols[num].addr = mod->symtab[i].st_value; + while (*str && (dataptr < endptr)) + *dataptr++ = *str++; + if (unlikely(*str)) + overflow = 1; + *dataptr++ = 0; + num++; + } } - return mod; + if (unlikely(overflow)) + errk("Symbol names truncated!!! Should never happen!!\n"); + + /* sort symbols by address */ + _stp_sort(sm->symbols, num, sizeof(struct _stp_symbol), _stp_compare_addr, _stp_swap_symbol); + + return sm; } -/* Remove any old module info from our database */ -static void _stp_module_exists_delete (struct _stp_module *mod) +/* Remove any old module info from our database. */ +static void _stp_module_exists_delete(struct _stp_module *mod) { int i, num; - /* remove any old modules with the same name */ for (num = 1; num < _stp_num_modules; num++) { if (strcmp(_stp_modules[num]->name, mod->name) == 0) { - dbug(DEBUG_SYMBOLS, "found existing module with name %s. Deleting.\n", mod->name); + dbug_sym(1, "found existing module with name %s. Deleting.\n", mod->name); _stp_del_module(_stp_modules[num]); break; } @@ -435,143 +502,61 @@ static void _stp_module_exists_delete (struct _stp_module *mod) for (num = 1; num < _stp_num_modules; num++) { if (mod->text + mod->text_size < _stp_modules_by_addr[num]->text) continue; - if (mod->text < _stp_modules_by_addr[num]->text - + _stp_modules_by_addr[num]->text_size) { - dbug(DEBUG_SYMBOLS, "New module %s overlaps with old module %s. Deleting old.\n", - mod->name, _stp_modules_by_addr[num]->name); + if (mod->text < _stp_modules_by_addr[num]->text + _stp_modules_by_addr[num]->text_size) { + dbug_sym(1, "New module %s overlaps with old module %s. Deleting old.\n", + mod->name, _stp_modules_by_addr[num]->name); _stp_del_module(_stp_modules_by_addr[num]); } } } -static int _stp_ins_module(struct _stp_module *mod) +static void _stp_ins_module(struct module *mod) { - int i, num, res, ret = 0; + int i, num, res; unsigned long flags; - - // kbug(DEBUG_SYMBOLS, "insert %s\n", mod->name); + struct _stp_module *m; + dbug_sym(1, "insert %s\n", mod->name); + m = _stp_load_module_symbols(mod); + if (m == NULL) + return; STP_WLOCK_MODULES; - - _stp_module_exists_delete(mod); - + _stp_module_exists_delete(m); /* check for overflow */ if (_stp_num_modules == STP_MAX_MODULES) { errk("Exceeded the limit of %d modules\n", STP_MAX_MODULES); - ret = -ENOMEM; goto done; } - + /* insert alphabetically in _stp_modules[] */ for (num = 1; num < _stp_num_modules; num++) - if (strcmp(_stp_modules[num]->name, mod->name) > 0) + if (strcmp(_stp_modules[num]->name, m->name) > 0) break; for (i = _stp_num_modules; i > num; i--) - _stp_modules[i] = _stp_modules[i-1]; - _stp_modules[num] = mod; - + _stp_modules[i] = _stp_modules[i - 1]; + _stp_modules[num] = m; /* insert by text address in _stp_modules_by_addr[] */ for (num = 1; num < _stp_num_modules; num++) - if (mod->text < _stp_modules_by_addr[num]->text) + if (m->text < _stp_modules_by_addr[num]->text) break; for (i = _stp_num_modules; i > num; i--) - _stp_modules_by_addr[i] = _stp_modules_by_addr[i-1]; - _stp_modules_by_addr[num] = mod; - + _stp_modules_by_addr[i] = _stp_modules_by_addr[i - 1]; + _stp_modules_by_addr[num] = m; _stp_num_modules++; - done: STP_WUNLOCK_MODULES; - return ret; -} - - -/* Called from procfs.c when a STP_MODULE msg is received */ -static int _stp_do_module(const char __user *buf, int count) -{ - struct _stp_msg_module tmpmod; - struct _stp_module mod, *m; - unsigned i, section_len; - - if (count < (int)sizeof(tmpmod)) { - errk("expected %d and got %d\n", (int)sizeof(tmpmod), count); - return -EFAULT; - } - if (copy_from_user ((char *)&tmpmod, buf, sizeof(tmpmod))) - return -EFAULT; - - section_len = count - sizeof(tmpmod) - tmpmod.unwind_len; - if (section_len <= 0) { - errk("section_len = %d\n", section_len); - return -EFAULT; - } - dbug(DEBUG_SYMBOLS, "Got module %s, count=%d section_len=%d unwind_len=%d\n", - tmpmod.name, count, section_len, tmpmod.unwind_len); - - strcpy(mod.name, tmpmod.name); - mod.module = tmpmod.module; - mod.text = tmpmod.text; - mod.data = tmpmod.data; - mod.num_sections = tmpmod.num_sections; - - /* copy in section data */ - mod.sections = _stp_kmalloc(section_len); - if (mod.sections == NULL) { - errk("unable to allocate memory.\n"); - return -EFAULT; - } - if (copy_from_user ((char *)mod.sections, buf+sizeof(tmpmod), section_len)) { - _stp_kfree(mod.sections); - return -EFAULT; - } - for (i = 0; i < mod.num_sections; i++) { - mod.sections[i].symbol = - (char *)((long)mod.sections[i].symbol - + (long)((long)mod.sections + mod.num_sections * sizeof(struct _stp_symbol))); - } - - #if 0 - for (i = 0; i < mod.num_sections; i++) - _dbug("section %d (stored at %p): %s %lx\n", i, &mod.sections[i], mod.sections[i].symbol, mod.sections[i].addr); - #endif - - /* load symbols from tmpmod.module to mod */ - m = _stp_load_module_symbols(&mod, tmpmod.unwind_len); - if (m == NULL) { - _stp_kfree(mod.sections); - return 0; - } - - dbug(DEBUG_SYMBOLS, "module %s loaded. Text=%p text_size=%u\n", m->name, (int64_t)m->text, m->text_size); - /* finally copy unwind info */ - if (copy_from_user (m->unwind_data, buf+sizeof(tmpmod)+section_len, tmpmod.unwind_len)) { - _stp_free_module(m); - _stp_kfree(mod.sections); - return -EFAULT; - } - m->unwind_data_len = tmpmod.unwind_len; - - if (_stp_ins_module(m) < 0) { - _stp_free_module(m); - return -ENOMEM; - } - - return count; + return; } -static int _stp_ctl_send (int type, void *data, int len); - -static int _stp_module_load_notify(struct notifier_block * self, unsigned long val, void * data) +static int _stp_module_load_notify(struct notifier_block *self, unsigned long val, void *data) { struct module *mod = (struct module *)data; struct _stp_module rmod; - switch (val) { case MODULE_STATE_COMING: - dbug(DEBUG_SYMBOLS, "module %s load notify\n", mod->name); - strlcpy(rmod.name, mod->name, STP_MODULE_NAME_LEN); - _stp_ctl_send(STP_MODULE, &rmod, sizeof(struct _stp_module)); + dbug_sym(1, "module %s load notify\n", mod->name); + _stp_ins_module(mod); break; default: errk("module loaded? val=%ld\n", val); @@ -583,4 +568,72 @@ static struct notifier_block _stp_module_load_nb = { .notifier_call = _stp_module_load_notify, }; -#endif /* _SYMBOLS_C_ */ +#include <linux/seq_file.h> + +static int _stp_init_modules(void) +{ + loff_t pos = 0; + void *res; + struct module *mod; + const struct seq_operations *modules_op = (const struct seq_operations *)_stp_kallsyms_lookup_name("modules_op"); + + if (modules_op == NULL) { + _dbug("Lookup of modules_op failed.\n"); + return -1; + } + + /* Use the seq_file interface to safely get a list of installed modules */ + res = modules_op->start(NULL, &pos); + while (res) { + mod = list_entry(res, struct module, list); + _stp_ins_module(mod); + res = modules_op->next(NULL, res, &pos); + } + + if (register_module_notifier(&_stp_module_load_nb)) + errk("failed to load module notifier\n"); + + /* unlocks the list */ + modules_op->stop(NULL, NULL); + +#ifdef STP_USE_DWARF_UNWINDER + /* now that we have all the modules, ask for their unwind info */ + { + unsigned long flags; + int i, left = STP_CTL_BUFFER_SIZE; + char buf[STP_CTL_BUFFER_SIZE]; + char *ptr = buf; + *ptr = 0; + + STP_RLOCK_MODULES; + /* Loop through modules, sending module names packed into */ + /* messages of size STP_CTL_BUFFER. */ + for (i = 0; i < _stp_num_modules; i++) { + char *name = _stp_modules[i]->name; + int len = strlen(name); + if (len >= left) { + _stp_ctl_send(STP_UNWIND, buf, sizeof(buf) - left); + ptr = buf; + left = STP_CTL_BUFFER_SIZE; + } + strlcpy(ptr, name, left); + ptr += len + 1; + left -= len + 1; + } + STP_RUNLOCK_MODULES; + + /* Send terminator. When we get this back from stapio */ + /* that means all the unwind info has been sent. */ + strlcpy(ptr, "*", left); + left -= 2; + _stp_ctl_send(STP_UNWIND, buf, sizeof(buf) - left); + } +#else + /* done with modules, now go */ + _stp_ctl_send(STP_TRANSPORT, NULL, 0); +#endif /* STP_USE_DWARF_UNWINDER */ + + return 0; +} + +#endif /* _STP_SYMBOLS_C_ */ diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index 8335e44b..7ff9c8c0 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -23,83 +23,47 @@ #include "../procfs.c" static struct utt_trace *_stp_utt = NULL; - +static unsigned int utt_seq = 1; +static int _stp_probes_started = 0; +pid_t _stp_target = 0; +static int _stp_exit_called = 0; +int _stp_exit_flag = 0; #ifdef STP_OLD_TRANSPORT #include "relayfs.c" +#include "procfs.c" #else #include "utt.c" +#include "control.c" #endif -static unsigned int utt_seq = 1; - -static int _stp_probes_started = 0; - /* module parameters */ static int _stp_bufsize; module_param(_stp_bufsize, int, 0); MODULE_PARM_DESC(_stp_bufsize, "buffer size"); -pid_t _stp_target = 0; -static int _stp_exit_called = 0; -int _stp_exit_flag = 0; - /* forward declarations */ void probe_exit(void); int probe_start(void); void _stp_exit(void); -void _stp_handle_start (struct _stp_msg_start *st); -static void _stp_detach(void); -static void _stp_attach(void); /* check for new workqueue API */ -#ifdef DECLARE_DELAYED_WORK -static void _stp_work_queue (struct work_struct *data); +#ifdef DECLARE_DELAYED_WORK +static void _stp_work_queue(struct work_struct *data); static DECLARE_DELAYED_WORK(_stp_work, _stp_work_queue); #else -static void _stp_work_queue (void *data); +static void _stp_work_queue(void *data); static DECLARE_WORK(_stp_work, _stp_work_queue, NULL); #endif static struct workqueue_struct *_stp_wq; -static void _stp_ask_for_symbols(void); - -#ifdef STP_OLD_TRANSPORT -#include "procfs.c" -#else -#include "control.c" -#endif - -static void _stp_ask_for_symbols(void) -{ - struct _stp_msg_symbol req; - struct _stp_module mod; - static int sent_symbols = 0; - - if (sent_symbols == 0) { - /* ask for symbols and modules */ - kbug(DEBUG_SYMBOLS|DEBUG_TRANSPORT, "AFS\n"); - - req.endian = 0x1234; - req.ptr_size = sizeof(char *); - _stp_ctl_send(STP_SYMBOLS, &req, sizeof(req)); - - strcpy(mod.name, ""); - _stp_ctl_send(STP_MODULE, &mod, sizeof(mod)); - sent_symbols = 1; - } -} /* * _stp_handle_start - handle STP_START */ -void _stp_handle_start (struct _stp_msg_start *st) +void _stp_handle_start(struct _stp_msg_start *st) { - kbug (DEBUG_TRANSPORT, "stp_handle_start\n"); - - if (register_module_notifier(&_stp_module_load_nb)) - errk("failed to load module notifier\n"); - + dbug_trans(1, "stp_handle_start\n"); _stp_target = st->target; st->res = probe_start(); if (st->res >= 0) @@ -108,16 +72,15 @@ void _stp_handle_start (struct _stp_msg_start *st) _stp_ctl_send(STP_START, st, sizeof(*st)); } - /* common cleanup code. */ /* This is called from the kernel thread when an exit was requested */ /* by staprun or the exit() function. It is also called by transport_close() */ /* when the module is removed. In that case "dont_rmmod" is set to 1. */ /* We need to call it both times because we want to clean up properly */ /* when someone does /sbin/rmmod on a loaded systemtap module. */ -static void _stp_cleanup_and_exit (int dont_rmmod) +static void _stp_cleanup_and_exit(int dont_rmmod) { - kbug(DEBUG_TRANSPORT, "cleanup_and_exit (%d)\n", dont_rmmod); + dbug_trans(1, "cleanup_and_exit (%d)\n", dont_rmmod); if (!_stp_exit_called) { int failures; @@ -128,23 +91,24 @@ static void _stp_cleanup_and_exit (int dont_rmmod) _stp_exit_called = 1; if (_stp_probes_started) { - kbug(DEBUG_TRANSPORT, "calling probe_exit\n"); + dbug_trans(1, "calling probe_exit\n"); /* tell the stap-generated code to unload its probes, etc */ probe_exit(); - kbug(DEBUG_TRANSPORT, "done with probe_exit\n"); + dbug_trans(1, "done with probe_exit\n"); } failures = atomic_read(&_stp_transport_failures); if (failures) - _stp_warn ("There were %d transport failures.\n", failures); + _stp_warn("There were %d transport failures.\n", failures); - kbug(DEBUG_TRANSPORT, "************** calling startstop 0 *************\n"); - if (_stp_utt) utt_trace_startstop(_stp_utt, 0, &utt_seq); + dbug_trans(1, "************** calling startstop 0 *************\n"); + if (_stp_utt) + utt_trace_startstop(_stp_utt, 0, &utt_seq); - kbug(DEBUG_TRANSPORT, "ctl_send STP_EXIT\n"); + dbug_trans(1, "ctl_send STP_EXIT\n"); /* tell staprun to exit (if it is still there) */ _stp_ctl_send(STP_EXIT, &dont_rmmod, sizeof(int)); - kbug(DEBUG_TRANSPORT, "done with ctl_send STP_EXIT\n"); + dbug_trans(1, "done with ctl_send STP_EXIT\n"); } } @@ -153,7 +117,7 @@ static void _stp_cleanup_and_exit (int dont_rmmod) */ static void _stp_detach(void) { - kbug(DEBUG_TRANSPORT, "detach\n"); + dbug_trans(1, "detach\n"); _stp_attached = 0; _stp_pid = 0; @@ -169,10 +133,10 @@ static void _stp_detach(void) */ static void _stp_attach(void) { - kbug(DEBUG_TRANSPORT, "attach\n"); + dbug_trans(1, "attach\n"); _stp_attached = 1; _stp_pid = current->pid; - utt_set_overwrite(0); + utt_set_overwrite(0); queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER); } @@ -180,10 +144,10 @@ static void _stp_attach(void) * _stp_work_queue - periodically check for IO or exit * This is run by a kernel thread and may sleep. */ -#ifdef DECLARE_DELAYED_WORK -static void _stp_work_queue (struct work_struct *data) +#ifdef DECLARE_DELAYED_WORK +static void _stp_work_queue(struct work_struct *data) #else -static void _stp_work_queue (void *data) +static void _stp_work_queue(void *data) #endif { int do_io = 0; @@ -211,19 +175,19 @@ static void _stp_work_queue (void *data) */ void _stp_transport_close() { - kbug(DEBUG_TRANSPORT, "%d: ************** transport_close *************\n", current->pid); + dbug_trans(1, "%d: ************** transport_close *************\n", current->pid); _stp_cleanup_and_exit(1); destroy_workqueue(_stp_wq); _stp_unregister_ctl_channel(); - if (_stp_utt) utt_trace_remove(_stp_utt); + if (_stp_utt) + utt_trace_remove(_stp_utt); _stp_free_modules(); _stp_kill_time(); - _stp_print_cleanup(); /* free print buffers */ + _stp_print_cleanup(); /* free print buffers */ _stp_mem_debug_done(); - kbug(DEBUG_TRANSPORT, "---- CLOSED ----\n"); + dbug_trans(1, "---- CLOSED ----\n"); } - static struct utt_trace *_stp_utt_open(void) { struct utt_trace_setup utts; @@ -249,22 +213,22 @@ int _stp_transport_init(void) { int ret; - kbug(DEBUG_TRANSPORT, "transport_init\n"); + dbug_trans(1, "transport_init\n"); _stp_init_pid = current->pid; _stp_uid = current->uid; _stp_gid = current->gid; #ifdef RELAY_GUEST - /* Guest scripts use relay only for reporting warnings and errors */ - _stp_subbuf_size = 65536; - _stp_nsubbufs = 2; + /* Guest scripts use relay only for reporting warnings and errors */ + _stp_subbuf_size = 65536; + _stp_nsubbufs = 2; #endif if (_stp_bufsize) { unsigned size = _stp_bufsize * 1024 * 1024; _stp_subbuf_size = ((size >> 2) + 1) * 65536; _stp_nsubbufs = size / _stp_subbuf_size; - kbug(DEBUG_TRANSPORT, "Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size); + dbug_trans(1, "Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size); } /* initialize timer code */ @@ -286,41 +250,57 @@ int _stp_transport_init(void) if (_stp_print_init() < 0) goto err2; + /* start transport */ utt_trace_startstop(_stp_utt, 1, &utt_seq); /* create workqueue of kernel threads */ _stp_wq = create_workqueue("systemtap"); if (!_stp_wq) goto err3; + + _stp_transport_state = 1; + + dbug_trans(1, "calling init_kernel_symbols\n"); + if (_stp_init_kernel_symbols() < 0) + goto err4; + + dbug_trans(1, "calling init_modules\n"); + if (_stp_init_modules() < 0) + goto err4; + return 0; +err4: + errk("failed to initialize modules\n"); + _stp_free_modules(); + destroy_workqueue(_stp_wq); err3: _stp_print_cleanup(); err2: _stp_unregister_ctl_channel(); err1: - if (_stp_utt) utt_trace_remove(_stp_utt); + if (_stp_utt) + utt_trace_remove(_stp_utt); err0: _stp_kill_time(); return -1; } - static inline void _stp_lock_inode(struct inode *inode) { #ifdef DEFINE_MUTEX - mutex_lock(&inode->i_mutex); + mutex_lock(&inode->i_mutex); #else - down(&inode->i_sem); + down(&inode->i_sem); #endif } static inline void _stp_unlock_inode(struct inode *inode) { #ifdef DEFINE_MUTEX - mutex_unlock(&inode->i_mutex); + mutex_unlock(&inode->i_mutex); #else - up(&inode->i_sem); + up(&inode->i_sem); #endif } @@ -358,7 +338,8 @@ static void _stp_unlock_debugfs(void) /* utt.c and relayfs.c. Will not be necessary if utt is included */ /* in the kernel. */ -static struct dentry *_stp_get_root_dir(const char *name) { +static struct dentry *_stp_get_root_dir(const char *name) +{ struct file_system_type *fs; struct dentry *root; struct super_block *sb; @@ -377,7 +358,6 @@ static struct dentry *_stp_get_root_dir(const char *name) { errk("Couldn't lock transport directory.\n"); return NULL; } - #ifdef STP_OLD_TRANSPORT root = relayfs_create_dir(name, NULL); #else @@ -389,12 +369,11 @@ static struct dentry *_stp_get_root_dir(const char *name) { _stp_lock_inode(sb->s_root->d_inode); root = lookup_one_len(name, sb->s_root, strlen(name)); _stp_unlock_inode(sb->s_root->d_inode); - kbug(DEBUG_TRANSPORT, "root=%p\n", root); if (!IS_ERR(root)) dput(root); else { root = NULL; - kbug(DEBUG_TRANSPORT, "Could not create or find transport directory.\n"); + errk("Could not create or find transport directory.\n"); } } _stp_unlock_debugfs(); diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h index 6dc00d2b..dc499961 100644 --- a/runtime/transport/transport.h +++ b/runtime/transport/transport.h @@ -7,21 +7,37 @@ #include "transport_msgs.h" -void _stp_warn (const char *fmt, ...); - +/* The size of print buffers. This limits the maximum */ +/* amount of data a print can send. */ #define STP_BUFFER_SIZE 8192 +/* STP_CTL_BUFFER_SIZE is the maximum size of a message */ +/* exchanged on the control channel. */ +#ifdef STP_OLD_TRANSPORT +/* Old transport sends print output on control channel */ +#define STP_CTL_BUFFER_SIZE STP_BUFFER_SIZE +#else +#define STP_CTL_BUFFER_SIZE 256 +#endif + /* how often the work queue wakes up and checks buffers */ #define STP_WORK_TIMER (HZ/100) static unsigned _stp_nsubbufs = 8; static unsigned _stp_subbuf_size = 65536*4; + +void _stp_warn (const char *fmt, ...); extern void _stp_transport_close(void); extern int _stp_print_init(void); extern void _stp_print_cleanup(void); static struct dentry *_stp_get_root_dir(const char *name); static int _stp_lock_debugfs(void); static void _stp_unlock_debugfs(void); +static int _stp_ctl_send(int type, void *data, int len); +static void _stp_attach(void); +static void _stp_detach(void); +void _stp_handle_start(struct _stp_msg_start *st); + int _stp_pid = 0; uid_t _stp_uid = 0; gid_t _stp_gid = 0; diff --git a/runtime/transport/transport_msgs.h b/runtime/transport/transport_msgs.h index 55de2d4a..5f385565 100644 --- a/runtime/transport/transport_msgs.h +++ b/runtime/transport/transport_msgs.h @@ -23,8 +23,7 @@ enum STP_EXIT, STP_OOB_DATA, STP_SYSTEM, - STP_SYMBOLS, - STP_MODULE, + STP_UNWIND, STP_TRANSPORT, STP_CONNECT, STP_DISCONNECT, @@ -36,18 +35,16 @@ enum STP_SUBBUFS_CONSUMED, STP_REALTIME_DATA, #endif - STP_MAX_CMD }; -#ifdef DEBUG_TRANSPORT +#ifdef DEBUG_TRANS static const char *_stp_command_name[] = { "STP_START", "STP_EXIT", "STP_OOB_DATA", "STP_SYSTEM", - "STP_SYMBOLS", - "STP_MODULE", + "STP_UNWIND", "STP_TRANSPORT", "STP_CONNECT", "STP_DISCONNECT", @@ -59,68 +56,34 @@ static const char *_stp_command_name[] = { "STP_REALTIME_DATA", #endif }; -#endif /* DEBUG_TRANSPORT */ +#endif /* DEBUG_TRANS */ /* control channel messages */ -/* command to execute: sent to staprun */ +/* command to execute: module->stapio */ struct _stp_msg_cmd { char cmd[128]; }; -/* request for symbol data. sent to staprun */ -struct _stp_msg_symbol +/* Unwind data. stapio->module */ +struct _stp_msg_unwind { - int32_t endian; - int32_t ptr_size; + /* the module name, or "*" for all */ + char name[STP_MODULE_NAME_LEN]; + /* length of unwind data */ + uint32_t unwind_len; + /* data ...*/ }; /* Request to start probes. */ -/* Sent from staprun. Then returned from module. */ +/* stapio->module->stapio */ struct _stp_msg_start { pid_t target; int32_t res; // for reply: result of probe_start() }; -struct _stp_symbol32 -{ - uint32_t addr; - uint32_t symbol; -}; - -struct _stp_symbol64 -{ - uint64_t addr; - uint64_t symbol; -}; - -struct _stp_msg_symbol_hdr -{ - uint32_t num_syms; - uint32_t sym_size; - uint32_t unwind_size; -}; - -struct _stp_msg_module { - /* the module name, or "" for kernel */ - char name[STP_MODULE_NAME_LEN]; - - /* A pointer to the struct module */ - uint64_t module; - - /* the start of the module's text and data sections */ - uint64_t text; - uint64_t data; - - /* how many sections this module has */ - uint32_t num_sections; - - /* length of unwind data */ - uint32_t unwind_len; -}; - #ifdef STP_OLD_TRANSPORT /**** for compatibility with old relayfs ****/ struct _stp_buf_info diff --git a/runtime/unwind.c b/runtime/unwind.c new file mode 100644 index 00000000..f0010372 --- /dev/null +++ b/runtime/unwind.c @@ -0,0 +1,959 @@ +/* -*- linux-c -*- + * kernel stack unwinding + * Copyright (C) 2008 Red Hat Inc. + * + * Based on old kernel code that is + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich <jbeulich@novell.com> + * + * This code is released under version 2 of the GNU GPL. + * + * This code currently does stack unwinding in the + * kernel and modules. It will need some extension to handle + * userspace unwinding. + */ + +#include "unwind/unwind.h" + +struct eh_frame_hdr_table_entry { + unsigned long start, fde; +}; + +static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2) +{ + const struct eh_frame_hdr_table_entry *e1 = p1; + const struct eh_frame_hdr_table_entry *e2 = p2; + return (e1->start > e2->start) - (e1->start < e2->start); +} + +static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size) +{ + struct eh_frame_hdr_table_entry *e1 = p1; + struct eh_frame_hdr_table_entry *e2 = p2; + unsigned long v; + + v = e1->start; + e1->start = e2->start; + e2->start = v; + v = e1->fde; + e1->fde = e2->fde; + e2->fde = v; +} + +/* Build a binary-searchable unwind header. Also do some + * validity checks. In the future we might use */ +/* .eh_frame_hdr if it is already present. */ +static void _stp_create_unwind_hdr(struct _stp_module *m) +{ + const u8 *ptr; + unsigned long tableSize, hdrSize, last; + unsigned n = 0; + const u32 *fde; + int bad_order = 0; + struct { + u8 version; + u8 eh_frame_ptr_enc; + u8 fde_count_enc; + u8 table_enc; + unsigned long eh_frame_ptr; + unsigned int fde_count; + struct eh_frame_hdr_table_entry table[]; + } __attribute__ ((__packed__)) * header = NULL; + + /* already did this or no data? */ + if (m->unwind_hdr || m->unwind_data_len == 0) + return; + + tableSize = m->unwind_data_len; + if (tableSize & (sizeof(*fde) - 1)) { + dbug_unwind(1, "tableSize=0x%x not a multiple of 0x%x\n", (int)tableSize, (int)sizeof(*fde)); + goto bad; + } + + /* count the FDEs */ + for (fde = m->unwind_data; + tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; + tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { + signed ptrType; + const u32 *cie; + + /* check for extended length */ + if ((*fde & 0xfffffff0) == 0xfffffff0) { + dbug_unwind(1, "Module %s has extended-length CIE or FDE."); + dbug_unwind(1, "This is not supported at this time."); + goto bad; + } + cie = cie_for_fde(fde, m); + if (cie == ¬_fde) + continue; /* fde was a CIE. That's OK, just skip it. */ + if (cie == NULL || cie == &bad_cie || (ptrType = fde_pointer_type(cie)) < 0) + goto bad; + /* we have a real FDE */ + ptr = (const u8 *)(fde + 2); + if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType)) + goto bad; + ++n; + } + + if (tableSize || !n) { + dbug_unwind(1, "%s: tableSize=%ld, n=%d\n", m->name, tableSize, n); + goto bad; + } + + hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) + 2 * n * sizeof(unsigned long); + header = _stp_kmalloc(hdrSize); + if (header == NULL) { + header = _stp_vmalloc(hdrSize); + if (header == NULL) + return; + m->allocated.unwind_hdr = 1; + } + + header->version = 1; + header->eh_frame_ptr_enc = DW_EH_PE_absptr; + header->fde_count_enc = DW_EH_PE_data4; + header->table_enc = DW_EH_PE_absptr; + _stp_put_unaligned((unsigned long)m->unwind_data, &header->eh_frame_ptr); + + BUILD_BUG_ON(offsetof(typeof(*header), fde_count) + % __alignof(typeof(header->fde_count))); + header->fde_count = n; + + BUILD_BUG_ON(offsetof(typeof(*header), table) % __alignof(typeof(*header->table))); + + n = 0; + last = 0; + tableSize = m->unwind_data_len; + for (fde = m->unwind_data; tableSize; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { + const u32 *cie = cie_for_fde(fde, m); + if (cie == ¬_fde) + continue; + if (cie == NULL || cie == &bad_cie) + goto bad; + /* we have a real FDE */ + ptr = (const u8 *)(fde + 2); + header->table[n].start = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, fde_pointer_type(cie)); + header->table[n].fde = (unsigned long)fde; + if (header->table[n].start < last) + bad_order++; + last = header->table[n].start; + ++n; + } + WARN_ON(n != header->fde_count); + + /* Is sort ever necessary? */ + if (bad_order) + _stp_sort(header->table, n, sizeof(*header->table), cmp_eh_frame_hdr_table_entries, + swap_eh_frame_hdr_table_entries); + + m->unwind_hdr_len = hdrSize; + m->unwind_hdr = header; + return; + + /* unwind data is not acceptable. free it and return */ +bad: + dbug_unwind(1, "unwind data for %s is unacceptable. Freeing.", m->name); + if (header) { + if (m->allocated.unwind_hdr) { + m->allocated.unwind_hdr = 0; + _stp_vfree(header); + } else + _stp_kfree(header); + } + if (m->unwind_data) { + if (m->allocated.unwind_data) + _stp_vfree(m->unwind_data); + else + _stp_kfree(m->unwind_data); + m->unwind_data = NULL; + m->unwind_data_len = 0; + } + return; +} + +static uleb128_t get_uleb128(const u8 **pcur, const u8 *end) +{ + const u8 *cur = *pcur; + uleb128_t value = 0; + unsigned shift; + + for (shift = 0; cur < end; shift += 7) { + if (shift + 7 > 8 * sizeof(value) + && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { + cur = end + 1; + break; + } + value |= (uleb128_t)(*cur & 0x7f) << shift; + if (!(*cur++ & 0x80)) + break; + } + *pcur = cur; + + return value; +} + +static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) +{ + const u8 *cur = *pcur; + sleb128_t value = 0; + unsigned shift; + + for (shift = 0; cur < end; shift += 7) { + if (shift + 7 > 8 * sizeof(value) + && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { + cur = end + 1; + break; + } + value |= (sleb128_t)(*cur & 0x7f) << shift; + if (!(*cur & 0x80)) { + value |= -(*cur++ & 0x40) << shift; + break; + } + } + *pcur = cur; + + return value; +} + +/* given an FDE, find its CIE */ +static const u32 *cie_for_fde(const u32 *fde, const struct _stp_module *m) +{ + const u32 *cie; + + /* check that length is proper */ + if (!*fde || (*fde & (sizeof(*fde) - 1))) + return &bad_cie; + + /* CIE id for eh_frame is 0, otherwise 0xffffffff */ + if (m->unwind_is_ehframe && fde[1] == 0) + return ¬_fde; + else if (fde[1] == 0xffffffff) + return ¬_fde; + + /* OK, must be an FDE. Now find its CIE. */ + + /* CIE_pointer must be a proper offset */ + if ((fde[1] & (sizeof(*fde) - 1)) || fde[1] > (unsigned long)(fde + 1) - (unsigned long)m->unwind_data) { + dbug_unwind(1, "fde[1]=%lx fde+1=%lx, unwind_data=%lx %lx\n", + (unsigned long)fde[1], (unsigned long)(fde + 1), + (unsigned long)m->unwind_data, (unsigned long)(fde + 1) - (unsigned long)m->unwind_data); + return NULL; /* this is not a valid FDE */ + } + + /* cie pointer field is different in eh_frame vs debug_frame */ + if (m->unwind_is_ehframe) + cie = fde + 1 - fde[1] / sizeof(*fde); + else + cie = m->unwind_data + fde[1]; + + if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) + || (*cie & (sizeof(*cie) - 1)) + || (cie[1] != 0xffffffff && cie[1] != 0)) { + dbug_unwind(1, "cie is not valid %lx %x %x %x\n", cie, *cie, fde[1], cie[1]); + return NULL; /* this is not a (valid) CIE */ + } + + return cie; +} + +/* read an encoded pointer */ +static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrType) +{ + unsigned long value = 0; + union { + const u8 *p8; + const u16 *p16u; + const s16 *p16s; + const u32 *p32u; + const s32 *p32s; + const unsigned long *pul; + } ptr; + + if (ptrType < 0 || ptrType == DW_EH_PE_omit) + return 0; + + ptr.p8 = *pLoc; + switch (ptrType & DW_EH_PE_FORM) { + case DW_EH_PE_data2: + if (end < (const void *)(ptr.p16u + 1)) + return 0; + if (ptrType & DW_EH_PE_signed) + value = _stp_get_unaligned(ptr.p16s++); + else + value = _stp_get_unaligned(ptr.p16u++); + break; + case DW_EH_PE_data4: +#ifdef CONFIG_64BIT + if (end < (const void *)(ptr.p32u + 1)) + return 0; + if (ptrType & DW_EH_PE_signed) + value = _stp_get_unaligned(ptr.p32s++); + else + value = _stp_get_unaligned(ptr.p32u++); + break; + case DW_EH_PE_data8: + BUILD_BUG_ON(sizeof(u64) != sizeof(value)); +#else + BUILD_BUG_ON(sizeof(u32) != sizeof(value)); +#endif + case DW_EH_PE_absptr: + if (end < (const void *)(ptr.pul + 1)) + return 0; + value = _stp_get_unaligned(ptr.pul++); + break; + case DW_EH_PE_leb128: + BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); + value = ptrType & DW_EH_PE_signed ? get_sleb128(&ptr.p8, end) + : get_uleb128(&ptr.p8, end); + if ((const void *)ptr.p8 > end) + return 0; + break; + default: + return 0; + } + switch (ptrType & DW_EH_PE_ADJUST) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + value += (unsigned long)*pLoc; + break; + default: + return 0; + } + if ((ptrType & DW_EH_PE_indirect) + && _stp_read_address(value, (unsigned long *)value, KERNEL_DS)) + return 0; + *pLoc = ptr.p8; + + return value; +} + +static signed fde_pointer_type(const u32 *cie) +{ + const u8 *ptr = (const u8 *)(cie + 2); + unsigned version = *ptr; + + if (version != 1) + return -1; /* unsupported */ + if (*++ptr) { + const char *aug; + const u8 *end = (const u8 *)(cie + 1) + *cie; + uleb128_t len; + + /* check if augmentation size is first (and thus present) */ + if (*ptr != 'z') + return -1; + /* check if augmentation string is nul-terminated */ + if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) + return -1; + ++ptr; /* skip terminator */ + get_uleb128(&ptr, end); /* skip code alignment */ + get_sleb128(&ptr, end); /* skip data alignment */ + /* skip return address column */ + version <= 1 ? (void)++ptr : (void)get_uleb128(&ptr, end); + len = get_uleb128(&ptr, end); /* augmentation length */ + if (ptr + len < ptr || ptr + len > end) + return -1; + end = ptr + len; + while (*++aug) { + if (ptr >= end) + return -1; + switch (*aug) { + case 'L': + ++ptr; + break; + case 'P':{ + signed ptrType = *ptr++; + + if (!read_pointer(&ptr, end, ptrType) || ptr > end) + return -1; + } + break; + case 'R': + return *ptr; + default: + return -1; + } + } + } + return DW_EH_PE_absptr; +} + +static int advance_loc(unsigned long delta, struct unwind_state *state) +{ + state->loc += delta * state->codeAlign; + dbug_unwind(1, "state->loc=%lx\n", state->loc); + return delta > 0; +} + +static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value, struct unwind_state *state) +{ + dbug_unwind(1, "reg=%d, where=%d, value=%lx\n", reg, where, value); + if (reg < ARRAY_SIZE(state->regs)) { + state->regs[reg].where = where; + state->regs[reg].value = value; + } +} + +static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, signed ptrType, struct unwind_state *state) +{ + union { + const u8 *p8; + const u16 *p16; + const u32 *p32; + } ptr; + int result = 1; + + dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc); + if (start != state->cieStart) { + state->loc = state->org; + result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, state); + if (targetLoc == 0 && state->label == NULL) + return result; + } + + for (ptr.p8 = start; result && ptr.p8 < end;) { + switch (*ptr.p8 >> 6) { + uleb128_t value; + case 0: + switch (*ptr.p8++) { + case DW_CFA_nop: + dbug_unwind(1, "DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + if ((state->loc = read_pointer(&ptr.p8, end, ptrType)) == 0) + result = 0; + dbug_unwind(1, "DW_CFA_set_loc %lx (result=%d)\n", state->loc, result); + break; + case DW_CFA_advance_loc1: + result = ptr.p8 < end && advance_loc(*ptr.p8++, state); + dbug_unwind(1, "DW_CFA_advance_loc1 %d\n", result); + break; + case DW_CFA_advance_loc2: + result = ptr.p8 <= end + 2 && advance_loc(*ptr.p16++, state); + dbug_unwind(1, "DW_CFA_advance_loc2 %d\n", result); + break; + case DW_CFA_advance_loc4: + result = ptr.p8 <= end + 4 && advance_loc(*ptr.p32++, state); + dbug_unwind(1, "DW_CFA_advance_loc4 %d\n", result); + break; + case DW_CFA_offset_extended: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Memory, get_uleb128(&ptr.p8, end), state); + dbug_unwind(1, "DW_CFA_offset_extended\n"); + break; + case DW_CFA_val_offset: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Value, get_uleb128(&ptr.p8, end), state); + dbug_unwind(1, "DW_CFA_val_offset\n"); + break; + case DW_CFA_offset_extended_sf: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Memory, get_sleb128(&ptr.p8, end), state); + dbug_unwind(1, "DW_CFA_offset_extended_sf\n"); + break; + case DW_CFA_val_offset_sf: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Value, get_sleb128(&ptr.p8, end), state); + dbug_unwind(1, "DW_CFA_val_offset_sf\n"); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, state); + dbug_unwind(1, "DW_CFA_undefined\n"); + break; + case DW_CFA_register: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Register, get_uleb128(&ptr.p8, end), state); + dbug_unwind(1, "DW_CFA_register\n"); + break; + case DW_CFA_remember_state: + dbug_unwind(1, "DW_CFA_remember_state\n"); + if (ptr.p8 == state->label) { + state->label = NULL; + return 1; + } + if (state->stackDepth >= MAX_STACK_DEPTH) + return 0; + state->stack[state->stackDepth++] = ptr.p8; + break; + case DW_CFA_restore_state: + dbug_unwind(1, "DW_CFA_restore_state\n"); + if (state->stackDepth) { + const uleb128_t loc = state->loc; + const u8 *label = state->label; + + state->label = state->stack[state->stackDepth - 1]; + memcpy(&state->cfa, &badCFA, sizeof(state->cfa)); + memset(state->regs, 0, sizeof(state->regs)); + state->stackDepth = 0; + result = processCFI(start, end, 0, ptrType, state); + state->loc = loc; + state->label = label; + } else + return 0; + break; + case DW_CFA_def_cfa: + state->cfa.reg = get_uleb128(&ptr.p8, end); + dbug_unwind(1, "DW_CFA_def_cfa reg=%ld\n", state->cfa.reg); + /*nobreak */ + case DW_CFA_def_cfa_offset: + state->cfa.offs = get_uleb128(&ptr.p8, end); + dbug_unwind(1, "DW_CFA_def_cfa_offset offs=%lx\n", state->cfa.offs); + break; + case DW_CFA_def_cfa_sf: + state->cfa.reg = get_uleb128(&ptr.p8, end); + dbug_unwind(1, "DW_CFA_def_cfa_sf reg=%ld\n", state->cfa.reg); + /*nobreak */ + case DW_CFA_def_cfa_offset_sf: + state->cfa.offs = get_sleb128(&ptr.p8, end) * state->dataAlign; + dbug_unwind(1, "DW_CFA_def_cfa_offset_sf offs=%lx\n", state->cfa.offs); + break; + case DW_CFA_def_cfa_register: + state->cfa.reg = get_uleb128(&ptr.p8, end); + dbug_unwind(1, "DW_CFA_def_cfa_register reg=%ld\n", state->cfa.reg); + break; + /*todo case DW_CFA_def_cfa_expression: */ + /*todo case DW_CFA_expression: */ + /*todo case DW_CFA_val_expression: */ + case DW_CFA_GNU_args_size: + get_uleb128(&ptr.p8, end); + dbug_unwind(1, "DW_CFA_GNU_args_size\n"); + break; + case DW_CFA_GNU_negative_offset_extended: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Memory, (uleb128_t)0 - get_uleb128(&ptr.p8, end), state); + dbug_unwind(1, "DW_CFA_GNU_negative_offset_extended\n"); + break; + case DW_CFA_GNU_window_save: + default: + dbug_unwind(1, "unimplemented call frame instruction: 0x%x\n", *(ptr.p8 - 1)); + result = 0; + break; + } + break; + case 1: + result = advance_loc(*ptr.p8++ & 0x3f, state); + dbug_unwind(1, "case 1\n"); + break; + case 2: + value = *ptr.p8++ & 0x3f; + set_rule(value, Memory, get_uleb128(&ptr.p8, end), state); + dbug_unwind(1, "case 2\n"); + break; + case 3: + set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); + dbug_unwind(1, "case 3\n"); + break; + } + dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc); + if (ptr.p8 > end) + result = 0; + if (result && targetLoc != 0 && targetLoc < state->loc) + return 1; + } + return result && ptr.p8 == end && (targetLoc == 0 || state->label == NULL); +} + +/* If we previously created an unwind header, then use it now to binary search */ +/* for the FDE corresponding to pc. */ + +static u32 *_stp_search_unwind_hdr(unsigned long pc, struct _stp_module *m) +{ + const u8 *ptr, *end, *hdr = m->unwind_hdr; + unsigned long startLoc; + u32 *fde = NULL; + unsigned num, tableSize, t2; + + if (hdr == NULL || hdr[0] != 1) + return NULL; + + dbug_unwind(1, "search for %lx", pc); + + /* table_enc */ + switch (hdr[3] & DW_EH_PE_FORM) { + case DW_EH_PE_absptr: + tableSize = sizeof(unsigned long); + break; + case DW_EH_PE_data2: + tableSize = 2; + break; + case DW_EH_PE_data4: + tableSize = 4; + break; + case DW_EH_PE_data8: + tableSize = 8; + break; + default: + dbug_unwind(1, "bad table encoding"); + return NULL; + } + ptr = hdr + 4; + end = hdr + m->unwind_hdr_len; + + if (read_pointer(&ptr, end, hdr[1]) != (unsigned long)m->unwind_data) { + dbug_unwind(1, "eh_frame_ptr not valid"); + return NULL; + } + + num = read_pointer(&ptr, end, hdr[2]); + if (num == 0 || num != (end - ptr) / (2 * tableSize) || (end - ptr) % (2 * tableSize)) { + dbug_unwind(1, "Bad num=%d end-ptr=%ld 2*tableSize=%d", num, end - ptr, 2 * tableSize); + return NULL; + } + + do { + const u8 *cur = ptr + (num / 2) * (2 * tableSize); + startLoc = read_pointer(&cur, cur + tableSize, hdr[3]); + if (pc < startLoc) + num /= 2; + else { + ptr = cur - tableSize; + num = (num + 1) / 2; + } + } while (startLoc && num > 1); + + if (num == 1 && (startLoc = read_pointer(&ptr, ptr + tableSize, hdr[3])) != 0 && pc >= startLoc) + fde = (void *)read_pointer(&ptr, ptr + tableSize, hdr[3]); + + dbug_unwind(1, "returning fde=%lx startLoc=%lx", fde, startLoc); + return fde; +} + +#ifdef DEBUG_UNWIND +static const char *_stp_enc_hi_name[] = { + "", + "DW_EH_PE_pcrel", + "DW_EH_PE_textrel", + "DW_EH_PE_datarel", + "DW_EH_PE_funcrel", + "DW_EH_PE_aligned" +}; +static const char *_stp_enc_lo_name[] = { + "DW_EH_PE_absptr", + "DW_EH_PE_uleb128", + "DW_EH_PE_udata2", + "DW_EH_PE_udata4", + "DW_EH_PE_udata8", + "DW_EH_PE_sleb128", + "DW_EH_PE_sdata2", + "DW_EH_PE_sdata4", + "DW_EH_PE_sdata8" +}; +char *_stp_eh_enc_name(signed type) +{ + static char buf[64]; + int hi, low; + if (type == DW_EH_PE_omit) + return "DW_EH_PE_omit"; + + hi = (type & DW_EH_PE_ADJUST) >> 4; + low = type & DW_EH_PE_FORM; + if (hi > 5 || low > 4 || (low == 0 && (type & DW_EH_PE_signed))) { + sprintf(buf, "ERROR:encoding=0x%x", type); + return buf; + } + + buf[0] = 0; + if (type & DW_EH_PE_indirect) + strlcpy(buf, "DW_EH_PE_indirect|", sizeof(buf)); + if (hi) + strlcat(buf, _stp_enc_hi_name[hi], sizeof(buf)); + + if (type & DW_EH_PE_signed) + low += 4; + strlcat(buf, _stp_enc_lo_name[low], sizeof(buf)); + return buf; +} +#endif /* DEBUG_UNWIND */ + +/* Unwind to previous to frame. Returns 0 if successful, negative + * number in case of an error. A positive return means unwinding is finished; + * don't try to fallback to dumping addresses on the stack. */ +int unwind(struct unwind_frame_info *frame) +{ +#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) + const u32 *fde, *cie = NULL; + const u8 *ptr = NULL, *end = NULL; + unsigned long pc = UNW_PC(frame) - frame->call_frame; + unsigned long tableSize, startLoc = 0, endLoc = 0, cfa; + unsigned i; + signed ptrType = -1; + uleb128_t retAddrReg = 0; + struct _stp_module *m; + struct unwind_state state; + + dbug_unwind(1, "pc=%lx, %lx", pc, UNW_PC(frame)); + + if (UNW_PC(frame) == 0) + return -EINVAL; + + m = _stp_get_unwind_info(pc); + if (unlikely(m == NULL)) { + dbug_unwind(1, "No module found for pc=%lx", pc); + return -EINVAL; + } + + if (unlikely(m->unwind_data_len == 0 || m->unwind_data_len & (sizeof(*fde) - 1))) { + dbug_unwind(1, "Module %s: unwind_data_len=%d", m->name, m->unwind_data_len); + goto err; + } + + fde = _stp_search_unwind_hdr(pc, m); + dbug_unwind(1, "%s: fde=%lx\n", m->name, fde); + + /* found the fde, now set startLoc and endLoc */ + if (fde != NULL) { + cie = cie_for_fde(fde, m); + if (likely(cie != NULL && cie != &bad_cie && cie != ¬_fde)) { + ptr = (const u8 *)(fde + 2); + ptrType = fde_pointer_type(cie); + startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); + dbug_unwind(2, "startLoc=%lx, ptrType=%s", startLoc, _stp_eh_enc_name(ptrType)); + if (!(ptrType & DW_EH_PE_indirect)) + ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed; + endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); + if (pc > endLoc) { + dbug_unwind(1, "pc (%lx) > endLoc(%lx)\n", pc, endLoc); + goto done; + } + } else { + dbug_unwind(1, "fde found in header, but cie is bad!\n"); + fde = NULL; + } + } + + /* did not a good fde find with binary search, so do slow linear search */ + if (fde == NULL) { + for (fde = m->unwind_data, tableSize = m->unwind_data_len; cie = NULL, tableSize > sizeof(*fde) + && tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { + dbug_unwind(3, "fde=%lx tableSize=%d\n", (long)*fde, (int)tableSize); + cie = cie_for_fde(fde, m); + if (cie == &bad_cie) { + cie = NULL; + break; + } + if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie)) < 0) + continue; + + ptr = (const u8 *)(fde + 2); + startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); + dbug_unwind(2, "startLoc=%lx, ptrType=%s", startLoc, _stp_eh_enc_name(ptrType)); + if (!startLoc) + continue; + if (!(ptrType & DW_EH_PE_indirect)) + ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed; + endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); + dbug_unwind(3, "endLoc=%lx\n", endLoc); + if (pc >= startLoc && pc < endLoc) + break; + } + } + + dbug_unwind(1, "cie=%lx fde=%lx startLoc=%lx endLoc=%lx\n", cie, fde, startLoc, endLoc); + if (cie == NULL || fde == NULL) + goto err; + + /* found the CIE and FDE */ + + memset(&state, 0, sizeof(state)); + state.cieEnd = ptr; /* keep here temporarily */ + ptr = (const u8 *)(cie + 2); + end = (const u8 *)(cie + 1) + *cie; + frame->call_frame = 1; + if ((state.version = *ptr) != 1) { + dbug_unwind(1, "CIE version number is %d. 1 is supported.\n", state.version); + goto err; /* unsupported version */ + } + if (*++ptr) { + /* check if augmentation size is first (and thus present) */ + if (*ptr == 'z') { + while (++ptr < end && *ptr) { + switch (*ptr) { + /* check for ignorable (or already handled) + * nul-terminated augmentation string */ + case 'L': + case 'P': + case 'R': + continue; + case 'S': + dbug_unwind(1, "This is a signal frame\n"); + frame->call_frame = 0; + continue; + default: + break; + } + break; + } + } + if (ptr >= end || *ptr) { + dbug_unwind(1, "Problem parsing the augmentation string.\n"); + goto err; + } + } + ++ptr; + + /* get code aligment factor */ + state.codeAlign = get_uleb128(&ptr, end); + /* get data aligment factor */ + state.dataAlign = get_sleb128(&ptr, end); + if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) + goto err;; + + retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); + + /* skip augmentation */ + if (((const char *)(cie + 2))[1] == 'z') { + uleb128_t augSize = get_uleb128(&ptr, end); + ptr += augSize; + } + if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info) + || REG_INVALID(retAddrReg) + || reg_info[retAddrReg].width != sizeof(unsigned long)) + goto err; + + state.cieStart = ptr; + ptr = state.cieEnd; + state.cieEnd = end; + end = (const u8 *)(fde + 1) + *fde; + + /* skip augmentation */ + if (((const char *)(cie + 2))[1] == 'z') { + uleb128_t augSize = get_uleb128(&ptr, end); + if ((ptr += augSize) > end) + goto err; + } + + state.org = startLoc; + memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); + /* process instructions */ + if (!processCFI(ptr, end, pc, ptrType, &state) + || state.loc > endLoc || state.regs[retAddrReg].where == Nowhere || state.cfa.reg >= ARRAY_SIZE(reg_info) + || reg_info[state.cfa.reg].width != sizeof(unsigned long) + || state.cfa.offs % sizeof(unsigned long)) + goto err; + + /* update frame */ +#ifndef CONFIG_AS_CFI_SIGNAL_FRAME + if (frame->call_frame && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) + frame->call_frame = 0; +#endif + cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; + startLoc = min((unsigned long)UNW_SP(frame), cfa); + endLoc = max((unsigned long)UNW_SP(frame), cfa); + dbug_unwind(1, "cfa=%lx startLoc=%lx, endLoc=%lx\n", cfa, startLoc, endLoc); + if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { + startLoc = min(STACK_LIMIT(cfa), cfa); + endLoc = max(STACK_LIMIT(cfa), cfa); + dbug_unwind(1, "cfa startLoc=%p, endLoc=%p\n", (u64)startLoc, (u64)endLoc); + } +#ifndef CONFIG_64BIT +# define CASES CASE(8); CASE(16); CASE(32) +#else +# define CASES CASE(8); CASE(16); CASE(32); CASE(64) +#endif + dbug_unwind(1, "cie=%lx fde=%lx\n", cie, fde); + for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { + if (REG_INVALID(i)) { + if (state.regs[i].where == Nowhere) + continue; + dbug_unwind(2, "REG_INVALID %d\n", i); + goto err; + } + dbug_unwind(2, "register %d. where=%d\n", i, state.regs[i].where); + switch (state.regs[i].where) { + default: + break; + case Register: + if (state.regs[i].value >= ARRAY_SIZE(reg_info) + || REG_INVALID(state.regs[i].value) + || reg_info[i].width > reg_info[state.regs[i].value].width) { + dbug_unwind(2, "case Register bad\n"); + goto err; + } + switch (reg_info[state.regs[i].value].width) { +#define CASE(n) \ + case sizeof(u##n): \ + state.regs[i].value = FRAME_REG(state.regs[i].value, \ + const u##n); \ + break + CASES; +#undef CASE + default: + dbug_unwind(2, "default\n"); + goto err; + } + break; + } + } + for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { + dbug_unwind(2, "register %d. invalid=%d\n", i, REG_INVALID(i)); + if (REG_INVALID(i)) + continue; + dbug_unwind(2, "register %d. where=%d\n", i, state.regs[i].where); + switch (state.regs[i].where) { + case Nowhere: + if (reg_info[i].width != sizeof(UNW_SP(frame)) + || &FRAME_REG(i, __typeof__(UNW_SP(frame))) + != &UNW_SP(frame)) + continue; + UNW_SP(frame) = cfa; + break; + case Register: + switch (reg_info[i].width) { +#define CASE(n) case sizeof(u##n): \ + FRAME_REG(i, u##n) = state.regs[i].value; \ + break + CASES; +#undef CASE + default: + dbug_unwind(2, "default\n"); + goto err; + } + break; + case Value: + if (reg_info[i].width != sizeof(unsigned long)) { + dbug_unwind(2, "Value\n"); + goto err; + } + FRAME_REG(i, unsigned long) = cfa + state.regs[i].value * state.dataAlign; + break; + case Memory:{ + unsigned long addr = cfa + state.regs[i].value * state.dataAlign; + dbug_unwind(2, "addr=%lx width=%d\n", addr, reg_info[i].width); + switch (reg_info[i].width) { +#define CASE(n) case sizeof(u##n): \ + if (unlikely(_stp_read_address(FRAME_REG(i, u##n), (u##n *)addr, KERNEL_DS))) \ + goto copy_failed;\ + dbug_unwind(1, "set register %d to %lx\n", i, (long)FRAME_REG(i,u##n));\ + break + CASES; +#undef CASE + default: + dbug_unwind(2, "default\n"); + goto err; + } + } + break; + } + } + read_unlock(&m->lock); + dbug_unwind(1, "returning 0 (%lx)\n", UNW_PC(frame)); + return 0; + +copy_failed: + dbug_unwind(1, "_stp_read_address failed to access memory\n"); +err: + read_unlock(&m->lock); + return -EIO; + +done: + /* PC was in a range convered by a module but no unwind info */ + /* found for the specific PC. This seems to happen only for kretprobe */ + /* trampolines and at the end of interrupt backtraces. */ + read_unlock(&m->lock); + return 1; +#undef CASES +#undef FRAME_REG +} diff --git a/runtime/unwind/i386.h b/runtime/unwind/i386.h new file mode 100644 index 00000000..1f69b4a9 --- /dev/null +++ b/runtime/unwind/i386.h @@ -0,0 +1,153 @@ +/* -*- linux-c -*- + * + * 32-bit x86 dwarf unwinder header file + * Copyright (C) 2008 Red Hat Inc. + * Copyright (C) 2002-2006 Novell, 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 _STP_I386_UNWIND_H +#define _STP_I386_UNWIND_H + +#include <linux/sched.h> +#include <asm/fixmap.h> +#include <asm/ptrace.h> +#include <asm/uaccess.h> + +/* these are simple for i386 */ +#define _stp_get_unaligned(ptr) (*(ptr)) +#define _stp_put_unaligned(val, ptr) ((void)( *(ptr) = (val) )) + +struct unwind_frame_info +{ + struct pt_regs regs; + struct task_struct *task; + unsigned call_frame:1; +}; + +#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) + +#ifdef STAPCONF_X86_UNIREGS + +#define UNW_PC(frame) (frame)->regs.ip +#define UNW_SP(frame) (frame)->regs.sp +#ifdef STP_USE_FRAME_POINTER +#define UNW_FP(frame) (frame)->regs.bp +#define FRAME_RETADDR_OFFSET 4 +#define FRAME_LINK_OFFSET 0 +#define STACK_BOTTOM(tsk) STACK_LIMIT((tsk)->thread.sp0) +#define STACK_TOP(tsk) ((tsk)->thread.sp0) +#else +#define UNW_FP(frame) ((void)(frame), 0) +#endif + +#define UNW_REGISTER_INFO \ + PTREGS_INFO(ax), \ + PTREGS_INFO(cx), \ + PTREGS_INFO(dx), \ + PTREGS_INFO(bx), \ + PTREGS_INFO(sp), \ + PTREGS_INFO(bp), \ + PTREGS_INFO(si), \ + PTREGS_INFO(di), \ + PTREGS_INFO(ip) + +#else /* !STAPCONF_X86_UNIREGS */ + +#define UNW_PC(frame) (frame)->regs.eip +#define UNW_SP(frame) (frame)->regs.esp +#ifdef STP_USE_FRAME_POINTER +#define UNW_FP(frame) (frame)->regs.ebp +#define FRAME_RETADDR_OFFSET 4 +#define FRAME_LINK_OFFSET 0 +#define STACK_BOTTOM(tsk) STACK_LIMIT((tsk)->thread.esp0) +#define STACK_TOP(tsk) ((tsk)->thread.esp0) +#else +#define UNW_FP(frame) ((void)(frame), 0) +#endif + +#define UNW_REGISTER_INFO \ + PTREGS_INFO(eax), \ + PTREGS_INFO(ecx), \ + PTREGS_INFO(edx), \ + PTREGS_INFO(ebx), \ + PTREGS_INFO(esp), \ + PTREGS_INFO(ebp), \ + PTREGS_INFO(esi), \ + PTREGS_INFO(edi), \ + PTREGS_INFO(eip) + +#endif /* STAPCONF_X86_UNIREGS */ + +#define UNW_DEFAULT_RA(raItem, dataAlign) \ + ((raItem).where == Memory && \ + !((raItem).value * (dataAlign) + 4)) + +static inline void arch_unw_init_frame_info(struct unwind_frame_info *info, + /*const*/ struct pt_regs *regs) +{ + if (user_mode_vm(regs)) + info->regs = *regs; + else { +#ifdef STAPCONF_X86_UNIREGS + memcpy(&info->regs, regs, offsetof(struct pt_regs, sp)); + info->regs.sp = (unsigned long)®s->sp; + info->regs.ss = __KERNEL_DS; +#else + memcpy(&info->regs, regs, offsetof(struct pt_regs, esp)); + info->regs.esp = (unsigned long)®s->esp; + info->regs.xss = __KERNEL_DS; +#endif + + } + info->call_frame = 1; +} + +static inline void arch_unw_init_blocked(struct unwind_frame_info *info) +{ + memset(&info->regs, 0, sizeof(info->regs)); +#ifdef STAPCONF_X86_UNIREGS + info->regs.ip = info->task->thread.ip; + info->regs.cs = __KERNEL_CS; + __get_user(info->regs.bp, (long *)info->task->thread.sp); + info->regs.sp = info->task->thread.sp; + info->regs.ss = __KERNEL_DS; + info->regs.ds = __USER_DS; + info->regs.es = __USER_DS; +#else + info->regs.eip = info->task->thread.eip; + info->regs.xcs = __KERNEL_CS; + __get_user(info->regs.ebp, (long *)info->task->thread.esp); + info->regs.esp = info->task->thread.esp; + info->regs.xss = __KERNEL_DS; + info->regs.xds = __USER_DS; + info->regs.xes = __USER_DS; +#endif + +} + + +static inline int arch_unw_user_mode(const struct unwind_frame_info *info) +{ +#if 0 /* This can only work when selector register and EFLAGS saves/restores + are properly annotated (and tracked in UNW_REGISTER_INFO). */ + return user_mode_vm(&info->regs); +#else +#ifdef STAPCONF_X86_UNIREGS + return info->regs.ip < PAGE_OFFSET + || (info->regs.ip >= __fix_to_virt(FIX_VDSO) + && info->regs.ip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE) + || info->regs.sp < PAGE_OFFSET; +#else + return info->regs.eip < PAGE_OFFSET + || (info->regs.eip >= __fix_to_virt(FIX_VDSO) + && info->regs.eip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE) + || info->regs.esp < PAGE_OFFSET; +#endif +#endif +} + +#endif /* _STP_I386_UNWIND_H */ diff --git a/runtime/unwind/unwind.h b/runtime/unwind/unwind.h new file mode 100644 index 00000000..8651cb9e --- /dev/null +++ b/runtime/unwind/unwind.h @@ -0,0 +1,142 @@ +/* -*- linux-c -*- + * + * dwarf unwinder header file + * Copyright (C) 2008 Red Hat Inc. + * Copyright (C) 2002-2006 Novell, 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 _STP_UNWIND_H_ +#define _STP_UNWIND_H_ + +#if defined (__x86_64__) +#include "x86_64.h" +#elif defined (__i386__) +#include "i386.h" +#else +#error "Unsupported dwarf unwind architecture" +#endif + +#define MAX_STACK_DEPTH 8 + +#define EXTRA_INFO(f) { \ + BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \ + % FIELD_SIZEOF(struct unwind_frame_info, f)) \ + + offsetof(struct unwind_frame_info, f) \ + / FIELD_SIZEOF(struct unwind_frame_info, f), \ + FIELD_SIZEOF(struct unwind_frame_info, f) \ + } +#define PTREGS_INFO(f) EXTRA_INFO(regs.f) + +static const struct { + unsigned offs:BITS_PER_LONG / 2; + unsigned width:BITS_PER_LONG / 2; +} reg_info[] = { + UNW_REGISTER_INFO +}; + +#undef PTREGS_INFO +#undef EXTRA_INFO + +#ifndef REG_INVALID +#define REG_INVALID(r) (reg_info[r].width == 0) +#endif + +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 +#define DW_CFA_lo_user 0x1c +#define DW_CFA_GNU_window_save 0x2d +#define DW_CFA_GNU_args_size 0x2e +#define DW_CFA_GNU_negative_offset_extended 0x2f +#define DW_CFA_hi_user 0x3f + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_leb128 0x01 +#define DW_EH_PE_data2 0x02 +#define DW_EH_PE_data4 0x03 +#define DW_EH_PE_data8 0x04 +#define DW_EH_PE_FORM 0x07 /* mask */ +#define DW_EH_PE_signed 0x08 /* signed versions of above have this bit set */ + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 +#define DW_EH_PE_ADJUST 0x70 /* mask */ +#define DW_EH_PE_indirect 0x80 +#define DW_EH_PE_omit 0xff + +typedef unsigned long uleb128_t; +typedef signed long sleb128_t; + +static struct unwind_table { + unsigned long pc; /* text */ + unsigned long range; /* text_size */ + const void *address; /* unwind_data */ + unsigned long size; /* unwind_data_len */ + const unsigned char *header; /* unwind_header */ + unsigned long hdrsz; + struct unwind_table *link; + const char *name; /* module name */ +} root_table; + +struct unwind_item { + enum item_location { + Nowhere, + Memory, + Register, + Value + } where; + uleb128_t value; +}; + +struct unwind_state { + uleb128_t loc, org; + const u8 *cieStart, *cieEnd; + uleb128_t codeAlign; + sleb128_t dataAlign; + struct cfa { + uleb128_t reg, offs; + } cfa; + struct unwind_item regs[ARRAY_SIZE(reg_info)]; + unsigned stackDepth:8; + unsigned version:8; + const u8 *label; + const u8 *stack[MAX_STACK_DEPTH]; +}; + +static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; +static unsigned long read_pointer(const u8 **pLoc, + const void *end, + signed ptrType); +static const u32 bad_cie, not_fde; +static const u32 *cie_for_fde(const u32 *fde, const struct _stp_module *); +static signed fde_pointer_type(const u32 *cie); + +#endif /*_STP_UNWIND_H_*/ diff --git a/runtime/unwind/x86_64.h b/runtime/unwind/x86_64.h new file mode 100644 index 00000000..5eb3a58f --- /dev/null +++ b/runtime/unwind/x86_64.h @@ -0,0 +1,150 @@ +/* -*- linux-c -*- + * + * x86_64 dwarf unwinder header file + * Copyright (C) 2008 Red Hat Inc. + * Copyright (C) 2002-2006 Novell, 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 _STP_X86_64_UNWIND_H +#define _STP_X86_64_UNWIND_H + +/* + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich <jbeulich@novell.com> + * This code is released under version 2 of the GNU GPL. + */ + +#include <linux/sched.h> +#include <asm/ptrace.h> +#include <asm/vsyscall.h> + +/* these are simple for x86_64 */ +#define _stp_get_unaligned(ptr) (*(ptr)) +#define _stp_put_unaligned(val, ptr) ((void)( *(ptr) = (val) )) + +struct unwind_frame_info +{ + struct pt_regs regs; + struct task_struct *task; + unsigned call_frame:1; +}; + +#ifdef STAPCONF_X86_UNIREGS +#define UNW_PC(frame) (frame)->regs.ip +#define UNW_SP(frame) (frame)->regs.sp +#else +#define UNW_PC(frame) (frame)->regs.rip +#define UNW_SP(frame) (frame)->regs.rsp +#endif /* STAPCONF_X86_UNIREGS */ + +#if 0 /* STP_USE_FRAME_POINTER */ +/* Frame pointers not implemented in x86_64 currently */ +#define UNW_FP(frame) (frame)->regs.rbp +#define FRAME_RETADDR_OFFSET 8 +#define FRAME_LINK_OFFSET 0 +#define STACK_BOTTOM(tsk) (((tsk)->thread.rsp0 - 1) & ~(THREAD_SIZE - 1)) +#define STACK_TOP(tsk) ((tsk)->thread.rsp0) +#endif + +/* Might need to account for the special exception and interrupt handling + stacks here, since normally + EXCEPTION_STACK_ORDER < THREAD_ORDER < IRQSTACK_ORDER, + but the construct is needed only for getting across the stack switch to + the interrupt stack - thus considering the IRQ stack itself is unnecessary, + and the overhead of comparing against all exception handling stacks seems + not desirable. */ +#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) + +#ifdef STAPCONF_X86_UNIREGS +#define UNW_REGISTER_INFO \ + PTREGS_INFO(ax), \ + PTREGS_INFO(dx), \ + PTREGS_INFO(cx), \ + PTREGS_INFO(bx), \ + PTREGS_INFO(si), \ + PTREGS_INFO(di), \ + PTREGS_INFO(bp), \ + PTREGS_INFO(sp), \ + PTREGS_INFO(r8), \ + PTREGS_INFO(r9), \ + PTREGS_INFO(r10), \ + PTREGS_INFO(r11), \ + PTREGS_INFO(r12), \ + PTREGS_INFO(r13), \ + PTREGS_INFO(r14), \ + PTREGS_INFO(r15), \ + PTREGS_INFO(ip) +#else +#define UNW_REGISTER_INFO \ + PTREGS_INFO(rax), \ + PTREGS_INFO(rdx), \ + PTREGS_INFO(rcx), \ + PTREGS_INFO(rbx), \ + PTREGS_INFO(rsi), \ + PTREGS_INFO(rdi), \ + PTREGS_INFO(rbp), \ + PTREGS_INFO(rsp), \ + PTREGS_INFO(r8), \ + PTREGS_INFO(r9), \ + PTREGS_INFO(r10), \ + PTREGS_INFO(r11), \ + PTREGS_INFO(r12), \ + PTREGS_INFO(r13), \ + PTREGS_INFO(r14), \ + PTREGS_INFO(r15), \ + PTREGS_INFO(rip) +#endif /* STAPCONF_X86_UNIREGS */ + +#define UNW_DEFAULT_RA(raItem, dataAlign) \ + ((raItem).where == Memory && \ + !((raItem).value * (dataAlign) + 8)) + +static inline void arch_unw_init_frame_info(struct unwind_frame_info *info, + /*const*/ struct pt_regs *regs) +{ + info->regs = *regs; + info->call_frame = 1; +} + +static inline void arch_unw_init_blocked(struct unwind_frame_info *info) +{ + extern const char thread_return[]; + + memset(&info->regs, 0, sizeof(info->regs)); + info->regs.cs = __KERNEL_CS; + info->regs.ss = __KERNEL_DS; + +#ifdef STAPCONF_X86_UNIREGS + info->regs.ip = (unsigned long)thread_return; + __get_user(info->regs.bp, (unsigned long *)info->task->thread.sp); + info->regs.sp = info->task->thread.sp; +#else + info->regs.rip = (unsigned long)thread_return; + __get_user(info->regs.rbp, (unsigned long *)info->task->thread.rsp); + info->regs.rsp = info->task->thread.rsp; +#endif +} + +static inline int arch_unw_user_mode(const struct unwind_frame_info *info) +{ +#if 0 /* This can only work when selector register saves/restores + are properly annotated (and tracked in UNW_REGISTER_INFO). */ + return user_mode(&info->regs); +#else +#ifdef STAPCONF_X86_UNIREGS + return (long)info->regs.ip >= 0 + || (info->regs.ip >= VSYSCALL_START && info->regs.ip < VSYSCALL_END) + || (long)info->regs.sp >= 0; +#else + return (long)info->regs.rip >= 0 + || (info->regs.rip >= VSYSCALL_START && info->regs.rip < VSYSCALL_END) + || (long)info->regs.rsp >= 0; +#endif +#endif +} + +#endif /* _STP_X86_64_UNWIND_H */ diff --git a/tapsets.cxx b/tapsets.cxx index a7f8034e..7d377d40 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -104,7 +104,6 @@ struct be_derived_probe: public derived_probe struct be_derived_probe_group: public generic_dpg<be_derived_probe> { public: - void emit_module_header (systemtap_session& ) { }; void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -2069,7 +2068,6 @@ private: public: void enroll (dwarf_derived_probe* probe); - void emit_module_header (systemtap_session& s); void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -3929,22 +3927,17 @@ dwarf_derived_probe_group::enroll (dwarf_derived_probe* p) void -dwarf_derived_probe_group::emit_module_header (systemtap_session& s) +dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) { if (probes_by_module.empty()) return; - // Warn of misconfigured kernels + s.op->newline() << "/* ---- dwarf probes ---- */"; + + // Warn of misconfigured kernels s.op->newline() << "#if ! defined(CONFIG_KPROBES)"; s.op->newline() << "#error \"Need CONFIG_KPROBES!\""; s.op->newline() << "#endif"; -} - -void -dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) -{ - if (probes_by_module.empty()) return; - - s.op->newline() << "/* ---- dwarf probes ---- */"; + s.op->newline(); // Forward declare the master entry functions s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,"; @@ -4271,7 +4264,6 @@ struct uprobe_derived_probe: public derived_probe struct uprobe_derived_probe_group: public generic_dpg<uprobe_derived_probe> { public: - void emit_module_header (systemtap_session& s); void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -4321,9 +4313,10 @@ struct uprobe_builder: public derived_probe_builder void -uprobe_derived_probe_group::emit_module_header (systemtap_session& s) +uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) { if (probes.empty()) return; + s.op->newline() << "/* ---- user probes ---- */"; // If uprobes isn't in the kernel, pull it in from the runtime. s.op->newline() << "#if defined(CONFIG_UPROBES) || defined(CONFIG_UPROBES_MODULE)"; @@ -4331,14 +4324,6 @@ uprobe_derived_probe_group::emit_module_header (systemtap_session& s) s.op->newline() << "#else"; s.op->newline() << "#include \"uprobes/uprobes.h\""; s.op->newline() << "#endif"; -} - - -void -uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) -{ - if (probes.empty()) return; - s.op->newline() << "/* ---- user probes ---- */"; s.op->newline() << "struct stap_uprobe {"; s.op->newline(1) << "union { struct uprobe up; struct uretprobe urp; };"; @@ -4457,7 +4442,6 @@ struct timer_derived_probe_group: public generic_dpg<timer_derived_probe> { void emit_interval (translator_output* o); public: - void emit_module_header (systemtap_session& ) { }; void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -4599,7 +4583,6 @@ struct profile_derived_probe: public derived_probe struct profile_derived_probe_group: public generic_dpg<profile_derived_probe> { public: - void emit_module_header (systemtap_session& ) { }; void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -4767,7 +4750,6 @@ public: has_read_probes(false), has_write_probes(false) {} void enroll (procfs_derived_probe* probe); - void emit_module_header (systemtap_session& ) { }; void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -5243,7 +5225,6 @@ struct mark_derived_probe: public derived_probe struct mark_derived_probe_group: public generic_dpg<mark_derived_probe> { public: - void emit_module_header (systemtap_session& s); void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -5610,7 +5591,19 @@ void mark_derived_probe::join_group (systemtap_session& s) { if (! s.mark_derived_probes) - s.mark_derived_probes = new mark_derived_probe_group (); + { + 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); } @@ -5682,20 +5675,6 @@ mark_derived_probe::initialize_probe_context_vars (translator_output* o) void -mark_derived_probe_group::emit_module_header (systemtap_session& s) -{ - if (probes.empty()) - return; - - // Warn of misconfigured kernels - s.op->newline() << "#if ! defined(CONFIG_MARKERS)"; - s.op->newline() << "#error \"Need CONFIG_MARKERS!\""; - s.op->newline() << "#endif"; - s.op->newline() << "#include <linux/marker.h>"; - s.op->newline(); -} - -void mark_derived_probe_group::emit_module_decls (systemtap_session& s) { if (probes.empty()) @@ -5958,7 +5937,6 @@ struct hrtimer_derived_probe_group: public generic_dpg<hrtimer_derived_probe> { void emit_interval (translator_output* o); public: - void emit_module_header (systemtap_session& ) { }; void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); @@ -6314,7 +6292,6 @@ public: struct perfmon_derived_probe_group: public generic_dpg<perfmon_derived_probe> { public: - void emit_module_header (systemtap_session& ) { }; void emit_module_decls (systemtap_session&) {} void emit_module_init (systemtap_session&) {} void emit_module_exit (systemtap_session&) {} diff --git a/testsuite/.gitignore b/testsuite/.gitignore new file mode 100644 index 00000000..34a4e8d0 --- /dev/null +++ b/testsuite/.gitignore @@ -0,0 +1,4 @@ +.systemtap +site.exp +systemtap.log +systemtap.sum diff --git a/testsuite/systemtap.context/backtrace.stp b/testsuite/systemtap.context/backtrace.stp index c14d071c..ddd7d00a 100644 --- a/testsuite/systemtap.context/backtrace.stp +++ b/testsuite/systemtap.context/backtrace.stp @@ -1,10 +1,9 @@ -function print_all_trace_info(point:string) { +function print_all_trace_info(point) { printf("backtrace from %s:\n", pp()) print_backtrace() - print("--------\n") + printf("--- %s ---\n", point) bt = backtrace() - printf("the %s stack is %s\n", point, bt) - printf("--<%s>--\n", point) + printf("the stack is %s\n", bt) print_stack(bt); print("--------\n") } @@ -18,18 +17,20 @@ probe end { } global flag = 0 -probe module("systemtap_test_module2").function("yyy_func3").call { - print_all_trace_info("call") + +probe module("systemtap_test_module2").function("yyy_func2") { + print_all_trace_info("yyy_func2") flag ++ } -probe module("systemtap_test_module2").function("yyy_func4").return { - print_all_trace_info("return") + +probe module("systemtap_test_module2").function("yyy_func3") { + print_all_trace_info("yyy_func3") flag ++ } -probe timer.profile { - if (cpu() == 0 && flag == 2 && probemod() != "systemtap_test_module2") { - print_all_trace_info("profile") - flag ++ - } + +probe module("systemtap_test_module2").function("yyy_func4") { + print_all_trace_info("yyy_func4") + flag ++ } + diff --git a/testsuite/systemtap.context/backtrace.tcl b/testsuite/systemtap.context/backtrace.tcl index f359cd41..6c8aee40 100644 --- a/testsuite/systemtap.context/backtrace.tcl +++ b/testsuite/systemtap.context/backtrace.tcl @@ -5,13 +5,7 @@ set m4 0 set m5 0 set m6 0 -if {[istarget ia64-*-*]} { - set retexp {.*return\>--\r\n 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} -} else { - set retexp {.*return\>--\r\n 0x[a-f0-9]+ : kretprobe_trampoline_holder[^\r\n]+\r\n} -} - -spawn stap $srcdir/$subdir/backtrace.stp +spawn stap -DMAXSTRINGLEN=256 $srcdir/$subdir/backtrace.stp #exp_internal 1 expect { -timeout 240 @@ -20,69 +14,61 @@ expect { exec echo 0 > /proc/stap_test_cmd exp_continue } - -re {^backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func3@[^\r\n]+\r\n} { + + #backtrace from yyy_func2 + -re {^backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func2@[^\r\n]+\r\n} { incr m1 expect { -timeout 5 - -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m1 == 1} {incr m1} - exp_continue - } -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m1 == 2} {incr m1} + if {$m1 == 1} {incr m1} exp_continue } -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m1 == 3} {incr m1} + if {$m1 == 2} {incr m1} } } exp_continue } - -re {.*---\r\nthe call stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { + -re {.*--- yyy_func2 ---\r\nthe stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { incr m2 expect { -timeout 5 - -re {.*call\>--\r\n 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m2 == 1} {incr m2} - exp_continue - } -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m2 == 2} {incr m2} + if {$m2 == 1} {incr m2} exp_continue } -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m2 == 3} {incr m2} + if {$m2 == 2} {incr m2} } } exp_continue } - -re {.*backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func4@[^\r\n]+\r\n} { + + #backtrace from yyy_func3 + -re {.*backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func3@[^\r\n]+\r\n} { incr m3 expect { -timeout 5 - -re {^Returning from: 0x[a-f0-9]+ : yyy_func4[^\[]+\[systemtap_test_module2\]\r\n} { + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { if {$m3 == 1} {incr m3} exp_continue - } - -re {^Returning to : 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m3 == 2} {incr m3} - exp_continue - } + } -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m3 == 3} {incr m3} + if {$m3 == 2} {incr m3} exp_continue } -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m3 == 4} {incr m3} + if {$m3 == 3} {incr m3} } } exp_continue } - -re {.*---\r\nthe return stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { + -re {.*--- yyy_func3 ---\r\nthe stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { incr m4 expect { -timeout 5 - -re $retexp { + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { if {$m4 == 1} {incr m4} exp_continue } @@ -96,58 +82,87 @@ expect { } exp_continue } - -re {.*backtrace from timer.profile:\r\n} { + + #backtrace from yyy_func4 + -re {.*backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func4@[^\r\n]+\r\n} { incr m5 expect { -timeout 5 - -re {^ 0x[a-f0-9]+[^\r\n]+\r\n} { + -re {^ 0x[a-f0-9]+ : yyy_func4[^\[]+\[systemtap_test_module2\]\r\n} { if {$m5 == 1} {incr m5} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m5 == 2} {incr m5} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m5 == 3} {incr m5} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m5 == 4} {incr m5} } } exp_continue } - -re {.*---\r\nthe profile stack is 0x[a-f0-9]+[^\r\n]+\r\n} { + -re {.*--- yyy_func4 ---\r\nthe stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { incr m6 expect { -timeout 5 - -re {.*profile>--\r\n 0x[a-f0-9]+[^\r\n]+\r\n} { + -re {^ 0x[a-f0-9]+ : yyy_func4[^\[]+\[systemtap_test_module2\]\r\n} { if {$m6 == 1} {incr m6} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m6 == 2} {incr m6} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m6 == 3} {incr m6} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m6 == 4} {incr m6} } } + exp_continue } - eof {fail "backtrace of yyy_func3, yyy_func4.return and timer.profile. unexpected EOF" } + eof {fail "backtrace of yyy_func[2-4]: unexpected EOF" } } exec kill -INT -[exp_pid] -if {$m1 == 4} { - pass "backtrace of yyy_func3" +if {$m1 == 3} { + pass "backtrace of yyy_func2" } else { - fail "backtrace of yyy_func3 ($m1)" + fail "backtrace of yyy_func2 ($m1)" } -if {$m2 == 4} { - pass "print_stack of yyy_func3" +if {$m2 == 3} { + pass "print_stack of yyy_func2" } else { - fail "print_stack of yyy_func3 ($m2)" + fail "print_stack of yyy_func2 ($m2)" } -if {$m3 == 5} { - pass "backtrace of yyy_func4.return" +if {$m3 == 4} { + pass "backtrace of yyy_func3" } else { - fail "backtrace of yyy_func4.return ($m3)" + fail "backtrace of yyy_func3 ($m3)" } if {$m4 == 4} { - pass "print_stack of yyy_func4.return" + pass "print_stack of yyy_func3" } else { - fail "print_stack of yyy_func4.return ($m4)" + fail "print_stack of yyy_func3 ($m4)" } -if {$m5 == 2} { - pass "backtrace of timer.profile" +if {$m5 == 5} { + pass "backtrace of yyy_func4" } else { - fail "backtrace of timer.profile ($m5)" + fail "backtrace of yyy_func4 ($m5)" } -if {$m6 == 2} { - pass "print_stack of timer.profile" +if {$m6 == 5} { + pass "print_stack of yyy_func4" } else { - fail "print_stack of timer.profile ($m6)" + fail "print_stack of yyy_func4 ($m6)" } + + close wait diff --git a/translate.cxx b/translate.cxx index ba848d43..1b812ec1 100644 --- a/translate.cxx +++ b/translate.cxx @@ -849,10 +849,6 @@ translator_output::line () void c_unparser::emit_common_header () { - vector<derived_probe_group*> g = all_session_groups (*session); - for (unsigned i=0; i<g.size(); i++) - g[i]->emit_module_header (*session); - o->newline(); o->newline() << "typedef char string_t[MAXSTRINGLEN];"; o->newline(); @@ -4363,83 +4359,127 @@ c_unparser::visit_hist_op (hist_op*) assert(false); } -int -emit_symbol_data (systemtap_session& s) + +static map< Dwarf_Addr, string> addrmap; + +#include <string.h> + +static int +kernel_filter (const char *module, const char *file __attribute__((unused))) { - int rc = 0; + return !strcmp(module,"kernel"); +} - // Instead of processing elf symbol tables, for now we just snatch - // /proc/kallsyms and convert it to our use. We need it sorted by - // address (so we can binary search) , and filtered (to show text - // symbols only), a task that we defer to grep(1) and sort(1). It - // may be useful to cache the symbols.sorted file, perhaps indexed - // by md5sum(/proc/modules), but let's not until this simple method - // proves too costly. LC_ALL=C is already set to avoid the - // excessive penalty of i18n code in some glibc/coreutils versions. - - string sorted_kallsyms = s.tmpdir + "/symbols.sorted"; - string sortcmd = "grep \" [AtT] \" /proc/kallsyms | "; - - if (s.symtab == false) +static int +get_symbols (Dwfl_Module *m, + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + void *arg __attribute__ ((unused))) +{ + int syments = dwfl_module_getsymtab(m); + assert(syments); + for (int i = 1; i < syments; ++i) { - s.op->newline() << "/* filled in by runtime */"; - s.op->newline() << "struct stap_symbol *stap_symbols;"; - s.op->newline() << "unsigned stap_num_symbols;\n"; - return 0; + GElf_Sym sym; + const char *name = dwfl_module_getsym(m, i, &sym, NULL); + if (name) { + if (GELF_ST_TYPE (sym.st_info) == STT_FUNC || + strcmp(name, "_etext") == 0 || + strcmp(name, "_stext") == 0 || + strcmp(name, "modules_op") == 0) + addrmap[sym.st_value] = name; + } } + return DWARF_CB_OK; +} - sortcmd += "sort "; -#if __LP64__ - sortcmd += "-k 1,16 "; -#else - sortcmd += "-k 1,8 "; -#endif - sortcmd += "-s -o " + sorted_kallsyms; - - if (s.verbose>1) clog << "Running " << sortcmd << endl; - rc = system(sortcmd.c_str()); - if (rc == 0) +int +emit_symbol_data_from_debuginfo(systemtap_session& s, ofstream& kallsyms_out) +{ + static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug"; + static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH"); + + static char *debuginfo_path = (debuginfo_env_arr ? + debuginfo_env_arr : debuginfo_path_arr); + + static const Dwfl_Callbacks kernel_callbacks = { - ifstream kallsyms (sorted_kallsyms.c_str()); - char kallsyms_outbuf [4096]; - ofstream kallsyms_out ((s.tmpdir + "/stap-symbols.h").c_str()); - kallsyms_out.rdbuf()->pubsetbuf (kallsyms_outbuf, - sizeof(kallsyms_outbuf)); + dwfl_linux_kernel_find_elf, + dwfl_standard_find_debuginfo, + dwfl_offline_section_address, + & debuginfo_path + }; + + Dwfl *dwfl = dwfl_begin (&kernel_callbacks); + if (!dwfl) + throw semantic_error ("cannot open dwfl"); + dwfl_report_begin (dwfl); - s.op->newline() << "\n\n#include \"stap-symbols.h\""; + int rc = dwfl_linux_kernel_report_offline (dwfl, + s.kernel_release.c_str(), + kernel_filter); + dwfl_report_end (dwfl, NULL, NULL); + if (rc < 0) + return rc; + + dwfl_getmodules (dwfl, &get_symbols, NULL, 0); + dwfl_end(dwfl); + + int i = 0; + map< Dwarf_Addr, string>::iterator pos; + kallsyms_out << "struct _stp_symbol _stp_kernel_symbols [] = {"; + for (pos = addrmap.begin(); pos != addrmap.end(); pos++) { + kallsyms_out << " { 0x" << hex << pos->first << ", " << "\"" << pos->second << "\" },\n"; + i++; + } - unsigned i=0; - kallsyms_out << "struct stap_symbol _stp_stap_symbols [] = {"; - string lastaddr; - while (! kallsyms.eof()) - { - string addr, type, sym, module; - kallsyms >> addr >> type >> sym; - kallsyms >> ws; - if (kallsyms.peek() == '[') - { - string bracketed; - kallsyms >> bracketed; - module = bracketed.substr (1, bracketed.length()-2); - } - - // NB: kallsyms includes some duplicate addresses - if ((type == "t" || type == "T" || type == "A") && lastaddr != addr) - { - kallsyms_out << " { 0x" << addr << ", " - << "\"" << sym << "\", " - << "\"" << module << "\" }," - << "\n"; - lastaddr = addr; - i ++; - } - } kallsyms_out << "};\n"; - kallsyms_out << "struct stap_symbol *stap_symbols = _stp_stap_symbols;"; - kallsyms_out << "unsigned stap_num_symbols = " << i << ";\n"; - } + kallsyms_out << "unsigned _stp_num_kernel_symbols = " << dec << i << ";\n"; + return i == 0; +} - return rc; +int +emit_symbol_data (systemtap_session& s) +{ + unsigned i=0; + char kallsyms_outbuf [4096]; + ofstream kallsyms_out ((s.tmpdir + "/stap-symbols.h").c_str()); + kallsyms_out.rdbuf()->pubsetbuf (kallsyms_outbuf, + sizeof(kallsyms_outbuf)); + s.op->newline() << "\n\n#include \"stap-symbols.h\""; + + // FIXME for non-debuginfo use. + if (true) { + return emit_symbol_data_from_debuginfo(s, kallsyms_out); + } else { + // For symbol-table only operation, we don't have debuginfo, + // so parse /proc/kallsyms. + + ifstream kallsyms("/proc/kallsyms"); + string lastaddr, modules_op_addr; + + kallsyms_out << "struct _stp_symbol _stp_kernel_symbols [] = {"; + while (! kallsyms.eof()) + { + string addr, type, sym; + kallsyms >> addr >> type >> sym >> ws; + + if (kallsyms.peek() == '[') + break; + + // NB: kallsyms includes some duplicate addresses + if ((type == "t" || type == "T" || type == "A" || sym == "modules_op") && lastaddr != addr) + { + kallsyms_out << " { 0x" << addr << ", " << "\"" << sym << "\" },\n"; + lastaddr = addr; + i ++; + } + } + kallsyms_out << "};\n"; + kallsyms_out << "unsigned _stp_num_kernel_symbols = " << i << ";\n"; + } + return (i == 0); } @@ -4525,7 +4565,7 @@ translate_pass (systemtap_session& s) s.op->newline() << "#include <linux/random.h>"; s.op->newline() << "#include <linux/utsname.h>"; s.op->newline() << "#include \"loc2c-runtime.h\" "; - + // XXX: old 2.6 kernel hack s.op->newline() << "#ifndef read_trylock"; s.op->newline() << "#define read_trylock(x) ({ read_lock(x); 1; })"; |