diff options
149 files changed, 5793 insertions, 2073 deletions
@@ -4,7 +4,6 @@ .#* autom4te.* cscope*out -Makefile config.h config.log config.status @@ -18,3 +17,13 @@ systemtap.spec testresults stapio stap_merge +CVS +.checkstyle +.cproject +.metadata +.project +.settings +SNAPSHOT +*.o +git_version.h +Makefile @@ -28,6 +28,248 @@ * hash.cxx, session.h, stap.1.in: Added --kelf, --kmap, --ignore-vmlinux, and --ignore-dwarf. * testsuite/{semok,semko}/nodwf*.stp +2008-05-05 Frank Ch. Eigler <fche@elastic.org> + + PR 444886. From <crquan@gmail.com>: + * tapsets.cxx, translate.cxx: Add .../build/... to default debuginfo + path, to ease search for hand-built kernels. + +2008-05-01 Frank Ch. Eigler <fche@elastic.org> + + PR 6474 + * configure.ac (--enable-pie): Add default option. + * Makefile.am (stap* binaries): Use -fpie/-z relro/-z now as + applicable. + * configure, aclocal.m4, Makefile.in, doc/Makefile.in: Regenerated. + +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + PR 6008 + * main.cxx (main): Increase the limitation of buffer size to 4095MB. + * staprun.8.in: Ditto. + +2008-04-29 Frank Ch. Eigler <fche@elastic.org> + + PR 6466 + * elaborate.cxx + (dead_stmtexpr_remover): Expand scope to kill far more + side-effect-free statemnets, including if/for/foreach. + (semantic_pass_opt4): Warn on elided function/probe bodies. + (typeresolution_info::visit_target_symbol): Dump parse tree of + resolution-challenged functions/probes. + (*): Adapt to probe->body being a statement* + rather than a block*. + * tapsets.cxx (*): Ditto. + * staptree.cxx (block::block ctor): New cons constructor. + * staptree.h: Corresponding changes. + +2008-04-29 David Smith <dsmith@redhat.com> + + * tapsets.cxx (utrace_derived_probe_group::emit_probe_decl): Added + death event handlers to ensure that for every utrace_attach there + is a corresponding utrace_detach. + (utrace_derived_probe_group::emit_module_decls): Ditto. + +2008-04-28 Frank Ch. Eigler <fche@elastic.org> + + * translate.cxx (translate_pass): Don't #define TEST_MODE. + +2008-04-26 Frank Ch. Eigler <fche@elastic.org> + + * tapsets.cxx (common_probe_entryfn_prologue): Undo + clear of overload-related context vars; add explanation why. + +2008-04-25 Frank Ch. Eigler <fche@elastic.org> + + * systemtap.spec.in: Simplify configuration defaults. + +2008-04-29 David Smith <dsmith@redhat.com>:ChangeLog + +2008-04-25 David Smith <dsmith@redhat.com> + + PR 6455. + * tapsets.cxx (mark_builder::build): Handles markers with no + format string. + +2008-04-24 Frank Ch. Eigler <fche@elastic.org> + + PR 6454. + * main.cxx (printscript): Avoid string truncation heuristics, and + also avoid plain probe::printsig. Hold nose and dig down into + raw location lists instead. + +2008-04-24 Will Cohen <wcohen@redhat.com> + + * aclocal.m4: Regenerated. + * Makefile.am (example/*): Moved to testsuite/systemtap.examples. + * Makefile.in: Regenerated. + +2008-04-23 Frank Ch. Eigler <fche@elastic.org> + + From: Srinivasa DS <srinivasa@in.ibm.com> + * tapsets.cxx (blacklisted_p): Blacklist more init/exit sections. + +2008-04-23 Frank Ch. Eigler <fche@elastic.org> + + * tapsets.cxx (common_probe_entryfn_prologue): Clear + overload-related context vars. + +2008-04-22 hunt <hunt@redhat.com> + + * staprun.8.in: Add documentation for -d option. + +2008-04-22 David Smith <dsmith@redhat.com> + + * tapsets.cxx (utrace_derived_probe_group::emit_module_decls): + Removed debug statements. + +2008-04-18 David Smith <dsmith@redhat.com> + + * tapsets.cxx (struct utrace_builder): Added exec probes. + (utrace_derived_probe_group::emit_probe_decl): Ditto. + (utrace_derived_probe_group::emit_module_decls): Ditto. + (register_standard_tapsets): Ditto. + * stapprobes.5.in: Added information about exec probes. + * NEWS: Added information about utrace probes. + + * stapprobes.5.in: Added information about utrace probes. + +2008-04-17 Josh Stone <joshua.i.stone@intel.com> + + * tapsets.cxx (build_blacklist): Fix regexps for atomics. + * vim/syntax/stap.vim: Recognize the 'limit' keyword and script arguments, + allow '$' in variable names, and highlight $target variables. + +2008-04-17 David Smith <dsmith@redhat.com> + + * tapsets.cxx (utrace_builder::build): Make sure that the PATH of + 'process("PATH")' probes is an absolute path. + (utrace_derived_probe_group::emit_module_decls): Made calls to + utrace probe handler functions conditional on which types of + utrace probes are going to be output. + +2008-04-16 Frank Ch. Eigler <fche@elastic.org> + + * tapsets.cxx (task_finder_derived_probe): Add dummy constructor + for old (RHEL4) gcc compatibility. + +2008-04-16 Frank Ch. Eigler <fche@elastic.org> + + PR 6417: From Srinivasa DS <srinivasa@in.ibm.com>: + * tapsets.cxx (build_blacklist): Extend. + +2008-04-15 David Smith <dsmith@redhat.com> + + * session.h (struct systemtap_session): Added utrace_derived_probe + group and task_finder_derived_probe_group members. + * elaborate.cxx (systemtap_session::systemtap_session): Added + initialization of utrace_derived_probes and + task_finder_derived_probes. + * tapsets.cxx (struct task_finder_derived_probe_group): New + derived_probe_group to handle task_finder framework. + (struct utrace_derived_probe_group): New derived_probe_group to + handle utrace probes. + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + PR 6405 cont'd. + * Makefile.am (AM_CFLAGS): Remove -Wshadow, as it triggers for + new stapio (modname global vs. dwfl headers). + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + PR 6405 + * buildrun.cxx (compile_pass): Add STAPCONF_MODULE_NSECTIONS. + +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 + emit_module_header virtual function. + * translate.cxx (c_unparser::emit_common_header): Calls each probe + group's emit_module_header function. + (translate_pass): Moved inclusion of linux/marker.h to + mark_derived_probe_group::emit_module_header(). + * tapsets.cxx (struct be_derived_probe_group): Added empty + 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_header): Moved kprobes + kernel check from emit_module_decls() to here. + (uprobe_derived_probe_group::emit_module_header): Moved uprobe + kernel check from emit_module_decls() to here. + (uprobe_derived_probe_group::emit_module_decls): Moved uprobe + kernel check to emit_module_header(). + (mark_derived_probe_group::emit_module_header): Moved marker + kernel check from emit_module_decls and translate_pass() to here. + (uprobe_derived_probe_group::emit_module_decls): Moved marker + kernel check to emit_module_header(). + +2008-04-10 Frank Ch. Eigler <fche@elastic.org> + + PR 2949. + * session.h (listing_mode): New field. + * main.cxx (main): Test it. Enjoy it. + (printscript): Do it. + (usage): Document it. + * stap.1.in, stapex.5.in: Ditto. + * elaborate.cxx (print_error): Disable error messages in listing mode. + +2008-04-10 Frank Ch. Eigler <fche@elastic.org> + + PR 6393 cont'd. + * Makefile.am: Also copy RadeonHD.am fragment to force + git_version.h regeneration at every make, and also special + tagging for "make dist". Thanks <ndim>. + +2008-04-10 Frank Ch. Eigler <fche@elastic.org> + + PR 6393. + * git_version.sh: New file, copied from radeonhd. + * configure.ac: No longer generate $builddir/SNAPSHOT. + * Makefile.am: Generate $builddir/git_version.h. + (EXTRA_DIST): Add git_version.h and git_version.sh. + * main.cxx (version): Print generated GIT_MESSAGE therefrom. + * Makefile.in, configure: Regenerated. + +2008-04-09 David Smith <dsmith@redhat.com> + + * .gitignore: Added more files to ignore. 2008-04-04 Masami Hiramatsu <mhiramat@redhat.com> @@ -36,6 +36,9 @@ the <systemtap@sources.redhat.com> mailing list. Some subdirectories have ChangeLog files of their own, so make sure you find the correct ones to prepend. + In the git commit message, make the first line an brief summary of + the patch. There is no need to transcribe the ChangeLog entries there. + - test suites As far as practicable, changes should be accompanied by test cases diff --git a/Makefile.am b/Makefile.am index 1fc4dbec..4eb1335b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,7 @@ pkglibexecdir = ${libexecdir}/${PACKAGE} AM_CPPFLAGS = -DBINDIR='"$(bindir)"' -DPKGDATADIR='"${pkgdatadir}"' -DPKGLIBDIR='"$(pkglibexecdir)"' -AM_CFLAGS = -D_GNU_SOURCE -fexceptions -Wall -Werror -Wshadow -Wunused -Wformat=2 -W +AM_CFLAGS = -D_GNU_SOURCE -fexceptions -Wall -Werror -Wunused -Wformat=2 -W AM_CXXFLAGS = -Wall -Werror dist_man_MANS = stap.1 stapprobes.5 stapfuncs.5 stapex.5 staprun.8 man/stapprobes.iosched.5 man/stapprobes.netdev.5 man/stapprobes.nfs.5 man/stapprobes.nfsd.5 man/stapprobes.pagefault.5 man/stapprobes.process.5 man/stapprobes.rpc.5 man/stapprobes.scsi.5 man/stapprobes.signal.5 man/stapprobes.socket.5 man/stapprobes.tcp.5 man/stapprobes.udp.5 @@ -18,11 +18,55 @@ stap_SOURCES = main.cxx \ cache.cxx util.cxx coveragedb.cxx stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@ +BUILT_SOURCES = +CLEANFILES = + +# Arrange for git_version.h to be regenerated at every "make". +# Code fragment is based upon RadeonHD.am. + +# The stamp file which is never created ensures that git_version.h is updated +# before every build. Having git_version.h in foo_SOURCES ensures a recompile +# 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 +CLEANFILES += git_version.h +GIT_VERSION_CMD = $(SHELL) $(top_srcdir)/git_version.sh +git_version.stamp: + @if test -f "$(srcdir)/git_version.h"; then \ + if test -f "git_version.h"; then :; \ + else \ + cp "$(srcdir)/git_version.h" "git_version.h"; \ + fi; \ + fi + $(GIT_VERSION_CMD) -k -s $(top_srcdir) -o git_version.h + @if test -s "$(srcdir)/git_version.h"; then \ + if cmp "$(srcdir)/git_version.h" "git_version.h"; then :; \ + else \ + echo "Error: $(srcdir)/git_version.h and git_version.h differ."; \ + echo " You probably want to remove the former."; \ + exit 1; \ + fi; \ + fi + +dist-gitversion: git_version.stamp + if test -f "git_version.h"; then \ + sed -e 's|^#undef GIT_IS_DIST.*|#define GIT_IS_DIST 1|' \ + "git_version.h" > "$(distdir)/git_version.h"; \ + fi + + +git_version.h: + $(srcdir)/git_version.sh -k --srcdir $(srcdir) -o git_version.h + + + stap_CXXFLAGS = $(AM_CXXFLAGS) stap_CPPFLAGS = $(AM_CPPFLAGS) -stap_LDFLAGS = $(AM_LDFLAGS) - -CLEANFILES = +stap_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@ +staprun_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@ +stapio_CPPFLAGS = $(AM_CPPFLAGS) +stapio_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@ if BUILD_ELFUTILS # This tells automake's "make distcheck" what we need to compile. @@ -31,7 +75,12 @@ 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) -BUILT_SOURCES = stamp-elfutils +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 $(MAKE) $(AM_MAKEFLAGS) -C build-elfutils all @@ -53,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 @@ -102,9 +150,13 @@ LDADD = EXTRA_DIST = buildrun.h elaborate.h loc2c.h session.h \ parse.h staptree.h tapsets.h translate.h \ cache.h hash.h mdfour.h util.h staplog.c coveragedb.h \ - examples testsuite systemtap.spec runtime tapset + testsuite systemtap.spec runtime tapset \ + git_version.h git_version.sh + +EXAMPLE_DEST_DIR = $(distdir)/examples +SAMPLE_DEST_DIR = $(EXAMPLE_DEST_DIR)/samples -SAMPLE_DEST_DIR = $(distdir)/examples/samples +EXAMPLE_SRC = $(srcdir)/testsuite/systemtap.examples SAMPLE_SRC = $(srcdir)/testsuite/systemtap.samples/iotask.stp \ $(srcdir)/testsuite/systemtap.samples/kmalloc-stacks.stp \ @@ -114,21 +166,27 @@ SAMPLE_SRC = $(srcdir)/testsuite/systemtap.samples/iotask.stp \ $(srcdir)/testsuite/systemtap.samples/tcp_connections.stp \ $(srcdir)/testsuite/systemtap.samples/topsys.stp +dist-add-examples: $(EXAMPLE_SRC) + rm -rf $(EXAMPLE_DEST_DIR) + mkdir -p $(EXAMPLE_DEST_DIR) + cp -a $(EXAMPLE_SRC)/* $(EXAMPLE_DEST_DIR)/. + # Copy some of the testsuite sample scripts to the distdir # 'examples/samples' directory. -dist-add-samples: $(SAMPLE_SRC) +dist-add-samples: $(SAMPLE_SRC) dist-add-examples rm -rf $(SAMPLE_DEST_DIR) mkdir -p $(SAMPLE_DEST_DIR) cp $(SAMPLE_SRC) $(SAMPLE_DEST_DIR) -dist-hook: dist-add-samples +dist-hook: dist-add-samples dist-gitversion find $(distdir) -name CVS -o -name '*~' -o -name '.#*' | xargs rm -rf find $(distdir) -name '*.o' -o -name '*.ko' -o -name '*.cmd' -o -name '*.mod.c' -o -name '.??*' | xargs rm -rf 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 e82d66a3..8ff882b4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -37,7 +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_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) @@ -102,18 +107,18 @@ 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) \ +staprun_LINK = $(CCLD) $(staprun_CFLAGS) $(CFLAGS) $(staprun_LDFLAGS) \ $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -204,6 +209,7 @@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIELDFLAGS = @PIELDFLAGS@ PROCFLAGS = @PROCFLAGS@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ @@ -266,7 +272,7 @@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = dist-bzip2 pkglibexecdir = ${libexecdir}/${PACKAGE} AM_CPPFLAGS = -DBINDIR='"$(bindir)"' -DPKGDATADIR='"${pkgdatadir}"' -DPKGLIBDIR='"$(pkglibexecdir)"' -AM_CFLAGS = -D_GNU_SOURCE -fexceptions -Wall -Werror -Wshadow -Wunused -Wformat=2 -W +AM_CFLAGS = -D_GNU_SOURCE -fexceptions -Wall -Werror -Wunused -Wformat=2 -W AM_CXXFLAGS = -Wall -Werror dist_man_MANS = stap.1 stapprobes.5 stapfuncs.5 stapex.5 staprun.8 man/stapprobes.iosched.5 man/stapprobes.netdev.5 man/stapprobes.nfs.5 man/stapprobes.nfsd.5 man/stapprobes.pagefault.5 man/stapprobes.process.5 man/stapprobes.rpc.5 man/stapprobes.scsi.5 man/stapprobes.signal.5 man/stapprobes.socket.5 man/stapprobes.tcp.5 man/stapprobes.udp.5 stap_SOURCES = main.cxx \ @@ -275,29 +281,42 @@ stap_SOURCES = main.cxx \ cache.cxx util.cxx coveragedb.cxx stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@ + +# Arrange for git_version.h to be regenerated at every "make". +# Code fragment is based upon RadeonHD.am. + +# The stamp file which is never created ensures that git_version.h is updated +# before every build. Having git_version.h in foo_SOURCES ensures a recompile +# 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_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) -CLEANFILES = $(am__append_3) $(pkglibexec_PROGRAMS) +stap_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@ $(am__append_2) +staprun_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@ +stapio_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_3) +stapio_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@ $(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@BUILT_SOURCES = stamp-elfutils @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) @@ -311,9 +330,12 @@ LDADD = EXTRA_DIST = buildrun.h elaborate.h loc2c.h session.h \ parse.h staptree.h tapsets.h translate.h \ cache.h hash.h mdfour.h util.h staplog.c coveragedb.h \ - examples testsuite systemtap.spec runtime tapset + testsuite systemtap.spec runtime tapset \ + git_version.h git_version.sh -SAMPLE_DEST_DIR = $(distdir)/examples/samples +EXAMPLE_DEST_DIR = $(distdir)/examples +SAMPLE_DEST_DIR = $(EXAMPLE_DEST_DIR)/samples +EXAMPLE_SRC = $(srcdir)/testsuite/systemtap.examples SAMPLE_SRC = $(srcdir)/testsuite/systemtap.samples/iotask.stp \ $(srcdir)/testsuite/systemtap.samples/kmalloc-stacks.stp \ $(srcdir)/testsuite/systemtap.samples/kmalloc-top \ @@ -505,12 +527,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 $@ $< @@ -583,88 +605,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 @@ -736,20 +772,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 @@ -1458,6 +1480,31 @@ uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8 uninstall-man uninstall-man1 uninstall-man5 uninstall-man8 \ uninstall-pkglibexecPROGRAMS +git_version.stamp: + @if test -f "$(srcdir)/git_version.h"; then \ + if test -f "git_version.h"; then :; \ + else \ + cp "$(srcdir)/git_version.h" "git_version.h"; \ + fi; \ + fi + $(GIT_VERSION_CMD) -k -s $(top_srcdir) -o git_version.h + @if test -s "$(srcdir)/git_version.h"; then \ + if cmp "$(srcdir)/git_version.h" "git_version.h"; then :; \ + else \ + echo "Error: $(srcdir)/git_version.h and git_version.h differ."; \ + echo " You probably want to remove the former."; \ + exit 1; \ + fi; \ + fi + +dist-gitversion: git_version.stamp + if test -f "git_version.h"; then \ + sed -e 's|^#undef GIT_IS_DIST.*|#define GIT_IS_DIST 1|' \ + "git_version.h" > "$(distdir)/git_version.h"; \ + fi + +git_version.h: + $(srcdir)/git_version.sh -k --srcdir $(srcdir) -o git_version.h @BUILD_ELFUTILS_TRUE@stamp-elfutils: config.status @BUILD_ELFUTILS_TRUE@ $(MAKE) $(AM_MAKEFLAGS) -C build-elfutils all @BUILD_ELFUTILS_TRUE@ for dir in libelf libebl libdw libdwfl backends; do \ @@ -1485,21 +1532,27 @@ install-exec-hook: @BUILD_CRASHMOD_TRUE@ $(MKDIR_P) $(DESTDIR)$(pkglibdir) @BUILD_CRASHMOD_TRUE@ $(INSTALL) $(STAPLOG) $(DESTDIR)$(pkglibdir) +dist-add-examples: $(EXAMPLE_SRC) + rm -rf $(EXAMPLE_DEST_DIR) + mkdir -p $(EXAMPLE_DEST_DIR) + cp -a $(EXAMPLE_SRC)/* $(EXAMPLE_DEST_DIR)/. + # Copy some of the testsuite sample scripts to the distdir # 'examples/samples' directory. -dist-add-samples: $(SAMPLE_SRC) +dist-add-samples: $(SAMPLE_SRC) dist-add-examples rm -rf $(SAMPLE_DEST_DIR) mkdir -p $(SAMPLE_DEST_DIR) cp $(SAMPLE_SRC) $(SAMPLE_DEST_DIR) -dist-hook: dist-add-samples +dist-hook: dist-add-samples dist-gitversion find $(distdir) -name CVS -o -name '*~' -o -name '.#*' | xargs rm -rf find $(distdir) -name '*.o' -o -name '*.ko' -o -name '*.cmd' -o -name '*.mod.c' -o -name '.??*' | xargs rm -rf 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,3 +1,31 @@ +* 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 + vm.mmap + vm.munmap + vm.oom_kill + vm.pagefault + vm.write_shared + +- More user-space probe types are added: + + probe process(PID).clone { } + probe process("PATH").clone { } + probe process(PID).exec { } + probe process("PATH").exec { } + probe process(PID).death { } + probe process("PATH").death { } + probe process(PID).syscall { } + probe process("PATH").syscall { } + probe process(PID).syscall.return { } + probe process("PATH").syscall.return { } + * What's new in version 0.6 - A copy of the systemtap tutorial and language reference guide diff --git a/buildrun.cxx b/buildrun.cxx index f3e72272..76efe7c0 100644 --- a/buildrun.cxx +++ b/buildrun.cxx @@ -93,9 +93,9 @@ compile_pass (systemtap_session& s) o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-tsc-khz.c, -DSTAPCONF_TSC_KHZ,)" << endl; o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-ktime-get-real.c, -DSTAPCONF_KTIME_GET_REAL,)" << endl; o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-x86-uniregs.c, -DSTAPCONF_X86_UNIREGS,)" << endl; - o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-nameidata.c, -DSTAPCONF_NAMEIDATA_CLEANUP,)" << endl; o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-unregister-kprobes.c, -DSTAPCONF_UNREGISTER_KPROBES,)" << endl; + o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-module-nsections.c, -DSTAPCONF_MODULE_NSECTIONS,)" << endl; for (unsigned i=0; i<s.macros.size(); i++) o << "EXTRA_CFLAGS += -D " << lex_cast_qstring(s.macros[i]) << endl; @@ -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) + " "; @@ -705,6 +705,7 @@ EGREP U ANSI2KNR RANLIB +PIELDFLAGS sqlite3_LIBS staplog_CPPFLAGS BUILD_CRASHMOD_TRUE @@ -1324,6 +1325,7 @@ Optional Features: location). --enable-prologues make -P prologue-searching default --disable-ssp disable gcc stack-protector + --disable-pie disable position-independent-executable --enable-sqlite build with sqlite support --enable-crash[=DIRECTORY] enable crash extension (default is disabled). @@ -6007,6 +6009,67 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi +# Check whether --enable-pie was given. +if test "${enable_pie+set}" = set; then + enableval=$enable_pie; +fi + +if test "x$enable_pie" != xno; then + + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LDFLAGS="$LDFLAGS" + CFLAGS="$CFLAGS -fpie" + CXXFLAGS="$CXXFLAGS -fpie" + LDFLAGS="$LDFLAGS -pie -Wl,-z,relro -Wl,-z,now" + cat >conftest.$ac_ext <<_ACEOF +void main () {} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: Compiling with gcc pie et al." >&5 +echo "$as_me: Compiling with gcc pie et al." >&6;} + # LDFLAGS is special since it may be passed down to bundled-elfutils, + # and interfere with the .so's built therein + PIELDFLAGS="$LDFLAGS -pie -Wl,-z,relro -Wl,-z,now" + LDFLAGS="$save_LDFLAGS" + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: Compiler does not support -pie et al." >&5 +echo "$as_me: Compiler does not support -pie et al." >&6;} + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + PIELDFLAGS="" + LDFLAGS="$save_LDFLAGS" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi + + + # Check whether --enable-sqlite was given. if test "${enable_sqlite+set}" = set; then enableval=$enable_sqlite; @@ -6812,13 +6875,6 @@ cap_LIBS="$LIBS" LIBS="$SAVE_LIBS" CFLAGS="$SAVE_CFLAGS" -if test -d $srcdir/.git -a ! -f $srcdir/SNAPSHOT; then - snapshot=`cd $srcdir; git-rev-list --abbrev-commit --max-count=1 HEAD` - echo $snapshot > SNAPSHOT - { echo "$as_me:$LINENO: Created git SNAPSHOT $snapshot" >&5 -echo "$as_me: Created git SNAPSHOT $snapshot" >&6;} -fi - ac_config_headers="$ac_config_headers config.h:config.in" ac_config_files="$ac_config_files Makefile doc/Makefile systemtap.spec stap.1 stapprobes.5 stapfuncs.5 stapex.5 staprun.8 man/stapprobes.iosched.5 man/stapprobes.netdev.5 man/stapprobes.nfs.5 man/stapprobes.nfsd.5 man/stapprobes.pagefault.5 man/stapprobes.process.5 man/stapprobes.rpc.5 man/stapprobes.scsi.5 man/stapprobes.signal.5 man/stapprobes.socket.5 man/stapprobes.tcp.5 man/stapprobes.udp.5" @@ -7620,12 +7676,12 @@ EGREP!$EGREP$ac_delim U!$U$ac_delim ANSI2KNR!$ANSI2KNR$ac_delim RANLIB!$RANLIB$ac_delim +PIELDFLAGS!$PIELDFLAGS$ac_delim sqlite3_LIBS!$sqlite3_LIBS$ac_delim staplog_CPPFLAGS!$staplog_CPPFLAGS$ac_delim BUILD_CRASHMOD_TRUE!$BUILD_CRASHMOD_TRUE$ac_delim BUILD_CRASHMOD_FALSE!$BUILD_CRASHMOD_FALSE$ac_delim have_latex!$have_latex$ac_delim -have_dvips!$have_dvips$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then @@ -7667,6 +7723,7 @@ _ACEOF ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF +have_dvips!$have_dvips$ac_delim have_ps2pdf!$have_ps2pdf$ac_delim have_latex2html!$have_latex2html$ac_delim BUILD_DOCS_TRUE!$BUILD_DOCS_TRUE$ac_delim @@ -7683,7 +7740,7 @@ LIBOBJS!$LIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 14; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 15; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 @@ -8084,21 +8141,22 @@ echo "$as_me: $ac_file is unchanged" >&6;} fi rm -f "$tmp/out12" # Compute $ac_file's index in $config_headers. +_am_arg=$ac_file _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in - $ac_file | $ac_file:* ) + $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done -echo "timestamp for $ac_file" >`$as_dirname -- $ac_file || -$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X$ac_file : 'X\(//\)[^/]' \| \ - X$ac_file : 'X\(//\)$' \| \ - X$ac_file : 'X\(/\)' \| . 2>/dev/null || -echo X$ac_file | +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -8135,7 +8193,7 @@ echo "$as_me: executing $ac_file commands" >&6;} # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ diff --git a/configure.ac b/configure.ac index fa14516c..4d0a4263 100644 --- a/configure.ac +++ b/configure.ac @@ -81,6 +81,29 @@ AS_IF([test "x$enable_ssp" != xno],[ CFLAGS="$save_CFLAGS" CXXFLAGS="$save_CXXFLAGS"])]) +AC_ARG_ENABLE([pie], + [AS_HELP_STRING([--disable-pie], [disable position-independent-executable])]) +AS_IF([test "x$enable_pie" != xno],[ + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LDFLAGS="$LDFLAGS" + CFLAGS="$CFLAGS -fpie" + CXXFLAGS="$CXXFLAGS -fpie" + LDFLAGS="$LDFLAGS -pie -Wl,-z,relro -Wl,-z,now" + AC_LINK_IFELSE([void main () {}], [ + AC_MSG_NOTICE([Compiling with gcc pie et al.]) + # LDFLAGS is special since it may be passed down to bundled-elfutils, + # and interfere with the .so's built therein + PIELDFLAGS="$LDFLAGS -pie -Wl,-z,relro -Wl,-z,now" + LDFLAGS="$save_LDFLAGS" + ],[ + AC_MSG_NOTICE([Compiler does not support -pie et al.]) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + PIELDFLAGS="" + LDFLAGS="$save_LDFLAGS"])]) +AC_SUBST(PIELDFLAGS) + dnl Handle optional sqlite support. If enabled/disabled by the user, dnl do the right thing. If not specified by the user, use it if dnl present. @@ -204,13 +227,6 @@ AC_SUBST(cap_LIBS) LIBS="$SAVE_LIBS" CFLAGS="$SAVE_CFLAGS" -dnl Create SNAPSHOT file from git commit id if possible -if test -d $srcdir/.git -a ! -f $srcdir/SNAPSHOT; then - snapshot=`cd $srcdir; git-rev-list --abbrev-commit --max-count=1 HEAD` - echo $snapshot > SNAPSHOT - AC_MSG_NOTICE([Created git SNAPSHOT $snapshot]) -fi - AC_CONFIG_HEADERS([config.h:config.in]) AC_CONFIG_FILES(Makefile doc/Makefile systemtap.spec stap.1 stapprobes.5 stapfuncs.5 stapex.5 staprun.8 man/stapprobes.iosched.5 man/stapprobes.netdev.5 man/stapprobes.nfs.5 man/stapprobes.nfsd.5 man/stapprobes.pagefault.5 man/stapprobes.process.5 man/stapprobes.rpc.5 man/stapprobes.scsi.5 man/stapprobes.signal.5 man/stapprobes.socket.5 man/stapprobes.tcp.5 man/stapprobes.udp.5) AC_CONFIG_SUBDIRS(testsuite) diff --git a/cscope.files b/cscope.files index e4f44b2c..d2d0e462 100644 --- a/cscope.files +++ b/cscope.files @@ -8,6 +8,7 @@ ./coveragedb.h ./elaborate.cxx ./elaborate.h +./git_version.h ./hash.cxx ./hash.h ./loc2c.c @@ -20,11 +21,19 @@ ./parse.h ./runtime/alloc.c ./runtime/arith.c +./runtime/autoconf-constant-tsc.c ./runtime/autoconf-hrtimer-rel.c ./runtime/autoconf-inode-private.c +./runtime/autoconf-ktime-get-real.c +./runtime/autoconf-module-nsections.c +./runtime/autoconf-nameidata.c +./runtime/autoconf-tsc-khz.c +./runtime/autoconf-unregister-kprobes.c +./runtime/autoconf-x86-uniregs.c ./runtime/bench2/itest.c ./runtime/copy.c ./runtime/counter.c +./runtime/debug.h ./runtime/docs/examples/argv.c ./runtime/docs/examples/foreach.c ./runtime/docs/examples/list.c @@ -36,6 +45,7 @@ ./runtime/map-gen.c ./runtime/map.h ./runtime/map-stat.c +./runtime/mempool.c ./runtime/perf.c ./runtime/perf.h ./runtime/pmap-gen.c @@ -61,6 +71,7 @@ ./runtime/probes/tasklet/stp_tasklet.c ./runtime/probes/test4/test4.c ./runtime/probes/where_func/kprobe_where_funct.c +./runtime/procfs.c ./runtime/regs.c ./runtime/regs.h ./runtime/regs-ia64.c @@ -72,14 +83,19 @@ ./runtime/stack-ppc64.c ./runtime/stack-s390.c ./runtime/stack-x86_64.c +./runtime/staprun/cap.c +./runtime/staprun/common.c ./runtime/staprun/ctl.c ./runtime/staprun/mainloop.c ./runtime/staprun/relay.c ./runtime/staprun/relay_old.c +./runtime/staprun/stapio.c ./runtime/staprun/stap_merge.c ./runtime/staprun/staprun.c +./runtime/staprun/staprun_funcs.c ./runtime/staprun/staprun.h ./runtime/staprun/symbols.c +./runtime/staprun/unwind_data.c ./runtime/stat.c ./runtime/stat-common.c ./runtime/stat.h @@ -87,6 +103,7 @@ ./runtime/string.h ./runtime/sym.c ./runtime/sym.h +./runtime/task_finder.c ./runtime/tests/agg/count.c ./runtime/tests/agg/stats.c ./runtime/tests/maps/ii.c @@ -132,15 +149,27 @@ ./runtime/transport/transport_msgs.h ./runtime/transport/utt.c ./runtime/transport/utt.h -./runtime/user/alloc.c -./runtime/user/copy.c -./runtime/user/emul.h -./runtime/user/io.c -./runtime/user/print.c -./runtime/user/runtime.h -./runtime/user/test.h +./runtime/unwind.c +./runtime/unwind/i386.h +./runtime/unwind/unwind.h +./runtime/unwind/x86_64.h +./runtime/uprobes/uprobes_arch.c +./runtime/uprobes/uprobes_arch.h +./runtime/uprobes/uprobes.c +./runtime/uprobes/uprobes.h +./runtime/uprobes/uprobes_i386.c +./runtime/uprobes/uprobes_i386.h +./runtime/uprobes/uprobes_ppc64.c +./runtime/uprobes/uprobes_ppc64.h +./runtime/uprobes/uprobes_s390.c +./runtime/uprobes/uprobes_s390.h +./runtime/uprobes/uprobes_x86_64.c +./runtime/uprobes/uprobes_x86_64.h +./runtime/uprobes/uprobes_x86.c +./runtime/uprobes/uprobes_x86.h ./runtime/vsprintf.c ./session.h +./staplog.c ./staptree.cxx ./staptree.h ./tapsets.cxx diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..d8a93302 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,8 @@ +*.aux +*.glo +*.idx +*.log +*.lot +*.out +*.pdf +*.toc diff --git a/doc/ChangeLog b/doc/ChangeLog index e652078d..16a3c111 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,11 @@ +2008-04-24 Will Cohen <wcohen@redhat.com> + + * Makefile.in: Regenerated. + +2008-04-09 David Smith <dsmith@redhat.com> + + * .gitignore: New file. + 2008-03-25 Frank Ch. Eigler <fche@elastic.org> * langref.tex: Clarify utility of epilogue-type probe aliases. diff --git a/doc/Makefile.in b/doc/Makefile.in index e76f154d..a2700d3c 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -88,6 +88,7 @@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIELDFLAGS = @PIELDFLAGS@ PROCFLAGS = @PROCFLAGS@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ 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.cxx b/elaborate.cxx index 2d9fa7bc..306baff1 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -492,7 +492,6 @@ alias_expansion_builder // the token location of the alias, n->tok = location->tok; - n->body->tok = location->tok; // and statements representing the concatenation of the alias' // body with the use's. @@ -503,25 +502,9 @@ alias_expansion_builder // resulting variable. if (alias->epilogue_style) - { - for (unsigned i = 0; i < use->body->statements.size(); ++i) - n->body->statements.push_back - (deep_copy_visitor::deep_copy(use->body->statements[i])); - - for (unsigned i = 0; i < alias->body->statements.size(); ++i) - n->body->statements.push_back - (deep_copy_visitor::deep_copy(alias->body->statements[i])); - } + n->body = new block (use->body, alias->body); else - { - for (unsigned i = 0; i < alias->body->statements.size(); ++i) - n->body->statements.push_back - (deep_copy_visitor::deep_copy(alias->body->statements[i])); - - for (unsigned i = 0; i < use->body->statements.size(); ++i) - n->body->statements.push_back - (deep_copy_visitor::deep_copy(use->body->statements[i])); - } + n->body = new block (alias->body, use->body); derive_probes (sess, n, finished_results, location->optional); } @@ -1041,7 +1024,7 @@ semantic_pass_conditions (systemtap_session & sess) notex->tok = e->tok; notex->operand = e; ifs->condition = notex; - p->body->statements.insert (p->body->statements.begin(), ifs); + p->body = new block (ifs, p->body); } } @@ -1197,6 +1180,8 @@ systemtap_session::systemtap_session (): be_derived_probes(0), dwarf_derived_probes(0), uprobe_derived_probes(0), + utrace_derived_probes(0), + task_finder_derived_probes(0), timer_derived_probes(0), profile_derived_probes(0), mark_derived_probes(0), @@ -1214,6 +1199,9 @@ systemtap_session::print_error (const semantic_error& e) string message_str; stringstream message; + // NB: we don't print error messages during listing mode. + if (listing_mode) return; + message << "semantic error: " << e.what (); if (e.tok1 || e.tok2) message << ": "; @@ -1794,6 +1782,7 @@ struct dead_stmtexpr_remover: public traversing_visitor session(s), relaxed_p(r), current_stmt(0) {} void visit_block (block *s); + void visit_null_statement (null_statement *s); void visit_if_statement (if_statement* s); void visit_foreach_loop (foreach_loop *s); void visit_for_loop (for_loop *s); @@ -1804,15 +1793,44 @@ struct dead_stmtexpr_remover: public traversing_visitor void +dead_stmtexpr_remover::visit_null_statement (null_statement *s) +{ + // easy! + if (session.verbose>2) + clog << "Eliding side-effect-free null statement " << *s->tok << endl; + *current_stmt = 0; +} + + +void dead_stmtexpr_remover::visit_block (block *s) { - for (unsigned i=0; i<s->statements.size(); i++) + vector<statement*> new_stmts; + for (unsigned i=0; i<s->statements.size(); i++ ) { statement** last_stmt = current_stmt; current_stmt = & s->statements[i]; s->statements[i]->visit (this); + if (*current_stmt != 0) + new_stmts.push_back (*current_stmt); current_stmt = last_stmt; } + if (new_stmts.size() == 0) + { + if (session.verbose>2) + clog << "Eliding side-effect-free empty block " << *s->tok << endl; + *current_stmt = 0; + } + else if (new_stmts.size() == 1) + { + if (session.verbose>2) + clog << "Eliding side-effect-free singleton block " << *s->tok << endl; + *current_stmt = new_stmts[0]; + } + else + { + s->statements = new_stmts; + } } void @@ -1821,12 +1839,36 @@ dead_stmtexpr_remover::visit_if_statement (if_statement *s) statement** last_stmt = current_stmt; current_stmt = & s->thenblock; s->thenblock->visit (this); + if (s->elseblock) { current_stmt = & s->elseblock; s->elseblock->visit (this); + // null *current_stmt is OK here. } current_stmt = last_stmt; + + if (s->elseblock == 0 && s->thenblock == 0) + { + // We may be able to elide this statement, if the condition + // expression is side-effect-free. + varuse_collecting_visitor vct; + s->condition->visit(& vct); + if (vct.side_effect_free ()) + { + if (session.verbose>2) + clog << "Eliding side-effect-free if statement " << *s->tok << endl; + *current_stmt = 0; // yeah, baby + return; + } + } + + if (s->thenblock == 0) + { + // Can't elide this whole if/else statement; put a null in there. + s->thenblock = new null_statement(); + s->thenblock->tok = s->tok; + } } void @@ -1836,6 +1878,13 @@ dead_stmtexpr_remover::visit_foreach_loop (foreach_loop *s) current_stmt = & s->block; s->block->visit (this); current_stmt = last_stmt; + + if (s->block == 0) + { + if (session.verbose>2) + clog << "Eliding side-effect-free foreach statement " << *s->tok << endl; + *current_stmt = 0; // yeah, baby + } } void @@ -1845,6 +1894,27 @@ dead_stmtexpr_remover::visit_for_loop (for_loop *s) current_stmt = & s->block; s->block->visit (this); current_stmt = last_stmt; + + if (s->block == 0) + { + // We may be able to elide this statement, if the condition + // expression is side-effect-free. + varuse_collecting_visitor vct; + if (s->init) s->init->visit(& vct); + s->cond->visit(& vct); + if (s->incr) s->incr->visit(& vct); + if (vct.side_effect_free ()) + { + if (session.verbose>2) + clog << "Eliding side-effect-free for statement " << *s->tok << endl; + *current_stmt = 0; // yeah, baby + return; + } + + // Can't elide this whole statement; put a null in there. + s->block = new null_statement(); + s->block->tok = s->tok; + } } @@ -1881,14 +1951,10 @@ dead_stmtexpr_remover::visit_expr_statement (expr_statement *s) clog << "Eliding side-effect-free expression " << *s->tok << endl; - null_statement* ns = new null_statement; - ns->tok = s->tok; - * current_stmt = ns; - // XXX: A null_statement carries more weight in the translator's - // output than a nonexistent statement. It might be nice to - // work a little harder and completely eliminate all traces of - // an elided statement. - + // NB: this 0 pointer is invalid to leave around for any length of + // time, but the parent parse tree objects above handle it. + * current_stmt = 0; + relaxed_p = false; } } @@ -1905,23 +1971,48 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p) for (unsigned i=0; i<s.probes.size(); i++) { + derived_probe* p = s.probes[i]; + duv.focal_vars.clear (); duv.focal_vars.insert (s.globals.begin(), s.globals.end()); - duv.focal_vars.insert (s.probes[i]->locals.begin(), - s.probes[i]->locals.end()); - s.probes[i]->body->visit (& duv); + duv.focal_vars.insert (p->locals.begin(), + p->locals.end()); + + duv.current_stmt = & p->body; + p->body->visit (& duv); + if (p->body == 0) + { + if (! s.suppress_warnings) + clog << "WARNING: side-effect-free probe '" << p->name << "' " + << *p->tok << endl; + + p->body = new null_statement(); + p->body->tok = p->tok; + } } for (unsigned i=0; i<s.functions.size(); i++) { + functiondecl* fn = s.functions[i]; duv.focal_vars.clear (); - duv.focal_vars.insert (s.functions[i]->locals.begin(), - s.functions[i]->locals.end()); - duv.focal_vars.insert (s.functions[i]->formal_args.begin(), - s.functions[i]->formal_args.end()); + duv.focal_vars.insert (fn->locals.begin(), + fn->locals.end()); + duv.focal_vars.insert (fn->formal_args.begin(), + fn->formal_args.end()); duv.focal_vars.insert (s.globals.begin(), s.globals.end()); - s.functions[i]->body->visit (& duv); + + duv.current_stmt = & fn->body; + fn->body->visit (& duv); + if (fn->body == 0) + { + if (! s.suppress_warnings) + clog << "WARNING: side-effect-free function '" << fn->name << "' " + << *fn->tok << endl; + + fn->body = new null_statement(); + fn->body->tok = fn->tok; + } } } @@ -2504,6 +2595,26 @@ typeresolution_info::visit_target_symbol (target_symbol* e) // later unused-expression-elimination pass didn't get rid of it // either. So we have a target symbol that is believed to be of // genuine use, yet unresolved by the provider. + + if (session.verbose > 2) + { + clog << "Resolution problem with "; + if (current_function) + { + clog << "function " << current_function->name << endl; + current_function->body->print (clog); + clog << endl; + } + else if (current_probe) + { + clog << "probe " << current_probe->name << endl; + current_probe->body->print (clog); + clog << endl; + } + else + clog << "other" << endl; + } + if (e->saved_conversion_error) throw (* (e->saved_conversion_error)); else diff --git a/elaborate.h b/elaborate.h index fc8fbbcb..30bf5bce 100644 --- a/elaborate.h +++ b/elaborate.h @@ -114,6 +114,8 @@ struct derived_probe: public probe derived_probe (probe* b, probe_point* l); probe* base; // the original parsed probe virtual probe* basest () { return base->basest(); } + // XXX: might be helpful for listing and stepwise expansion, but aliases/wildcards don't show up right + // virtual probe* almost_basest () { probe* bb = base->basest(); return (bb == base) ? this : bb; } virtual ~derived_probe () {} virtual void join_group (systemtap_session& s) = 0; virtual probe_point* sole_location () const; diff --git a/git_version.sh b/git_version.sh new file mode 100755 index 00000000..69eb0f24 --- /dev/null +++ b/git_version.sh @@ -0,0 +1,348 @@ +#!/bin/sh +# +# Generate some basic versioning information which can be piped to a header. +# +# Copyright (c) 2006-2007 Luc Verhaegen <libv@skynet.be> +# Copyright (C) 2007 Hans Ulrich Niedermann <hun@n-dimensional.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# This script is based on the one written for xf86-video-unichrome by +# Luc Verhaegen, but was rewritten almost completely by Hans Ulrich +# Niedermann. The script contains a few bug fixes from Egbert Eich, +# Matthias Hopf, Joerg Sonnenberger, and possibly others. +# +# The author thanks the nice people on #git for the assistance. +# +# Simple testing of this script: +# /sbin/busybox sh git_version.sh --example > moo.c \ +# && gcc -Wall -Wextra -Wno-unused -o moo moo.c \ +# && ./moo +# (bash should also do) +# +# For how to hook this up to your automake- and/or imake-based build +# system, best take a look at how the RadeonHD.am and/or RadeonHD.tmpl +# work in the xf86-video-radeonhd build system. For non-recursive make, +# you can probably make things a little bit simpler. +# +# KNOWN BUGS: +# * Uses hyphenated ("git-foo-bar") program names, which git upstream +# have declared deprecated. +# + +# Help messages +USAGE="[<option>...]" +LONG_USAGE="\ +Options: + -h, --help Print this help message. + + -k, --keep-if-no-repo Keep old output file if no git repo found. + -o, --output FILENAME Set output file name. + -q, --quiet Quiet output. + -s, --srcdir DIRNAME Set source tree dir name. + -x, --example Print complete example program." + +# The caller may have set these for us +SED="${SED-sed}" + +# Initialize +working_dir="$(pwd)" + +# Who am I? +self="$(basename "$0")" + +# Defaults +ifndef_symbol="GIT_VERSION_H" +outfile="-" +print_example=false +keep_if_no_repo=no +quiet=false +srcdir="$(pwd)" + +# Parse command line parameter, affecting defaults +while [ "x$1" != "x" ] +do + case "$1" in + -x|--example) + print_example=: + ;; + -o|--output) + if shift; then + outfile="$1" + if [ "x$outfile" = "x-" ]; then + : # keep default ifndef_symbol + else + ifndef_symbol=`basename "$outfile" | $SED 's|\.|_|g; s|[^A-Za-z0-9_]||g' | tr a-z A-Z` + fi + else + echo "$self: Fatal: \"$1\" option requires parameter." >&2 + exit 1 + fi + ;; + -q|--quiet) + quiet=: + ;; + -h|--help) + echo "Usage: ${self} $USAGE" + [ -n "$LONG_USAGE" ] && echo "$LONG_USAGE" + exit + ;; + -k|--keep-if-no-repo) + keep_if_no_repo=yes + ;; + -s|--srcdir) + if shift; then + if test -d "$1"; then + srcdir="$1" + else + echo "$self: Fatal: \"$1\" not a directory." >&2 + exit 1 + fi + else + echo "$self: Fatal: \"$1\" option requires directory parameter." >&2 + exit 1 + fi + ;; + *) + echo "$self: Fatal: Invalid command line paramenter: \"$1\"" >&2 + exit 1 + ;; + esac + shift +done + +# If not printing to stdout, redirect stdout to output file +rename_new_output=false +if [ "x$outfile" = "x-" ] +then + : # keep using stdout +else + exec 1> "${outfile}.new" +fi + +# Done with creating output files, so we can change to source dir +abs_srcdir="$(cd "$srcdir" && pwd)" +cd "$srcdir" + +# Write program header +cat<<EOF +/* + * Basic versioning gathered from the git repository. + * Automatically generated by $0. + */ + +#ifndef ${ifndef_symbol} +#define ${ifndef_symbol} 1 + +/* whether this is a dist tarball or not */ +#undef GIT_IS_DIST + +EOF + +# Detect git tools (should work with old and new git versions) +git_found=yes +for git_tool in git-symbolic-ref git-rev-parse git-diff-files git-diff-index git +do + if [ x`which $git_tool 2>/dev/null` = "x" ]; then + git_found="'$git_tool' not found" + break + fi +done + +# Determine git specific defines +unset git_errors ||: +if [ "x$git_found" = "xyes" ]; then + git_version=`git --version` + if [ "x$git_version" = "x" ]; then + git_errors="${git_errors+${git_errors}; }error running 'git --version'" + fi +fi + +git_repo=no +# "git-rev-parse --git-dir" since git-0.99.7 +git_repo_dir="$(git-rev-parse --git-dir 2> /dev/null || true)" +abs_repo_dir="$(cd "$git_repo_dir" && pwd)" +# Only accept the found git repo iff it is in our top srcdir, as determined +# by comparing absolute pathnames creaged by running pwd in the respective dir. +if [ "x$git_repo_dir" != "x" ] && [ "x${abs_repo_dir}" = "x${abs_srcdir}/.git" ]; then + git_repo=yes + if [ "x$git_found" = "xyes" ]; then + # git-1.4 and probably earlier understand "git-rev-parse HEAD" + git_shaid=`git-rev-parse HEAD | $SED -n 's/^\(.\{8\}\).*/\1/p'` + if [ "x$git_shaid" = "x" ]; then + git_errors="${git_errors+${git_errors}; }error running 'git-rev-parse HEAD'" + fi + # git-1.4 and probably earlier understand "git-symbolic-ref HEAD" + git_branch=`git-symbolic-ref HEAD | $SED -n 's|^refs/heads/||p'` + if [ "x$git_branch" = "x" ]; then + # This happens, is OK, and "(no branch)" is what "git branch" prints. + git_branch="(no branch)" + fi + git_dirty=yes + # git-1.4 does not understand "git-diff-files --quiet" + # git-1.4 does not understand "git-diff-index --cached --quiet HEAD" + if [ "x$(git-diff-files)" = "x" ] && [ "x$(git-diff-index --cached HEAD)" = "x" ]; then + git_dirty=no + fi + fi +fi + +# Write git specific defines +if [ "x$git_errors" = "x" ]; then + echo "/* No errors occured while running git */" + echo "#undef GIT_ERRORS" +else + echo "/* Some errors occured while running git */" + echo "#define GIT_ERRORS \"${git_errors}\"" +fi +echo "" + +if [ "x$git_found" = "xyes" ]; then + echo "/* git utilities found */" + echo "#undef GIT_NOT_FOUND" + echo "#define GIT_VERSION \"${git_version}\"" +else + echo "/* git utilities not found */" + echo "#define GIT_NOT_FOUND \"${git_found}\"" + echo "#undef GIT_VERSION" +fi +echo "" + +if [ "x$git_repo" = "xno" ]; then + echo "/* No git repo found, probably building from dist tarball */" + echo "#undef GIT_REPO" +else + echo "/* git repo found */" + echo "#define GIT_REPO 1" + echo "" + if [ "x$git_found" = "xyes" ]; then + echo "/* Git SHA ID of last commit */" + echo "#define GIT_SHAID \"${git_shaid}\"" + echo "" + + echo "/* Branch this tree is on */" + echo "#define GIT_BRANCH \"$git_branch\"" + echo "" + + # Any uncommitted changes we should know about? + # Or technically: Are the working tree or index dirty? + if [ "x$git_dirty" = "xno" ]; then + echo "/* SHA-ID uniquely defines the state of this code */" + echo "#undef GIT_DIRTY" + else + echo "/* Local changes might be breaking things */" + echo "#define GIT_DIRTY 1" + fi + fi +fi + +# Define a few immediately useful message strings +cat<<EOF + +/* Define GIT_MESSAGE such that + * printf("%s: built from %s", argv[0], GIT_MESSAGE); + * forms a proper sentence. + */ + +#ifdef GIT_DIRTY +# define GIT_DIRTY_MSG " + changes" +#else /* !GIT_DIRTY */ +# define GIT_DIRTY_MSG "" +#endif /* GIT_DIRTY */ + +#ifdef GIT_ERRORS +# define GIT_ERROR_MSG " with error: " GIT_ERRORS +#else /* !GIT_ERRORS */ +# define GIT_ERROR_MSG "" +#endif /* GIT_ERRORS */ + +#ifdef GIT_IS_DIST +# define GIT_DIST_MSG "dist of " +#else /* !GIT_IS_DIST */ +# define GIT_DIST_MSG "" +#endif /* GIT_IS_DIST */ + +#ifdef GIT_REPO +# ifdef GIT_NOT_FOUND +# define GIT_MESSAGE GIT_DIST_MSG "git sources without git: " GIT_NOT_FOUND +# else /* !GIT_NOT_FOUND */ +# define GIT_MESSAGE \\ + GIT_DIST_MSG \\ + "git branch " GIT_BRANCH ", " \\ + "commit " GIT_SHAID GIT_DIRTY_MSG \\ + GIT_ERROR_MSG +# endif /* GIT_NOT_FOUND */ +#else /* !GIT_REPO */ +# define GIT_MESSAGE GIT_DIST_MSG "non-git sources" GIT_ERROR_MSG +#endif /* GIT_REPO */ + +#endif /* ${ifndef_symbol} */ +EOF + +# Example program +if "$print_example" +then + cat<<EOF + +/* example program demonstrating the use of git_version.sh output */ +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +int main(int argc, char *argv[]) +{ + const char *const idx = strrchr(argv[0], '/'); + const char *const prog = (idx)?(idx+1):(argv[0]); +#ifdef PACKAGE_VERSION + printf("%s: version %s, built from %s\n", prog, PACKAGE_VERSION, GIT_MESSAGE); +#elif defined(GIT_USED) + printf("%s: built from %s\n", prog, GIT_MESSAGE); +#endif + return 0; +} +EOF +fi + +# Change back to working dir for the remaining output file manipulations. +cd "$working_dir" + +# If necessary, overwrite outdated output file with new one +if [ "x$outfile" != "x-" ] +then + if [ -f "$outfile" ]; then + if [ "x$keep_if_no_repo" = "xyes" ] && [ "x$git_repo" = "xno" ]; then + "$quiet" || echo "$self: Not a git repo, keeping existing $outfile" >&2 + rm -f "$outfile.new" + elif cmp "$outfile" "$outfile.new" > /dev/null; then + "$quiet" || echo "$self: Output is unchanged, keeping $outfile" >&2 + rm -f "$outfile.new" + else + echo "$self: Output has changed, updating $outfile" >&2 + mv -f "$outfile.new" "$outfile" + fi + else + echo "$self: Output is new file, creating $outfile" >&2 + mv -f "$outfile.new" "$outfile" + fi +fi + +# THE END. @@ -19,6 +19,7 @@ #include "cache.h" #include "util.h" #include "coveragedb.h" +#include "git_version.h" #include <iostream> #include <fstream> @@ -48,7 +49,7 @@ version () clog << "SystemTap translator/driver " << "(version " << VERSION << "/" << dwfl_version (NULL) - << " built " << DATE << ")" << endl + << " " << GIT_MESSAGE << ")" << endl << "Copyright (C) 2005-2008 Red Hat, Inc. and others" << endl << "This is free software; see the source for copying conditions." << endl; } @@ -65,9 +66,11 @@ usage (systemtap_session& s, int exitcode) << endl << " or: stap [options] -e SCRIPT Run given script." << endl + << " or: stap [options] -l PROBE List matching probes." + << endl << endl << "Options:" << endl - << " -- no more options after this" << endl + << " -- end of translator options, script options follow" << endl << " -v increase verbosity [" << s.verbose << "]" << endl << " -h show help" << endl << " -V show version" << endl @@ -102,6 +105,17 @@ usage (systemtap_session& s, int exitcode) << " -c CMD start the probes, run CMD, and exit when it finishes" << endl << " -x PID sets target() to PID" << endl + << " -d OBJECT add unwind/symbol data for OBJECT file"; + if (s.unwindsym_modules.size() == 0) + clog << endl; + else + clog << ", in addition to" << endl; + { + vector<string> syms (s.unwindsym_modules.begin(), s.unwindsym_modules.end()); + for (unsigned i=0; i<syms.size(); i++) + clog << " " << syms[i] << endl; + } + clog << " -t collect probe timing information" << endl #ifdef HAVE_LIBSQLITE3 << " -q generate information on tapset coverage" << endl @@ -124,75 +138,122 @@ usage (systemtap_session& s, int exitcode) static void printscript(systemtap_session& s, ostream& o) { - if (s.embeds.size() > 0) - o << "# global embedded code" << endl; - for (unsigned i=0; i<s.embeds.size(); i++) + if (s.listing_mode) { - embeddedcode* ec = s.embeds[i]; - ec->print (o); - o << endl; - } + // We go through some heroic measures to produce clean output. + set<string> seen; - if (s.globals.size() > 0) - o << "# globals" << endl; - for (unsigned i=0; i<s.globals.size(); i++) - { - vardecl* v = s.globals[i]; - v->printsig (o); - if (s.verbose && v->init) + for (unsigned i=0; i<s.probes.size(); i++) { - o << " = "; - v->init->print(o); + derived_probe* p = s.probes[i]; + // NB: p->basest() is not so interesting; + // p->almost_basest() doesn't quite work, so ... + vector<probe*> chain; + p->collect_derivation_chain (chain); + probe* second = (chain.size()>1) ? chain[chain.size()-2] : chain[0]; + + #if 0 + cerr << "\tchain[" << chain.size() << "]:" << endl; + for (unsigned i=0; i<chain.size(); i++) + { cerr << "\t"; chain[i]->printsig(cerr); cerr << endl; } + #endif + + stringstream tmps; + const probe_alias *a = second->get_alias(); + if (a) + { + assert (a->alias_names.size() >= 1); + a->alias_names[0]->print(tmps); // XXX: [0] is arbitrary; perhaps print all + } + else + { + assert (second->locations.size() >= 1); + second->locations[0]->print(tmps); // XXX: [0] is less arbitrary here, but still ... + } + string pp = tmps.str(); + + // Now duplicate-eliminate. An alias may have expanded to + // several actual derived probe points, but we only want to + // print the alias head name once. + if (seen.find (pp) == seen.end()) + { + o << pp << endl; + seen.insert (pp); + } } - o << endl; } - - if (s.functions.size() > 0) - o << "# functions" << endl; - for (unsigned i=0; i<s.functions.size(); i++) + else { - functiondecl* f = s.functions[i]; - f->printsig (o); - o << endl; - if (f->locals.size() > 0) - o << " # locals" << endl; - for (unsigned j=0; j<f->locals.size(); j++) + if (s.embeds.size() > 0) + o << "# global embedded code" << endl; + for (unsigned i=0; i<s.embeds.size(); i++) { - vardecl* v = f->locals[j]; - o << " "; - v->printsig (o); - o << endl; - } - if (s.verbose) + embeddedcode* ec = s.embeds[i]; + ec->print (o); + o << endl; + } + + if (s.globals.size() > 0) + o << "# globals" << endl; + for (unsigned i=0; i<s.globals.size(); i++) { - f->body->print (o); - o << endl; - } - } - - if (s.probes.size() > 0) - o << "# probes" << endl; - for (unsigned i=0; i<s.probes.size(); i++) - { - derived_probe* p = s.probes[i]; - p->printsig (o); - o << endl; - if (p->locals.size() > 0) - o << " # locals" << endl; - for (unsigned j=0; j<p->locals.size(); j++) + vardecl* v = s.globals[i]; + v->printsig (o); + if (s.verbose && v->init) + { + o << " = "; + v->init->print(o); + } + o << endl; + } + + if (s.functions.size() > 0) + o << "# functions" << endl; + for (unsigned i=0; i<s.functions.size(); i++) { - vardecl* v = p->locals[j]; - o << " "; - v->printsig (o); - o << endl; - } - if (s.verbose) + functiondecl* f = s.functions[i]; + f->printsig (o); + o << endl; + if (f->locals.size() > 0) + o << " # locals" << endl; + for (unsigned j=0; j<f->locals.size(); j++) + { + vardecl* v = f->locals[j]; + o << " "; + v->printsig (o); + o << endl; + } + if (s.verbose) + { + f->body->print (o); + o << endl; + } + } + + if (s.probes.size() > 0) + o << "# probes" << endl; + for (unsigned i=0; i<s.probes.size(); i++) { - p->body->print (o); - o << endl; - } + derived_probe* p = s.probes[i]; + p->printsig (o); + o << endl; + if (p->locals.size() > 0) + o << " # locals" << endl; + for (unsigned j=0; j<p->locals.size(); j++) + { + vardecl* v = p->locals[j]; + o << " "; + v->printsig (o); + o << endl; + } + if (s.verbose) + { + p->body->print (o); + o << endl; + } + } + } } -} int pending_interrupts; @@ -232,6 +293,7 @@ main (int argc, char * const argv []) s.bulk_mode = false; s.unoptimized = false; s.suppress_warnings = false; + s.listing_mode = false; #ifdef ENABLE_PROLOGUES s.prologue_searching = true; @@ -318,7 +380,7 @@ main (int argc, char * const argv []) { "ignore-dwarf", 0, &long_opt, LONG_OPT_IGNORE_DWARF }, { NULL, 0, NULL, 0 } }; - int grc = getopt_long (argc, argv, "hVMvtp:I:e:o:R:r:m:kgPc:x:D:bs:uqw", + int grc = getopt_long (argc, argv, "hVMvtp:I:e:o:R:r:m:kgPc:x:D:bs:uqwl:d:", long_options, NULL); if (grc < 0) break; @@ -345,6 +407,11 @@ main (int argc, char * const argv []) break; case 'p': + if (s.listing_mode) + { + cerr << "Listing (-l) mode implies pass 2." << endl; + usage (s, 1); + } s.last_pass = atoi (optarg); if (s.last_pass < 1 || s.last_pass > 5) { @@ -357,6 +424,10 @@ main (int argc, char * const argv []) s.include_path.push_back (string (optarg)); break; + case 'd': + s.unwindsym_modules.insert (string (optarg)); + break; + case 'e': if (have_script) { @@ -450,9 +521,9 @@ main (int argc, char * const argv []) case 's': s.buffer_size = atoi (optarg); - if (s.buffer_size < 1 || s.buffer_size > 64) + if (s.buffer_size < 1 || s.buffer_size > 4095) { - cerr << "Invalid buffer size (should be 1-64)." << endl; + cerr << "Invalid buffer size (should be 1-4095)." << endl; usage (s, 1); } break; @@ -477,6 +548,18 @@ main (int argc, char * const argv []) usage (s, 0); break; + case 'l': + s.listing_mode = true; + s.last_pass = 2; + if (have_script) + { + cerr << "Only one script can be given on the command line." + << endl; + usage (s, 1); + } + cmdline_script = string("probe ") + string(optarg) + " {}"; + have_script = true; + case 0: switch (long_opt) { @@ -785,7 +868,7 @@ main (int argc, char * const argv []) // PASS 2: ELABORATION rc = semantic_pass (s); - if (rc == 0 && s.last_pass == 2) + if (s.listing_mode || (rc == 0 && s.last_pass == 2)) printscript(s, cout); times (& tms_after); @@ -834,7 +917,7 @@ main (int argc, char * const argv []) } } - if (rc || s.last_pass == 2 || pending_interrupts) goto cleanup; + if (rc || s.listing_mode || s.last_pass == 2 || pending_interrupts) goto cleanup; // PASS 3: TRANSLATION 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..8410b918 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,105 @@ +2008-05-05 Frank Ch. Eigler <fche@elastic.org> + + PR 6481. + * time.c (__stp_time_timer_callback): Reenable irq's before + mod_timer. + +2008-05-05 David Smith <dsmith@redhat.com> + + * task_finder.c (stap_utrace_detach_ops): Make sure we ignore + /sbin/init. + (__stp_utrace_attach): Added function to handle details of + attaching a utrace engine. + (__stp_utrace_task_finder_report_clone): Calls + __stp_utrace_attach. + (__stp_utrace_task_finder_report_exec): Ditto. + (stap_start_task_finder): Ditto. + +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + PR 5648 + From Shaohua Li <shaohua.li@intel.com> + * vsprintf.c (_stp_vsnprintf): Fix unaligned access warning on ia64. + +2008-04-29 David Smith <dsmith@redhat.com> + + * task_finder.c: Made more robust by ensuring that all utrace + attaches have a corresponding utrace detach. + +2008-04-28 Frank Ch. Eigler <fche@elastic.org> + + * runtime.h (TEST_MODE): Remove. + +2008-04-25 David Smith <dsmith@redhat.com> + + From Srinivasa <srinivasa@in.ibm.com> + * task_finder.c (__stp_get_mm_path): Fixed kernel 2.6.25 change. + +2008-04-24 David Smith <dsmith@redhat.com> + + * task_finder.c (__stp_get_mm_path): Made kernel 2.6.25 changes. + +2008-04-16 David Smith <dsmith@redhat.com> + + * task_finder.c (__stp_get_mm_path): Made kernel 2.6.18 changes. + +2008-04-15 David Smith <dsmith@redhat.com> + + PR 5961 (partial) + * task_finder.c (stap_start_task_finder): When an interesting + thread is found that is already running, make sure to set up + thread death notification. + +2008-04-15 hunt <hunt@redhat.com> + * print.c (_stp_pbuf_full): Delete. + +2008-04-15 hunt <hunt@redhat.com> + * stack-x86_64.c (_stp_stack_print_fallback): Add levels. + (__stp_stack_print): Count levels properly. + +2008-04-15 Martin Hunt <hunt@redhat.com> + + Finish support for limits on backtrace depth. + * runtime.h (MAXTRACE): Default to 20. + * stack.c (_stp_stack_print): Call __stp_stack_print + with levels set properly. + * sym.c (_stp_func_print): Return a value indicating + if something was printed. + + Support for i386 and x86_64 on 2.6.25 kernel + * unwind/i386.h: Support unified registers on 2.6.25. + Remove unused frame stuff, including STACK_*. + * stack-i386.c (__stp_stack_print): Support unified + registers on 2.6.25. + * regs.h (REG_FP): Define for i386. + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + PR 6410. + * unwind.c, unwind.h: Make body conditional in STP_USE_DWARF_UNWINDER. + * stack-x86_64.c (__stp_stack_print): Tolerate !unwinder. + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + PR 6405 + * autoconf-module-nsections.c: New file. + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + * unwind/i386.h (STACK_BOTTOM, STACK_TOP): Comment out these + unused definitions, for they collide with some kernels + (2.6.25-0.121.rc5.git4 rawhide). + +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 +108,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/autoconf-module-nsections.c b/runtime/autoconf-module-nsections.c new file mode 100644 index 00000000..c1ce58b7 --- /dev/null +++ b/runtime/autoconf-module-nsections.c @@ -0,0 +1,8 @@ +#include <linux/module.h> + +struct module_sect_attrs x; + +void foo (void) +{ + (void) x.nsections; +} 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..14a0820b 100644 --- a/runtime/print.c +++ b/runtime/print.c @@ -243,7 +243,6 @@ void _stp_print_char (const char c) pb->len ++; } - /* This function is used when printing maps or stats. */ /* Probably belongs elsewhere, but is here for now. */ /* It takes a format specification like those used for */ 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/regs.h b/runtime/regs.h index c1e2344b..4954020f 100644 --- a/runtime/regs.h +++ b/runtime/regs.h @@ -1,5 +1,5 @@ /* common register includes used in multiple modules - * Copyright (C) 2005 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 @@ -14,6 +14,7 @@ #if defined (STAPCONF_X86_UNIREGS) && (defined (__x86_64__) || defined (__i386__)) #define REG_IP(regs) regs->ip #define REG_SP(regs) regs->sp +#define REG_FP(regs) regs->bp; #elif defined (__x86_64__) @@ -24,6 +25,7 @@ #define REG_IP(regs) regs->eip #define REG_SP(regs) regs->esp +#define REG_FP(regs) regs->ebp; #elif defined (__ia64__) #define REG_IP(regs) ((regs)->cr_iip +ia64_psr(regs)->ri) diff --git a/runtime/runtime.h b/runtime/runtime.h index 318d3038..2711f531 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -58,25 +58,32 @@ static struct #define _stp_seq_inc() (atomic_inc_return(&_stp_seq.seq)) -/* TEST_MODE is always defined by systemtap */ -#ifdef TEST_MODE -#define SYSTEMTAP 1 -#else -#define MAXTRYLOCK 1000 -#define TRYLOCKDELAY 100 -#endif - #ifndef MAXSTRINGLEN #define MAXSTRINGLEN 128 #endif +#ifndef MAXTRACE +#define MAXTRACE 20 +#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..ad101889 100644 --- a/runtime/stack-i386.c +++ b/runtime/stack-i386.c @@ -8,40 +8,68 @@ * 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, int levels) +{ + unsigned long addr; + while (levels && _stp_valid_stack_ptr(context, stack)) { + if (unlikely(_stp_read_address(addr, (unsigned long *)stack, KERNEL_DS))) { + /* cannot access stack. give up. */ + return; + } + if (_stp_func_print(addr, verbose, 0)) + levels--; + 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 context = (unsigned long)®_SP(regs) & ~(THREAD_SIZE - 1); + +#ifdef STP_USE_FRAME_POINTER unsigned long addr; + unsigned long next_fp, fp = REG_FP(regs); -#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; + while (levels && _stp_valid_stack_ptr(context, (unsigned long)fp)) { + if (unlikely(_stp_read_address(addr, (unsigned long *)(fp + 4), KERNEL_DS))) { + /* cannot access stack. give up. */ + return; + } + _stp_func_print(addr, verbose, 1); + if (unlikely(_stp_read_address(next_fp, (unsigned long *)fp, KERNEL_DS))) { + /* cannot access stack. give up. */ + return; } + levels--; + + /* frame pointers move upwards */ + if (next_fp <= fp) + break; + fp = next_fp; } #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 (levels && !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); + levels--; + 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, levels); + break; } -#endif +#endif /* STP_USE_FRAME_POINTER */ } diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c index 186b2ad4..783e72bd 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,43 @@ * 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, int levels) { - unsigned long *stack = (unsigned long *)REG_SP(regs); unsigned long addr; + while (levels && stack & (THREAD_SIZE - 1)) { + if (unlikely(_stp_read_address(addr, (unsigned long *)stack, KERNEL_DS))) { + /* cannot access stack. give up. */ + return; + } + if (_stp_func_print(addr, verbose, 0)) + levels--; + stack++; + } +} + +static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels) +{ +#ifdef STP_USE_DWARF_UNWINDER + // 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 (levels && !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); + levels--; + 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, levels); + break; } +#else /* ! STP_USE_DWARF_UNWINDER */ + _stp_stack_print_fallback(REG_SP(regs), verbose); +#endif } diff --git a/runtime/stack.c b/runtime/stack.c index 9c01d65c..23ac2edc 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 @@ -46,7 +47,7 @@ * @param regs A pointer to the struct pt_regs. */ -void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instance *pi) +void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instance *pi, int levels) { if (verbose) { /* print the current address */ @@ -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, levels); } /** Writes stack backtrace to a string @@ -71,31 +75,30 @@ 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, int levels) { /* 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 */ /* and clear the print buffer. */ _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); _stp_print_flush(); - _stp_stack_print(regs, verbose, pi); + _stp_stack_print(regs, verbose, pi, levels); strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len); 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/ChangeLog b/runtime/staprun/ChangeLog index e9ef2e2d..969c299d 100644 --- a/runtime/staprun/ChangeLog +++ b/runtime/staprun/ChangeLog @@ -1,3 +1,60 @@ +2008-05-05 Martin Hunt <hunt@redhat.com> + + * mainloop.c (child_proc): Handle sig_chld + in the proper thread. + (signal_thread): Don't call send_request() + because it isn't thread-safe. + +2008-05-05 Martin Hunt <hunt@redhat.com> + + * mainloop.c (signal_thread): New thread to handle signals + better. + (setup_main_signals): Create signal thread. + +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + PR 6008 + * common.c (parse_args): Increase the limitation of the buffer size + to 4095MB. + * common.c (usage): Ditto. + +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + * stapio.c (main): Fix a typo in a debug message. + * staprun.c (main): Ditto. + +2008-04-24 Frank Ch. Eigler <fche@elastic.org> + + PR 6451. + * common.c (control_channel): Initialize to -1. + * ctl.c (close_ctl_channel): Tolerate fd=0. + +2008-04-22 Martin Hunt <hunt@redhat.com> + + * cap.c (init_cap): Detect capabilities failure and + run with them disabled. + +2008-04-22 Martin Hunt <hunt@redhat.com> + + * mainloop.c (send_request): Move here from common.c + staprun no longer send any messages. + +2008-04-22 hunt <hunt@redhat.com> + + * common.c (usage): Add -d option. + +2008-04-21 Martin Hunt <hunt@redhat.com> + + * staprun.c, stapio.c, staprun.h, mainloop.c, staprun_funcs.c, + ctl.c, common.c: Add "-d" option to have staprun remove + modules. Have staprun exec stapio and then have stapio + exec "staprun -d" to remove the module when finished. + +2008-04-16 Martin Hunt <hunt@redhat.com> + + * ctl.c (init_ctl_channel): Remove unused parameter. + Just opens one channel now. + 2008-02-21 David Smith <dsmith@redhat.com> * staprun_funcs.c (check_path): Small security fix. diff --git a/runtime/staprun/cap.c b/runtime/staprun/cap.c index 6f22dfc9..6ac6701f 100644 --- a/runtime/staprun/cap.c +++ b/runtime/staprun/cap.c @@ -23,6 +23,8 @@ #include "staprun.h" #include <sys/prctl.h> +static int _stp_no_caps = 0; + /* like perror, but exits */ #define ferror(msg) { \ _perr(msg); \ @@ -54,10 +56,10 @@ * CAP_CHOWN - allows chown */ -int init_cap(void) +void init_cap(void) { cap_t caps = cap_init(); - cap_value_t capv[] = {CAP_SYS_MODULE, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SETUID, CAP_SETGID, CAP_DAC_OVERRIDE}; + cap_value_t capv[] = { CAP_SYS_MODULE, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SETUID, CAP_SETGID, CAP_DAC_OVERRIDE }; const int numcaps = sizeof(capv) / sizeof(capv[0]); uid_t uid = getuid(); gid_t gid = getgid(); @@ -69,8 +71,11 @@ int init_cap(void) if (cap_set_flag(caps, CAP_PERMITTED, numcaps, capv, CAP_SET) < 0) ferror("cap_set_flag"); - if (cap_set_proc(caps) < 0) - ferror("cap_set_proc"); + if (cap_set_proc(caps) < 0) { + dbug(1, "Setting capabilities failed. Capabilities disabled.\n"); + _stp_no_caps = 1; + return; + } cap_free(caps); @@ -82,8 +87,6 @@ int init_cap(void) if (setresgid(gid, gid, gid) < 0) ferror("setresgid"); - - return 1; } void print_cap(char *text) @@ -97,19 +100,18 @@ void print_cap(char *text) perr("cap_get_proc"); return; } - + getresuid(&uid, &euid, &suid); getresgid(&gid, &egid, &sgid); printf("***** %s\n", text); - if ((p=prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0)) < 0) + if ((p = prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0)) < 0) perr("Couldn't get PR_SET_KEEPCAPS flag value"); - else + else printf("KEEPCAPS: %d\n", p); - printf("uid: %d, euid: %d, suid: %d\ngid: %d. egid: %d, sgid: %d\n", - uid, euid, suid, gid, egid, sgid ); + printf("uid: %d, euid: %d, suid: %d\ngid: %d. egid: %d, sgid: %d\n", uid, euid, suid, gid, egid, sgid); printf("Caps: %s\n", cap_to_text(caps, NULL)); cap_free(caps); printf("*****\n\n"); @@ -121,38 +123,44 @@ void print_cap(char *text) */ void drop_cap(cap_value_t cap) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_CLEAR) < 0) - ferror("Could not clear effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); + if (_stp_no_caps == 0) { + cap_t caps = cap_get_proc(); + if (caps == NULL) + ferror("cap_get_proc failed"); + if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_CLEAR) < 0) + ferror("Could not clear effective capabilities"); + if (cap_set_proc(caps) < 0) + ferror("Could not apply capability set"); + cap_free(caps); + } } /* add_cap() adds a permitted capability to the effective set. */ void add_cap(cap_value_t cap) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) < 0) - ferror("Could not set effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); + if (_stp_no_caps == 0) { + cap_t caps = cap_get_proc(); + if (caps == NULL) + ferror("cap_get_proc failed"); + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) < 0) + ferror("Could not set effective capabilities"); + if (cap_set_proc(caps) < 0) + ferror("Could not apply capability set"); + cap_free(caps); + } } /* del_cap() deletes a permitted capability from the effective set. */ void del_cap(cap_value_t cap) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR) < 0) - ferror("Could not clear effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); + if (_stp_no_caps == 0) { + cap_t caps = cap_get_proc(); + if (caps == NULL) + ferror("cap_get_proc failed"); + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR) < 0) + ferror("Could not clear effective capabilities"); + if (cap_set_proc(caps) < 0) + ferror("Could not apply capability set"); + cap_free(caps); + } } diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c index 47778efd..93da51d8 100644 --- a/runtime/staprun/common.c +++ b/runtime/staprun/common.c @@ -22,6 +22,7 @@ unsigned int buffer_size; char *target_cmd; char *outfile_name; int attach_mod; +int delete_mod; int load_only; int need_uprobes; @@ -30,8 +31,7 @@ char *modname = NULL; char *modpath = ""; char *modoptions[MAXMODOPTIONS]; -int initialized = 0; -int control_channel = 0; +int control_channel = -1; /* NB: fd==0 possible */ void parse_args(int argc, char **argv) { @@ -44,10 +44,11 @@ void parse_args(int argc, char **argv) target_cmd = NULL; outfile_name = NULL; attach_mod = 0; + delete_mod = 0; load_only = 0; need_uprobes = 0; - while ((c = getopt(argc, argv, "ALuvb:t:d:c:o:x:")) != EOF) { + while ((c = getopt(argc, argv, "ALuvb:t:dc:o:x:")) != EOF) { switch (c) { case 'u': need_uprobes = 1; @@ -57,8 +58,8 @@ void parse_args(int argc, char **argv) break; case 'b': buffer_size = (unsigned)atoi(optarg); - if (buffer_size < 1 || buffer_size > 64) { - err("Invalid buffer size '%d' (should be 1-64).\n", buffer_size); + if (buffer_size < 1 || buffer_size > 4095) { + err("Invalid buffer size '%d' (should be 1-4095).\n", buffer_size); usage(argv[0]); } break; @@ -67,7 +68,8 @@ void parse_args(int argc, char **argv) target_pid = atoi(optarg); break; case 'd': - /* obsolete internal option used by stap */ + /* delete module */ + delete_mod = 1; break; case 'c': target_cmd = optarg; @@ -128,11 +130,14 @@ void usage(char *prog) err("-o FILE Send output to FILE.\n"); err("-b buffer size The systemtap module specifies a buffer size.\n"); err(" Setting one here will override that value. The\n"); - err(" value should be an integer between 1 and 64\n"); + err(" value should be an integer between 1 and 4095 \n"); err(" which be assumed to be the buffer size in MB.\n"); err(" That value will be per-cpu in bulk mode.\n"); err("-L Load module and start probes, then detach.\n"); err("-A Attach to loaded systemtap module.\n"); + err("-d Delete a module. Only detached or unused modules\n"); + err(" the user has permission to access will be deleted. Use \"*\"\n"); + err(" (quoted) to delete all unused modules.\n"); err("MODULE can be either a module name or a module path. If a\n"); err("module name is used, it is looked for in the following\n"); err("directory: /lib/modules/`uname -r`/systemtap\n"); @@ -250,10 +255,7 @@ static void fatal_handler (int signum) rc = write (STDERR_FILENO, ERR_MSG, sizeof(ERR_MSG)); rc = write (STDERR_FILENO, str, strlen(str)); rc = write (STDERR_FILENO, "\n", 1); - if (initialized) - _exit(3); - else - _exit(1); + _exit(1); } void setup_signals(void) @@ -294,28 +296,6 @@ void setup_signals(void) #endif } -/** - * send_request - send request to kernel over control channel - * @type: the relay-app command id - * @data: pointer to the data to be sent - * @len: length of the data to be sent - * - * Returns 0 on success, negative otherwise. - */ -int send_request(int type, void *data, int len) -{ - char buf[1024]; - - /* Before doing memcpy, make sure 'buf' is big enough. */ - if ((len + 4) > (int)sizeof(buf)) { - _err("exceeded maximum send_request size.\n"); - return -1; - } - memcpy(buf, &type, 4); - memcpy(&buf[4], data, len); - return write(control_channel, buf, len+4); -} - /* * set FD_CLOEXEC for any file descriptor */ diff --git a/runtime/staprun/ctl.c b/runtime/staprun/ctl.c index af7e6c1a..4597bf72 100644 --- a/runtime/staprun/ctl.c +++ b/runtime/staprun/ctl.c @@ -12,45 +12,42 @@ #include "staprun.h" -int init_ctl_channel(int symbols) +int init_ctl_channel(const char *name, int verb) { - char *cname, buf[PATH_MAX]; + char buf[PATH_MAX]; struct statfs st; int old_transport = 0; - - if (symbols) - cname = ".symbols"; - else - cname = ".cmd"; - if (statfs("/sys/kernel/debug", &st) == 0 && (int) st.f_type == (int) DEBUGFS_MAGIC) { - if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s/%s", modname, cname)) + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) { + if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s/.cmd", name)) return -1; } else { old_transport = 1; - if (sprintf_chk(buf, "/proc/systemtap/%s/%s", modname, cname)) + if (sprintf_chk(buf, "/proc/systemtap/%s/.cmd", name)) return -1; } - - dbug(2, "Opening %s\n", buf); + + dbug(2, "Opening %s\n", buf); control_channel = open(buf, O_RDWR); if (control_channel < 0) { - if (attach_mod && errno == ENOENT) - err("ERROR: Can not attach. Module %s not running.\n", modname); - else - perr("Couldn't open control channel '%s'", buf); + if (verb) { + if (attach_mod && errno == ENOENT) + err("ERROR: Can not attach. Module %s not running.\n", name); + else + perr("Couldn't open control channel '%s'", buf); + } return -1; } if (set_clexec(control_channel) < 0) return -1; - + return old_transport; } void close_ctl_channel(void) { - if (control_channel > 0) { + if (control_channel >= 0) { close(control_channel); - control_channel = 0; + control_channel = -1; } } diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index 2e0c3c5c..61963743 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -15,39 +15,98 @@ /* globals */ int ncpus; -int use_old_transport = 0; +static int use_old_transport = 0; +//enum _stp_sig_type { sig_none, sig_done, sig_detach }; +//static enum _stp_sig_type got_signal = sig_none; -static void sigproc(int signum) +/** + * send_request - send request to kernel over control channel + * @type: the relay-app command id + * @data: pointer to the data to be sent + * @len: length of the data to be sent + * + * Returns 0 on success, negative otherwise. + */ +int send_request(int type, void *data, int len) { - dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); - - if (signum == SIGCHLD) { - pid_t pid = waitpid(-1, NULL, WNOHANG); - if (pid != target_pid) - return; - send_request(STP_EXIT, NULL, 0); - } else if (signum == SIGQUIT) - cleanup_and_exit(2); - else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) - send_request(STP_EXIT, NULL, 0); + char buf[1024]; + + /* Before doing memcpy, make sure 'buf' is big enough. */ + if ((len + 4) > (int)sizeof(buf)) { + _err("exceeded maximum send_request size.\n"); + return -1; + } + memcpy(buf, &type, 4); + memcpy(&buf[4], data, len); + return write(control_channel, buf, len + 4); } -static void setup_main_signals(int cleanup) +static void *signal_thread(void *arg) { - struct sigaction a; - memset(&a, 0, sizeof(a)); - sigfillset(&a.sa_mask); - if (cleanup == 0) { - a.sa_handler = sigproc; - sigaction(SIGCHLD, &a, NULL); - } else - a.sa_handler = SIG_IGN; - sigaction(SIGINT, &a, NULL); - sigaction(SIGTERM, &a, NULL); - sigaction(SIGHUP, &a, NULL); - sigaction(SIGQUIT, &a, NULL); + sigset_t *s = (sigset_t *) arg; + int signum, rc, btype = STP_EXIT; + + while (1) { + if (sigwait(s, &signum) < 0) { + _perr("sigwait"); + continue; + } + dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); + if (signum == SIGQUIT) + cleanup_and_exit(1); + else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) { + // send STP_EXIT + rc = write(control_channel, &btype, sizeof(btype)); + break; + } + } + return NULL; +} + +static void chld_proc(int signum) +{ + int32_t rc, btype = STP_EXIT; + dbug(2, "chld_proc %d (%s)\n", signum, strsignal(signum)); + pid_t pid = waitpid(-1, NULL, WNOHANG); + if (pid != target_pid) + return; + // send STP_EXIT + rc = write(control_channel, &btype, sizeof(btype)); } +static void setup_main_signals(void) +{ + pthread_t tid; + struct sigaction sa; + sigset_t *s = malloc(sizeof(*s)); + if (!s) { + _perr("malloc failed"); + exit(1); + } + sigfillset(s); + pthread_sigmask(SIG_SETMASK, s, NULL); + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + + sa.sa_handler = chld_proc; + sigaction(SIGCHLD, &sa, NULL); + + sigemptyset(s); + sigaddset(s, SIGINT); + sigaddset(s, SIGTERM); + sigaddset(s, SIGHUP); + sigaddset(s, SIGQUIT); + pthread_sigmask(SIG_SETMASK, s, NULL); + if (pthread_create(&tid, NULL, signal_thread, s) < 0) { + _perr("failed to create thread"); + exit(1); + } +} /* * start_cmd forks the command given on the command line @@ -75,7 +134,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 +145,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 +166,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 +187,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 +211,6 @@ static void read_buffer_info(void) return; } - /** * init_stapio - initialize the app * @print_summary: boolean, print summary or not at end of run @@ -164,7 +222,7 @@ int init_stapio(void) dbug(2, "init_stapio\n"); /* create control channel */ - use_old_transport = init_ctl_channel(0); + use_old_transport = init_ctl_channel(modname, 1); if (use_old_transport < 0) { err("Failed to initialize control channel.\n"); return -1; @@ -177,7 +235,7 @@ int init_stapio(void) if (init_oldrelayfs() < 0) { close_ctl_channel(); return -1; - } + } } else { if (init_relayfs() < 0) { close_ctl_channel(); @@ -192,17 +250,12 @@ int init_stapio(void) if (target_cmd) start_cmd(); - return 0; } -/* cleanup_and_exit() closed channels and frees memory - * then exits with the following status codes: - * 1 - failed to initialize. - * 2 - disconnected - * 3 - initialized - */ -void cleanup_and_exit (int closed) +/* cleanup_and_exit() closed channels, frees memory, + * removes the module (if necessary) and exits. */ +void cleanup_and_exit(int detach) { pid_t err; static int exiting = 0; @@ -211,32 +264,34 @@ void cleanup_and_exit (int closed) return; exiting = 1; - setup_main_signals(1); + setup_main_signals(); - dbug(1, "CLEANUP AND EXIT closed=%d\n", closed); + dbug(1, "detach=%d\n", detach); /* what about child processes? we will wait for them here. */ 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); + close_oldrelayfs(detach); else close_relayfs(); dbug(1, "closing control channel\n"); close_ctl_channel(); - if (initialized == 2 && closed == 2) { - err("\nDisconnecting from systemtap module.\n" \ - "To reconnect, type \"staprun -A %s\"\n", modname); - } else if (initialized) - closed = 3; - else - closed = 1; - exit(closed); + if (detach) { + err("\nDisconnecting from systemtap module.\n" "To reconnect, type \"staprun -A %s\"\n", modname); + } else { + dbug(2, "removing %s\n", modname); + if (execl(BINDIR "/staprun", "staprun", "-d", modname, NULL) < 0) { + perror(modname); + _exit(1); + } + } + _exit(0); } /** @@ -247,90 +302,103 @@ int stp_main_loop(void) { ssize_t nb; void *data; - int type; + uint32_t type; FILE *ofp = stdout; char recvbuf[8196]; setvbuf(ofp, (char *)NULL, _IOLBF, 0); - setup_main_signals(0); + setup_main_signals(); dbug(2, "in main loop\n"); send_request(STP_READY, NULL, 0); - while (1) { /* handle messages from control channel */ + /* handle messages from control channel */ + while (1) { nb = read(control_channel, recvbuf, sizeof(recvbuf)); + dbug(2, "nb=%d\n", (int)nb); 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)); + { + 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(0); + } + break; } - if (bw != (nb - (ssize_t)sizeof(int))) { - _perr("write error (nb=%ld)", (long)nb); - cleanup_and_exit(1); - } - 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); + fputs((char *)data, stderr); 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_EXIT: + { + /* module asks us to unload it and exit */ + dbug(2, "got STP_EXIT\n"); + cleanup_and_exit(0); + 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(0); + } 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) + { + struct _stp_msg_start ts; + if (use_old_transport) { + if (init_oldrelayfs() < 0) + cleanup_and_exit(0); + } else { + if (init_relayfs() < 0) + cleanup_and_exit(0); + } + ts.target = target_pid; + send_request(STP_START, &ts, sizeof(ts)); + if (load_only) cleanup_and_exit(1); + 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/stapio.c b/runtime/staprun/stapio.c index ee30a1a1..3c8c4f7f 100644 --- a/runtime/staprun/stapio.c +++ b/runtime/staprun/stapio.c @@ -27,24 +27,23 @@ char *__name__ = "stapio"; int main(int argc, char **argv) { setup_signals(); - parse_args(argc, argv); if (buffer_size) - dbug(1, "Using a buffer of %u bytes.\n", buffer_size); + dbug(1, "Using a buffer of %u MB.\n", buffer_size); if (optind < argc) { parse_modpath(argv[optind++]); dbug(2, "modpath=\"%s\", modname=\"%s\"\n", modpath, modname); } - if (optind < argc) { + if (optind < argc) { if (attach_mod) { err("ERROR: Cannot have module options with attach (-A).\n"); usage(argv[0]); } else { - unsigned start_idx = 3; /* reserve three slots in modoptions[] */ - while (optind < argc && start_idx+1 < MAXMODOPTIONS) + unsigned start_idx = 3; /* reserve three slots in modoptions[] */ + while (optind < argc && start_idx + 1 < MAXMODOPTIONS) modoptions[start_idx++] = argv[optind++]; modoptions[start_idx] = NULL; } @@ -57,13 +56,7 @@ int main(int argc, char **argv) if (init_stapio()) exit(1); - - initialized = 1; - if (attach_mod) { - /* already started */ - initialized++; - } - + if (stp_main_loop()) { err("ERROR: Couldn't enter main loop. Exiting.\n"); exit(1); diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index f4e67fdb..0291d01f 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -16,21 +16,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) 2005-2007 Red Hat, Inc. + * Copyright (C) 2005-2008 Red Hat, Inc. * */ #include "staprun.h" -int inserted_module = 0; - /* used in dbug, _err and _perr */ char *__name__ = "staprun"; extern long delete_module(const char *, unsigned int); -static int -run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) +static int run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) { pid_t pid; int rstatus; @@ -42,14 +39,13 @@ run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) err("%s ", argv[i]); i++; } - err("\n"); + err("\n"); } if ((pid = fork()) < 0) { _perr("fork"); return -1; - } - else if (pid == 0) { + } else if (pid == 0) { /* Make sure we run as the full user. If we're * switching to a non-root user, this won't allow * that process to switch back to root (since the @@ -79,17 +75,6 @@ run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) return -1; } -/* Keep the uid and gid settings because we will likely */ -/* conditionally restore "-u" */ -static int run_stapio(char **argv) -{ - uid_t uid = getuid(); - gid_t gid = getgid(); - argv[0] = PKGLIBDIR "/stapio"; - - return run_as(uid, gid, argv[0], argv); -} - /* * Module to be inserted has one or more user-space probes. Make sure * uprobes is enabled. @@ -132,8 +117,7 @@ static int enable_uprobes(void) dbug(2, "Inserting uprobes module from SystemTap runtime.\n"); argv[0] = NULL; - return insert_module(PKGDATADIR "/runtime/uprobes/uprobes.ko", - NULL, argv); + return insert_module(PKGDATADIR "/runtime/uprobes/uprobes.ko", NULL, argv); } static int insert_stap_module(void) @@ -144,6 +128,66 @@ static int insert_stap_module(void) return insert_module(modpath, bufsize_option, modoptions); } +static int remove_module(const char *name, int verb); + +static void remove_all_modules(void) +{ + char *base; + struct statfs st; + struct dirent *d; + DIR *moddir; + + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) + base = "/sys/kernel/debug/systemtap"; + else + base = "/proc/systemtap"; + + moddir = opendir(base); + if (moddir) { + while ((d = readdir(moddir))) + if (remove_module(d->d_name, 0) == 0) + printf("Module %s removed.\n", d->d_name); + closedir(moddir); + } +} + +static int remove_module(const char *name, int verb) +{ + int ret; + dbug(2, "%s\n", name); + + if (strcmp(name, "*") == 0) { + remove_all_modules(); + return 0; + } + + /* Call init_ctl_channel() which actually attempts an open() + * of the control channel. This is better than using access() because + * an open on an already open channel will fail, preventing us from attempting + * to remove an in-use module. + */ + if (init_ctl_channel(name, 0) < 0) { + if (verb) + err("Error accessing systemtap module %s: %s\n", name, strerror(errno)); + return 1; + } + close_ctl_channel(); + + dbug(2, "removing module %s\n", name); + + /* Don't remove module when priority is elevated. */ + if (setpriority(PRIO_PROCESS, 0, 0) < 0) + _perr("setpriority"); + + ret = do_cap(CAP_SYS_MODULE, delete_module, name, 0); + if (ret != 0) { + err("Error removing module '%s': %s.\n", name, strerror(errno)); + return 1; + } + + dbug(1, "Module %s removed.\n", name); + return 0; +} int init_staprun(void) { @@ -154,71 +198,28 @@ int init_staprun(void) /* We're done with CAP_SYS_ADMIN. */ drop_cap(CAP_SYS_ADMIN); - - if (!attach_mod) { + + if (delete_mod) + exit(remove_module(modname, 1)); + else if (!attach_mod) { if (need_uprobes && enable_uprobes() != 0) return -1; if (insert_stap_module() < 0) return -1; - else - inserted_module = 1; } - return 0; } - -static void cleanup(int rc) -{ - /* Only cleanup once. */ - static int done = 0; - if (done == 0) - done = 1; - else - return; - - dbug(2, "rc=%d, inserted_module=%d\n", rc, inserted_module); - - if (setpriority (PRIO_PROCESS, 0, 0) < 0) - _perr("setpriority"); - - stop_symbol_thread(); - - /* rc == 2 means disconnected */ - if (rc == 2) - return; - - /* If we inserted the module and did not get rc==2, then */ - /* we really want to remove it. */ - if (inserted_module || rc == 3) { - long ret; - dbug(2, "removing module %s\n", modname); - ret = do_cap(CAP_SYS_MODULE, delete_module, modname, 0); - if (ret != 0) - err("Error removing module '%s': %s\n", modname, moderror(errno)); - } -} - -static void exit_cleanup(void) -{ - dbug(2, "something exited...\n"); - cleanup(1); -} int main(int argc, char **argv) { int rc; - if (atexit(exit_cleanup)) { - _perr("cannot set exit function"); - exit(1); - } - - /* NB: Don't do the geteuid()!=0 check here, since we want to - test command-line error-handling while running non-root. */ + /* NB: Don't do the geteuid()!=0 check here, since we want to + test command-line error-handling while running non-root. */ /* Get rid of a few standard environment variables (which */ /* might cause us to do unintended things). */ rc = unsetenv("IFS") || unsetenv("CDPATH") || unsetenv("ENV") - || unsetenv("BASH_ENV"); + || unsetenv("BASH_ENV"); if (rc) { _perr("unsetenv failed"); exit(-1); @@ -229,20 +230,20 @@ int main(int argc, char **argv) parse_args(argc, argv); if (buffer_size) - dbug(2, "Using a buffer of %u bytes.\n", buffer_size); + dbug(2, "Using a buffer of %u MB.\n", buffer_size); if (optind < argc) { parse_modpath(argv[optind++]); dbug(2, "modpath=\"%s\", modname=\"%s\"\n", modpath, modname); } - if (optind < argc) { + if (optind < argc) { if (attach_mod) { err("ERROR: Cannot have module options with attach (-A).\n"); usage(argv[0]); } else { unsigned start_idx = 0; - while (optind < argc && start_idx+1 < MAXMODOPTIONS) + while (optind < argc && start_idx + 1 < MAXMODOPTIONS) modoptions[start_idx++] = argv[optind++]; modoptions[start_idx] = NULL; } @@ -254,14 +255,13 @@ int main(int argc, char **argv) } if (geteuid() != 0) { - err("ERROR: The effective user ID of staprun must be set to the root user.\n" - " Check permissions on staprun and ensure it is a setuid root program.\n"); + err("ERROR: The effective user ID of staprun must be set to the root user.\n" + " Check permissions on staprun and ensure it is a setuid root program.\n"); exit(1); } - if (!init_cap()) - exit(1); - + init_cap(); + if (check_permissions() != 1) usage(argv[0]); @@ -277,11 +277,14 @@ int main(int argc, char **argv) if (init_staprun()) exit(1); - setup_staprun_signals(); - start_symbol_thread(); - - rc = run_stapio(argv); - cleanup(rc); - + argv[0] = PKGLIBDIR "/stapio"; + if (execv(argv[0], argv) < 0) { + perror(argv[0]); + goto err; + } return 0; + +err: + remove_module(modname, 1); + return 1; } diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 1128fb4c..60bab391 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -103,8 +103,6 @@ extern char *__name__; #define STP_OLD_TRANSPORT #include "../transport/transport_msgs.h" -extern int use_old_transport; - #define RELAYFS_MAGIC 0xF0B4A981 #define DEBUGFS_MAGIC 0x64626720 #define DEBUGFSDIR "/sys/kernel/debug" @@ -118,9 +116,8 @@ 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); -int init_ctl_channel(int); +void send_unwind_data(const char *name); +int init_ctl_channel(const char *name, int verb); void close_ctl_channel(void); int init_relayfs(void); void close_relayfs(void); @@ -129,7 +126,7 @@ void close_oldrelayfs(int); void setup_signals(void); /* cap.c */ void print_cap(char *text); -int init_cap(void); +void init_cap(void); void add_cap(cap_value_t cap); void del_cap(cap_value_t cap); void drop_cap(cap_value_t cap); @@ -169,6 +166,7 @@ extern int target_pid; extern char *target_cmd; extern char *outfile_name; extern int attach_mod; +extern int delete_mod; extern int load_only; extern int need_uprobes; diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index 34e12c25..c1cb92b7 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -16,18 +16,6 @@ #include <grp.h> #include <pwd.h> -void setup_staprun_signals(void) -{ - struct sigaction a; - memset(&a, 0, sizeof(a)); - sigfillset(&a.sa_mask); - a.sa_handler = SIG_IGN; - sigaction(SIGINT, &a, NULL); - sigaction(SIGTERM, &a, NULL); - sigaction(SIGHUP, &a, NULL); - sigaction(SIGQUIT, &a, NULL); -} - extern long init_module(void *, unsigned long, const char *); /* Module errors get translated. */ @@ -401,95 +389,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/symbols.c b/runtime/staprun/symbols.c deleted file mode 100644 index c7362d9e..00000000 --- a/runtime/staprun/symbols.c +++ /dev/null @@ -1,333 +0,0 @@ -/* -*- linux-c -*- - * Symbols and modules functions for staprun. - * - * Copyright (C) 2006-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" - -/* send symbol data */ -static int send_data(int32_t type, void *data, int len) -{ - if (write(control_channel, &type, 4) <= 0) - return -1; - return write(control_channel, data, len); -} - - -/* Get the sections for a module. Put them in the supplied buffer */ -/* in the following order: */ -/* [struct _stp_msg_module][struct _stp_symbol sections ...][string data][unwind data] */ -/* Return the total length of all the data. */ - -#define SECDIR "/sys/module/%s/sections" -static int get_sections(char *name, char *data_start, int datalen) -{ - char dir[STP_MODULE_NAME_LEN + sizeof(SECDIR)]; - char filename[STP_MODULE_NAME_LEN + 256]; - char buf[32], strdata_start[32768]; - char *strdata=strdata_start, *data=data_start; - int fd, len, res, unwind_data_len=0; - struct _stp_msg_module *mod = (struct _stp_msg_module *)data_start; - - struct dirent *d; - DIR *secdir; - void *sec; - int struct_symbol_size = kernel_ptr_size == 8 ? sizeof(struct _stp_symbol64) : sizeof(struct _stp_symbol32); - uint64_t sec_addr; - - /* start of data is a struct _stp_msg_module */ - data += sizeof(struct _stp_msg_module); - - res = snprintf(dir, sizeof(dir), SECDIR, name); - if (res >= (int)sizeof(dir)) { - _err("Couldn't fit module \"%s\" into dir buffer.\n" \ - "This should never happen. Please file a bug report.\n", name); - return -1; - } - - if ((secdir = opendir(dir)) == NULL) - return 0; - - /* Initialize mod. */ - memset(mod, 0, sizeof(struct _stp_msg_module)); - - /* Copy name in and check for overflow. */ - strncpy(mod->name, name, STP_MODULE_NAME_LEN); - if (mod->name[STP_MODULE_NAME_LEN - 1] != '\0') { - _err("Couldn't fit module \"%s\" into mod->name buffer.\n" \ - "This should never happen. Please file a bug report.\n", name); - return -1; - } - - /* FIXME: optionally fill in unwind data here */ - mod->unwind_len = unwind_data_len; - - while ((d = readdir(secdir))) { - char *secname = d->d_name; - - /* Copy filename in and check for overflow. */ - res = snprintf(filename, sizeof(filename), "/sys/module/%s/sections/%s", name, secname); - if (res >= (int)sizeof(filename)) { - _err("Couldn't fit secname \"%s\" into filename buffer.\n" \ - "This should never happen. Please file a bug report.\n", secname); - closedir(secdir); - return -1; - } - - /* filter out some non-useful stuff */ - if (!strncmp(secname,"__",2) - || !strcmp(secname,".") - || !strcmp(secname,"..") - || !strcmp(secname,".module_sig") - || !strcmp(secname,".modinfo") - || !strcmp(secname,".strtab") - || !strcmp(secname,".symtab") ) { - continue; - } - if (!strncmp(secname, ".gnu.linkonce", 13) - && strcmp(secname, ".gnu.linkonce.this_module")) - continue; - - if ((fd = open(filename,O_RDONLY)) >= 0) { - if (read(fd, buf, 32) > 0) { - /* create next section */ - sec = data; - if (data - data_start + struct_symbol_size > datalen) - goto err1; - data += struct_symbol_size; - - sec_addr = (uint64_t)strtoull(buf,NULL,16); - if (kernel_ptr_size == 8) { - ((struct _stp_symbol64 *)sec)->addr = sec_addr; - ((struct _stp_symbol64 *)sec)->symbol = (uint64_t)(strdata - strdata_start); - } else { - ((struct _stp_symbol32 *)sec)->addr = (uint32_t)sec_addr; - ((struct _stp_symbol32 *)sec)->symbol = (uint32_t)(strdata - strdata_start); - } - mod->num_sections++; - - /* now create string data for the - * section (checking for overflow) */ - if ((strdata - strdata_start + strlen(strdata)) - >= sizeof(strdata_start)) - goto err1; - strcpy(strdata, secname); - strdata += strlen(secname) + 1; - - /* These sections are used a lot so keep the values handy */ - if (!strcmp(secname, ".data") || !strncmp(secname, ".rodata", 7)) { - if (mod->data == 0 || sec_addr < mod->data) - mod->data = sec_addr; - } - if (!strcmp(secname, ".text")) - mod->text = sec_addr; - if (!strcmp(secname, ".gnu.linkonce.this_module")) - mod->module = sec_addr; - } - close(fd); - } - } - closedir(secdir); - - /* consolidate buffers */ - len = strdata - strdata_start; - if ((len + data - data_start) > datalen) - goto err0; - strdata = strdata_start; - while (len--) - *data++ = *strdata++; - -#if 0 - if (unwind_data_len) { - if ((unwind_data_len + data - data_start) > datalen) - goto err0; - memcpy(data, unwind_data, unwind_data_len); - data += unwind_data_len; - } -#endif - return data - data_start; - -err1: - close(fd); - closedir(secdir); -err0: - /* if this happens, something went seriously wrong. */ - _err("Unexpected error. Overflowed buffers.\n"); - return -1; -} -#undef SECDIR - -/* - * For modules, we send the name, section names, and offsets - */ -static int send_module (char *mname) -{ - char data[65536]; - int len; - len = get_sections(mname, data, sizeof(data)); - if (len > 0) { - if (send_data(STP_MODULE, data, len) < 0) { - _err("Loading of module %s failed. Exiting...\n", mname); - return -1; - } - } - return len; -} - -/* - * Send either all modules, or a specific one. - * Returns: - * >=0 : OK - * -1 : serious error (exit) - */ -int do_module (void *data) -{ - struct _stp_msg_module *mod = (struct _stp_msg_module *)data; - - if (mod->name[0] == 0) { - struct dirent *d; - DIR *moddir = opendir("/sys/module"); - if (moddir) { - while ((d = readdir(moddir))) - if (send_module(d->d_name) < 0) { - closedir(moddir); - return -1; - } - closedir(moddir); - } - send_request(STP_MODULE, data, 1); - return 0; - } - - return send_module(mod->name); -} - -#define MAX_SYMBOLS 32*1024 - -/* - * Read /proc/kallsyms and send all kernel symbols to the - * systemtap module. Ignore module symbols; the systemtap module - * can access them directly. - */ -int do_kernel_symbols(void) -{ - FILE *kallsyms=NULL; - char *name, *mod, *dataptr, *datamax, type, *data_base=NULL; - unsigned long long addr; - void *syms = NULL; - int ret, num_syms, i = 0, struct_symbol_size; - int max_syms= MAX_SYMBOLS, data_basesize = MAX_SYMBOLS*32; - - if (kernel_ptr_size == 8) - struct_symbol_size = sizeof(struct _stp_symbol64); - else - struct_symbol_size = sizeof(struct _stp_symbol32); - - syms = malloc(max_syms * struct_symbol_size); - data_base = malloc(data_basesize); - if (data_base == NULL || syms == NULL) { - _err("Failed to allocate memory for symbols\n"); - goto err; - } - dataptr = data_base; - datamax = data_base + data_basesize; - - kallsyms = fopen ("/proc/kallsyms", "r"); - if (!kallsyms) { - _perr("Fatal error: Unable to open /proc/kallsyms"); - goto err; - } - - /* put empty string in data */ - *dataptr++ = 0; - - while ((ret = fscanf(kallsyms, "%llx %c %as [%as", &addr, &type, &name, &mod))>0 - && dataptr < datamax) { - if (ret < 3) - continue; - if (ret > 3) { - /* ignore modules */ - free(name); - free(mod); - /* modules are loaded above the kernel, so if we */ - /* are getting modules, then we're done. */ - break; - } - - if (type == 't' || type == 'T' || type == 'A') { - if (kernel_ptr_size == 8) { - ((struct _stp_symbol64 *)syms)[i].addr = (uint64_t)addr; - ((struct _stp_symbol64 *)syms)[i].symbol = (uint64_t)(dataptr - data_base); - } else { - ((struct _stp_symbol32 *)syms)[i].addr = (uint32_t)addr; - ((struct _stp_symbol32 *)syms)[i].symbol = (uint32_t)(dataptr - data_base); - } - if (dataptr >= datamax - strlen(name)) { - char *db; - data_basesize *= 2; - db = realloc(data_base, data_basesize); - if (db == NULL) { - _err("Could not allocate enough space for symbols.\n"); - goto err; - } - dataptr = db + (dataptr - data_base); - datamax = db + data_basesize; - data_base = db; - } - strcpy(dataptr, name); - dataptr += strlen(name) + 1; - free(name); - i++; - if (i >= max_syms) { - max_syms *= 2; - syms = realloc(syms, max_syms*struct_symbol_size); - if (syms == NULL) { - _err("Could not allocate enough space for symbols.\n"); - goto err; - } - } - } - } - num_syms = i; - if (num_syms <= 0) - goto err; - - - /* send header */ - struct _stp_msg_symbol_hdr smsh; - smsh.num_syms = num_syms; - smsh.sym_size = (uint32_t)(dataptr - data_base); - smsh.unwind_size = (uint32_t)0; - if (send_request(STP_SYMBOLS, &smsh, sizeof(smsh)) <= 0) - goto err; - - /* send syms */ - if (send_data(STP_SYMBOLS, syms, num_syms*struct_symbol_size) < 0) - goto err; - - /* send data */ - if (send_data(STP_SYMBOLS, data_base, dataptr-data_base) < 0) - goto err; - - free(data_base); - free(syms); - fclose(kallsyms); - return 0; - -err: - if (data_base) - free(data_base); - if (syms) - free(syms); - if (kallsyms) - fclose(kallsyms); - - _err("Loading of symbols failed. Exiting...\n"); - return -1; -} 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..7163bf92 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; } } @@ -223,8 +223,7 @@ void _stp_symbol_print(unsigned long address) } /* Like _stp_symbol_print, except only print if the address is a valid function address */ - -void _stp_func_print(unsigned long address, int verbose, int exact) +int _stp_func_print(unsigned long address, int verbose, int exact) { char *modname; const char *name; @@ -247,7 +246,9 @@ void _stp_func_print(unsigned long address, int verbose, int exact) _stp_printf(" %p : %s+%#lx/%#lx%s\n", (int64_t) address, name, offset, size, exstr); } else _stp_printf("%p ", (int64_t) address); + return 1; } + return 0; } void _stp_symbol_snprint(char *str, size_t len, unsigned long address) 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/task_finder.c b/runtime/task_finder.c index d2e57a6b..6d79c98a 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -1,9 +1,16 @@ #include <linux/list.h> +#include <linux/binfmts.h> static LIST_HEAD(__stp_task_finder_list); struct stap_task_finder_target; +#define __STP_TF_STARTING 0 +#define __STP_TF_RUNNING 1 +#define __STP_TF_STOPPING 2 +#define __STP_TF_STOPPED 3 +atomic_t __stp_task_finder_state = ATOMIC_INIT(__STP_TF_STARTING); + typedef int (*stap_task_finder_callback)(struct task_struct *tsk, int register_p, struct stap_task_finder_target *tgt); @@ -23,6 +30,10 @@ struct stap_task_finder_target { stap_task_finder_callback callback; }; +static u32 +__stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, + struct task_struct *tsk); + static int stap_register_task_finder_target(struct stap_task_finder_target *new_tgt) { @@ -38,6 +49,11 @@ stap_register_task_finder_target(struct stap_task_finder_target *new_tgt) else new_tgt->pathlen = 0; + // Make sure everything is initialized properly. + new_tgt->engine_attached = 0; + memset(&new_tgt->ops, 0, sizeof(new_tgt->ops)); + new_tgt->ops.report_death = &__stp_utrace_task_finder_target_death; + // Search the list for an existing entry for pathname/pid. list_for_each(node, &__stp_task_finder_list) { tgt = list_entry(node, struct stap_task_finder_target, list); @@ -62,7 +78,6 @@ stap_register_task_finder_target(struct stap_task_finder_target *new_tgt) } // Add this target to the callback list for this task. - new_tgt->engine_attached = 0; list_add_tail(&new_tgt->callback_list, &tgt->callback_list_head); return 0; } @@ -78,6 +93,10 @@ stap_utrace_detach_ops(struct utrace_engine_ops *ops) rcu_read_lock(); for_each_process(tsk) { struct mm_struct *mm; + + if (tsk->pid <= 1) + continue; + mm = get_task_mm(tsk); if (mm) { mmput(mm); @@ -152,11 +171,12 @@ __stp_get_mm_path(struct mm_struct *mm, char *buf, int buflen) vma = vma->vm_next; } if (vma) { - struct vfsmount *mnt = mntget(vma->vm_file->f_path.mnt); - struct dentry *dentry = dget(vma->vm_file->f_path.dentry); - rc = d_path(dentry, mnt, buf, buflen); - dput(dentry); - mntput(mnt); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + rc = d_path(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt, + buf, buflen); +#else + rc = d_path(&(vma->vm_file->f_path), buf, buflen); +#endif } else { *buf = '\0'; @@ -167,76 +187,82 @@ __stp_get_mm_path(struct mm_struct *mm, char *buf, int buflen) } #define __STP_UTRACE_TASK_FINDER_EVENTS (UTRACE_EVENT(CLONE) \ - | UTRACE_EVENT(EXEC)) + | UTRACE_EVENT(EXEC) \ + | UTRACE_EVENT(DEATH)) #define __STP_UTRACE_ATTACHED_TASK_EVENTS (UTRACE_EVENT(DEATH)) -static u32 -__stp_utrace_task_finder_clone(struct utrace_attached_engine *engine, - struct task_struct *parent, - unsigned long clone_flags, - struct task_struct *child) +static int +__stp_utrace_attach(struct task_struct *tsk, + const struct utrace_engine_ops *ops, void *data, + unsigned long event_flags) { - struct utrace_attached_engine *child_engine; + struct utrace_attached_engine *engine; struct mm_struct *mm; + int rc = 0; - // On clone, attach to the child. Ignore threads with no mm - // (which are kernel threads). - mm = get_task_mm(child); - if (mm) { - mmput(mm); - child_engine = utrace_attach(child, UTRACE_ATTACH_CREATE, - engine->ops, 0); - if (IS_ERR(child_engine)) - _stp_error("attach to clone child %d failed: %ld", - (int)child->pid, PTR_ERR(child_engine)); - else { - utrace_set_flags(child, child_engine, - __STP_UTRACE_TASK_FINDER_EVENTS); + // Ignore init + if (tsk->pid <= 1) + return EPERM; + + // Ignore threads with no mm (which are kernel threads). + mm = get_task_mm(tsk); + if (! mm) + return EPERM; + mmput(mm); + + engine = utrace_attach(tsk, UTRACE_ATTACH_CREATE, ops, data); + if (IS_ERR(engine)) { + int error = -PTR_ERR(engine); + if (error != ENOENT) { + _stp_error("utrace_attach returned error %d on pid %d", + error, (int)tsk->pid); + rc = error; } } - return UTRACE_ACTION_RESUME; + else if (unlikely(engine == NULL)) { + _stp_error("utrace_attach returned NULL on pid %d", + (int)tsk->pid); + rc = EFAULT; + } + else { + utrace_set_flags(tsk, engine, event_flags); + } + return rc; } static u32 -__stp_utrace_task_finder_death(struct utrace_attached_engine *engine, - struct task_struct *tsk) +__stp_utrace_task_finder_report_clone(struct utrace_attached_engine *engine, + struct task_struct *parent, + unsigned long clone_flags, + struct task_struct *child) { - struct stap_task_finder_target *tgt = engine->data; - - // The first implementation of this added a - // UTRACE_EVENT(DEATH) handler to - // __stp_utrace_task_finder_ops. However, dead threads don't - // have a mm_struct, so we can't find the exe's path. So, we - // don't know which callback(s) to call. - // - // So, now when an "interesting" thread is found, we add a - // separate UTRACE_EVENT(DEATH) handler for every probe. + struct utrace_attached_engine *child_engine; + struct mm_struct *mm; - if (tgt != NULL && tgt->callback != NULL) { - int rc; + if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) + return UTRACE_ACTION_RESUME; - // Call the callback - rc = tgt->callback(tsk, 0, tgt); - if (rc != 0) { - _stp_error("death callback for %d failed: %d", - (int)tsk->pid, rc); - } - } + // On clone, attach to the child. + (void) __stp_utrace_attach(child, engine->ops, 0, + __STP_UTRACE_TASK_FINDER_EVENTS); return UTRACE_ACTION_RESUME; } static u32 -__stp_utrace_task_finder_exec(struct utrace_attached_engine *engine, - struct task_struct *tsk, - const struct linux_binprm *bprm, - struct pt_regs *regs) +__stp_utrace_task_finder_report_exec(struct utrace_attached_engine *engine, + struct task_struct *tsk, + const struct linux_binprm *bprm, + struct pt_regs *regs) { size_t filelen; struct list_head *tgt_node; struct stap_task_finder_target *tgt; int found_node = 0; + if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) + return UTRACE_ACTION_RESUME; + // On exec, check bprm if (bprm->filename == NULL) return UTRACE_ACTION_RESUME; @@ -258,6 +284,8 @@ __stp_utrace_task_finder_exec(struct utrace_attached_engine *engine, struct list_head *cb_node; list_for_each(cb_node, &tgt->callback_list_head) { struct stap_task_finder_target *cb_tgt; + int rc; + cb_tgt = list_entry(cb_node, struct stap_task_finder_target, callback_list); @@ -274,31 +302,59 @@ __stp_utrace_task_finder_exec(struct utrace_attached_engine *engine, } // Set up thread death notification. - memset(&cb_tgt->ops, 0, sizeof(cb_tgt->ops)); - cb_tgt->ops.report_death - = &__stp_utrace_task_finder_death; - - engine = utrace_attach(tsk, - UTRACE_ATTACH_CREATE, - &cb_tgt->ops, cb_tgt); - if (IS_ERR(engine)) { - _stp_error("attach to exec'ed %d failed: %ld", - (int)tsk->pid, - PTR_ERR(engine)); - } - else { - utrace_set_flags(tsk, engine, + rc = __stp_utrace_attach(tsk, &cb_tgt->ops, cb_tgt, __STP_UTRACE_ATTACHED_TASK_EVENTS); - cb_tgt->engine_attached = 1; - } + if (rc != 0 && rc != EPERM) + break; + cb_tgt->engine_attached = 1; } } return UTRACE_ACTION_RESUME; } +static u32 +stap_utrace_task_finder_report_death(struct utrace_attached_engine *engine, + struct task_struct *tsk) +{ + return UTRACE_ACTION_DETACH; +} + +static u32 +__stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, + struct task_struct *tsk) +{ + struct stap_task_finder_target *tgt = engine->data; + + if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) { + return UTRACE_ACTION_DETACH; + } + + // The first implementation of this added a + // UTRACE_EVENT(DEATH) handler to + // __stp_utrace_task_finder_ops. However, dead threads don't + // have a mm_struct, so we can't find the exe's path. So, we + // don't know which callback(s) to call. + // + // So, now when an "interesting" thread is found, we add a + // separate UTRACE_EVENT(DEATH) handler for every probe. + + if (tgt != NULL && tgt->callback != NULL) { + int rc; + + // Call the callback + rc = tgt->callback(tsk, 0, tgt); + if (rc != 0) { + _stp_error("death callback for %d failed: %d", + (int)tsk->pid, rc); + } + } + return UTRACE_ACTION_DETACH; +} + struct utrace_engine_ops __stp_utrace_task_finder_ops = { - .report_clone = __stp_utrace_task_finder_clone, - .report_exec = __stp_utrace_task_finder_exec, + .report_clone = __stp_utrace_task_finder_report_clone, + .report_exec = __stp_utrace_task_finder_report_exec, + .report_death = stap_utrace_task_finder_report_death, }; int @@ -314,44 +370,36 @@ stap_start_task_finder(void) return ENOMEM; } + atomic_set(&__stp_task_finder_state, __STP_TF_RUNNING); + rcu_read_lock(); for_each_process(tsk) { - struct utrace_attached_engine *engine; struct mm_struct *mm; char *mmpath; size_t mmpathlen; struct list_head *tgt_node; + /* Attach to the thread */ + rc = __stp_utrace_attach(tsk, &__stp_utrace_task_finder_ops, 0, + __STP_UTRACE_TASK_FINDER_EVENTS); + if (rc == EPERM) { + /* Ignore EPERM errors, which mean this wasn't + * a thread we can attach to. */ + rc = 0; + continue; + } + else if (rc != 0) { + /* If we get a real error, quit. */ + break; + } + + /* Grab the path associated with this task. */ mm = get_task_mm(tsk); if (! mm) { /* If the thread doesn't have a mm_struct, it is * a kernel thread which we need to skip. */ continue; } - - /* Attach to the thread */ - engine = utrace_attach(tsk, UTRACE_ATTACH_CREATE, - &__stp_utrace_task_finder_ops, 0); - if (IS_ERR(engine)) { - int error = -PTR_ERR(engine); - if (error != ENOENT) { - mmput(mm); - _stp_error("utrace_attach returned error %d on pid %d", - error, (int)tsk->pid); - rc = error; - break; - } - } - else if (unlikely(engine == NULL)) { - mmput(mm); - _stp_error("utrace_attach returned NULL on pid %d", - (int)tsk->pid); - rc = EFAULT; - break; - } - utrace_set_flags(tsk, engine, __STP_UTRACE_TASK_FINDER_EVENTS); - - /* Check the thread's exe's path/pid against our list. */ mmpath = __stp_get_mm_path(mm, mmpath_buf, PATH_MAX); mmput(mm); /* We're done with mm */ if (IS_ERR(mmpath)) { @@ -361,6 +409,7 @@ stap_start_task_finder(void) break; } + /* Check the thread's exe's path/pid against our list. */ mmpathlen = strlen(mmpath); list_for_each(tgt_node, &__stp_task_finder_list) { struct stap_task_finder_target *tgt; @@ -394,10 +443,19 @@ stap_start_task_finder(void) (int)tsk->pid, rc); break; } + + // Set up thread death notification. + rc = __stp_utrace_attach(tsk, &cb_tgt->ops, + cb_tgt, + __STP_UTRACE_ATTACHED_TASK_EVENTS); + if (rc != 0 && rc != EPERM) + break; + cb_tgt->engine_attached = 1; } } } rcu_read_unlock(); + _stp_kfree(mmpath_buf); return rc; } @@ -405,6 +463,8 @@ stap_start_task_finder(void) static void stap_stop_task_finder(void) { + atomic_set(&__stp_task_finder_state, __STP_TF_STOPPING); stap_utrace_detach_ops(&__stp_utrace_task_finder_ops); __stp_task_finder_cleanup(); + atomic_set(&__stp_task_finder_state, __STP_TF_STOPPED); } diff --git a/runtime/time.c b/runtime/time.c index 52a2edbb..8a0b6fad 100644 --- a/runtime/time.c +++ b/runtime/time.c @@ -131,10 +131,13 @@ __stp_time_timer_callback(unsigned long val) time->base_cycles = cycles; write_sequnlock(&time->lock); + local_irq_restore(flags); + /* PR6481: reenable IRQs before resetting the timer. + XXX: The worst that can probably happen is that we get + two consecutive timer resets. */ + if (likely(stp_timer_reregister)) mod_timer(&time->timer, jiffies + 1); - - local_irq_restore(flags); } /* This is called as an IPI, with interrupts disabled. */ diff --git a/runtime/transport/ChangeLog b/runtime/transport/ChangeLog index c3837f86..9d0ba162 100644 --- a/runtime/transport/ChangeLog +++ b/runtime/transport/ChangeLog @@ -1,3 +1,51 @@ +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + PR 5645 + * transport.c (_stp_transport_init): Fix subbuffer size calculation + overflow. + +2008-04-21 hunt <hunt@redhat.com> + + * control.c (_stp_ctl_write): Return len + sizeof(int) so + sending an empty command doesn't return 0 and look like a failure. + * transport.c: _stp_cleanup_and_exit(): Cleanup. + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + PR 6410 + * symbols.c (_stp_do_unwind_data): Tolerate !STP_USE_DWARF_UNWINDER. + +2008-04-15 Frank Ch. Eigler <fche@elastic.org> + + PR 6405 + * symbols.c (_stp_load_module_symbols): Support older kernels + without module->sect_attrs->nsections. + +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..ca7edf79 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 @@ -203,99 +141,22 @@ static int _stp_ctl_write(int type, void *data, unsigned len) list_add_tail(&bptr->list, &_stp_ctl_ready_q); spin_unlock_irqrestore(&_stp_ctl_ready_lock, flags); - 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; + return len + sizeof(bptr->type); } /* 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..087bf893 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,134 @@ 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; +#ifdef STP_USE_DWARF_UNWINDER + _stp_create_unwind_hdr(m); +#endif +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 +353,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 +387,125 @@ 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 = mod->sect_attrs; + struct attribute_group *sag = & sa->grp; + unsigned sect_size = 0, sect_num = 0, sym_size, sym_num; + struct _stp_module *sm; + char *dataptr, *endptr; + unsigned nsections = 0; + +#ifdef STAPCONF_MODULE_NSECTIONS + nsections = sa->nsections; +#else + /* count section attributes on older kernel */ + struct attribute** gattr; + for (gattr = sag->attrs; *gattr; gattr++) + nsections++; + dbug_sym(2, "\tcount %d\n", nsections); +#endif + + /* calculate how much space to allocate for section strings */ + for (i = 0; i < 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 < 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 +515,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 +581,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..a4e4e652 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,14 @@ 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. */ +/* by staprun or the exit() function. */ /* 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 send_exit) { - kbug(DEBUG_TRANSPORT, "cleanup_and_exit (%d)\n", dont_rmmod); + dbug_trans(1, "cleanup_and_exit (%d)\n", send_exit); if (!_stp_exit_called) { int failures; @@ -128,23 +90,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"); - /* 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, "ctl_send STP_EXIT\n"); + if (send_exit) + _stp_ctl_send(STP_EXIT, NULL, 0); + dbug_trans(1, "done with ctl_send STP_EXIT\n"); } } @@ -153,7 +116,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 +132,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 +143,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; @@ -198,7 +161,7 @@ static void _stp_work_queue (void *data) /* if exit flag is set AND we have finished with probe_start() */ if (unlikely(_stp_exit_flag)) - _stp_cleanup_and_exit(0); + _stp_cleanup_and_exit(1); else if (likely(_stp_attached)) queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER); } @@ -211,19 +174,19 @@ static void _stp_work_queue (void *data) */ void _stp_transport_close() { - kbug(DEBUG_TRANSPORT, "%d: ************** transport_close *************\n", current->pid); - _stp_cleanup_and_exit(1); + dbug_trans(1, "%d: ************** transport_close *************\n", current->pid); + _stp_cleanup_and_exit(0); 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 +212,26 @@ 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_subbuf_size = 65536; + while (size / _stp_subbuf_size > 64 && + _stp_subbuf_size < 1024 * 1024) { + _stp_subbuf_size <<= 1; + } _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 +253,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 +341,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 +361,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 +372,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..aa270cad --- /dev/null +++ b/runtime/unwind.c @@ -0,0 +1,964 @@ +/* -*- 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" + +#ifdef STP_USE_DWARF_UNWINDER + +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 +} + + +#endif /* STP_USE_DWARF_UNWINDER */ diff --git a/runtime/unwind/i386.h b/runtime/unwind/i386.h new file mode 100644 index 00000000..79e6ba73 --- /dev/null +++ b/runtime/unwind/i386.h @@ -0,0 +1,135 @@ +/* -*- 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 + +#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 + +#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..ae5e75d3 --- /dev/null +++ b/runtime/unwind/unwind.h @@ -0,0 +1,146 @@ +/* -*- 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_ + +#ifdef STP_USE_DWARF_UNWINDER + +#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_USE_DWARF_UNWINDER */ +#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/runtime/vsprintf.c b/runtime/vsprintf.c index 0bf625a5..dcaa1bc3 100644 --- a/runtime/vsprintf.c +++ b/runtime/vsprintf.c @@ -256,18 +256,18 @@ int _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) break; case 2: if((str + 1) <= end) - *(int16_t *)str = (int16_t)num; + memcpy(str, &num, 2); str+=2; break; case 4: if((str + 3) <= end) - *(int32_t *)str = num; + memcpy(str, &num, 4); str+=4; break; default: // "%.8b" by default case 8: if((str + 7) <= end) - *(int64_t *)str = num; + memcpy(str, &num, 8); str+=8; break; } @@ -1,5 +1,5 @@ // -*- C++ -*- -// Copyright (C) 2005-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 @@ -30,6 +30,8 @@ struct derived_probe; struct be_derived_probe_group; struct dwarf_derived_probe_group; struct uprobe_derived_probe_group; +struct utrace_derived_probe_group; +struct task_finder_derived_probe_group; struct timer_derived_probe_group; struct profile_derived_probe_group; struct mark_derived_probe_group; @@ -88,6 +90,7 @@ struct systemtap_session bool timing; bool keep_tmpdir; bool guru_mode; + bool listing_mode; bool bulk_mode; bool unoptimized; bool merge; @@ -140,6 +143,8 @@ struct systemtap_session be_derived_probe_group* be_derived_probes; dwarf_derived_probe_group* dwarf_derived_probes; uprobe_derived_probe_group* uprobe_derived_probes; + utrace_derived_probe_group* utrace_derived_probes; + task_finder_derived_probe_group* task_finder_derived_probes; timer_derived_probe_group* timer_derived_probes; profile_derived_probe_group* profile_derived_probes; mark_derived_probe_group* mark_derived_probes; @@ -157,6 +162,9 @@ struct systemtap_session Dwarf_Addr sym_kprobes_text_end; Dwarf_Addr sym_stext; + // List of libdwfl module names to extract symbol/unwind data for. + std::set<std::string> unwindsym_modules; + std::set<std::string> seen_errors; unsigned num_errors () { return seen_errors.size(); } // void print_error (const parse_error& e); @@ -45,6 +45,15 @@ stap \- systemtap script translator/driver [ .I ARGUMENTS ] +.br +.B stap +[ +.I OPTIONS +] +.BI \-l " PROBE" +[ +.I ARGUMENTS +] .SH DESCRIPTION @@ -154,6 +163,10 @@ Start the probes, run CMD, and exit when CMD finishes. Sets target() to PID. This allows scripts to be written that filter on a specific process. .TP +.BI \-l " PROBE" +Instead of running a probe script, just list all available probe +points matching the given pattern. The pattern may include wildcards +and aliases. .B \-\-kelf For names and addresses of functions to probe, consult the symbol tables in the kernel and modules. diff --git a/stapex.5.in b/stapex.5.in index 86e1c87b..2c9ecb60 100644 --- a/stapex.5.in +++ b/stapex.5.in @@ -101,11 +101,9 @@ probe kernel.function("sys_mkdir") { println ("enter") } probe kernel.function("sys_mkdir").return { println ("exit") } .ESAMPLE -To list the probeable functions in the kernel, use the last-pass -option to the translator. That output needs to be filtered because -each inlined function instance is listed separately. +To list the probeable functions in the kernel, use the listings mode. .SAMPLE -% stap \-p2 \-e \[aq]probe kernel.function("*") {}\[aq] | sort | uniq +% stap \-l \[aq]kernel.function("*")\[aq] .ESAMPLE .SH SEE ALSO diff --git a/stapprobes.5.in b/stapprobes.5.in index ce8ef52d..3633fd39 100644 --- a/stapprobes.5.in +++ b/stapprobes.5.in @@ -306,6 +306,46 @@ in that both use raw (unverified) virtual addresses and provide no $variables. The target PID parameter must identify a running process, and ADDRESS should identify a valid instruction address. All threads of that process will be probed. +.PP +Additional user-space probing is available in the following forms: +.SAMPLE +process(PID).clone +process("PATH").clone +process(PID).exec +process("PATH").exec +process(PID).death +process("PATH").death +process(PID).syscall +process("PATH").syscall +process(PID).syscall.return +process("PATH").syscall.return +.ESAMPLE +.PP +A +.B .clone +probe gets called when a thread described by PID or PATH creates a new +thread. +A +.B .exec +probe gets called when a thread described by PID or PATH returns from +.IR exec() . +A +.B .death +probe gets called when a thread described by PID or PATH dies. +A +.B .syscall +probe gets called when a thread described by PID or PATH makes a +system call. The system call number is available in the "$syscall" +context variable. +A +.B .syscall.return +probe gets called when a thread described by PID or PATH returns from a +system call. The system call number is available in the "$syscall" +context variable. +.PP +Note that +.I PATH +pathnames must be absolute. .SS PROCFS diff --git a/staprun.8.in b/staprun.8.in index c1678f69..ca976a4a 100644 --- a/staprun.8.in +++ b/staprun.8.in @@ -57,7 +57,7 @@ be in percpu files FILE_x where 'x' is the cpu number. .B \-b BUFFER_SIZE The systemtap module will specify a buffer size. Setting one here will override that value. The value should be -an integer between 1 and 64 which be assumed to be the +an integer between 1 and 4095 which be assumed to be the buffer size in MB. That value will be per-cpu if bulk mode is used. .TP .B \-L @@ -68,6 +68,11 @@ option. .TP .B \-A Attach to loaded systemtap module. +.TP +.B \-d +Delete a module. Only detached or unused modules +the user has permission to access will be deleted. Use "*" +(quoted) to delete all unused modules. .SH ARGUMENTS .B MODULE @@ -136,7 +141,7 @@ Members of the .I stapusr group can use .I staprun -to insert systemtap modules (or attach to existing systemtap modules) that +to insert or remove systemtap modules (or attach to existing systemtap modules) that are located in the /lib/modules/VERSION/systemtap directory. .SH FILES .TP diff --git a/staptree.cxx b/staptree.cxx index 39f5580e..02a6c8dc 100644 --- a/staptree.cxx +++ b/staptree.cxx @@ -801,6 +801,14 @@ void block::print (ostream& o) const o << "}"; } +block::block (statement* car, statement* cdr) +{ + statements.push_back(car); + statements.push_back(cdr); + this->tok = car->tok; +} + + void for_loop::print (ostream& o) const { @@ -470,6 +470,8 @@ struct block: public statement std::vector<statement*> statements; void print (std::ostream& o) const; void visit (visitor* u); + block () {} + block (statement* car, statement* cdr); }; @@ -605,7 +607,7 @@ std::ostream& operator << (std::ostream& o, const probe_point& k); struct probe { std::vector<probe_point*> locations; - block* body; + statement* body; const token* tok; std::vector<vardecl*> locals; std::vector<vardecl*> unused_locals; diff --git a/systemtap.spec.in b/systemtap.spec.in index dd105790..e663b15a 100644 --- a/systemtap.spec.in +++ b/systemtap.spec.in @@ -1,19 +1,9 @@ -# Release number for rpm build. Stays at 1 for new PACKAGE_VERSION increases. %define release 1 -# Version number of oldest elfutils release that works with systemtap. -%define elfutils_version 0.127 - -# Default options (suitable for fedora) %define with_sqlite 1 %define with_docs 1 %define with_crash 0 %define with_bundled_elfutils 0 - -# Enable these options by default for RHEL -%if 0%{?rhel} >= 5 -%define with_crash 1 -%define with_bundled_elfutils 1 -%endif +%define elfutils_version 0.127 Name: systemtap Version: @VERSION@ diff --git a/tapset/ChangeLog b/tapset/ChangeLog index dae8b452..cf41c9cb 100644 --- a/tapset/ChangeLog +++ b/tapset/ChangeLog @@ -1,3 +1,18 @@ +2008-04-29 Frank Ch. Eigler <fche@elastic.org> + + PR 6466 + * tcp.stp (tcp_sockstate_str, tcp_sockopt_str): Initialize + number->string lookup tables here, instead of "probe begin(-1)" + block that can be elided/warned. + +2008-04-16 Wenji Huang <wenji.huang@oracle.com> + + * scsi.stp (scsi.iodispatching): Correct for 2.6.25 kernel. + +2008-04-15 Martin Hunt <hunt@monkey> + + * context.stp (print_backtrace, backtrace): Use MAXTRACE. + 2008-03-21 Eugene Teo <eugeneteo@kernel.sg> PR 5528 diff --git a/tapset/context.stp b/tapset/context.stp index 4aa75158..dc560316 100644 --- a/tapset/context.stp +++ b/tapset/context.stp @@ -15,7 +15,7 @@ function print_regs () %{ function print_backtrace () %{ if (CONTEXT->regs) { - _stp_stack_print(CONTEXT->regs, 1, CONTEXT->pi); + _stp_stack_print(CONTEXT->regs, 1, CONTEXT->pi, MAXTRACE); } else { _stp_printf("Systemtap probe: %s\n", CONTEXT->probe_point); } @@ -23,7 +23,7 @@ function print_backtrace () %{ function backtrace:string () %{ /* pure */ if (CONTEXT->regs) - _stp_stack_snprint (THIS->__retvalue, MAXSTRINGLEN, CONTEXT->regs, 0, CONTEXT->pi); + _stp_stack_snprint (THIS->__retvalue, MAXSTRINGLEN, CONTEXT->regs, 0, CONTEXT->pi, MAXTRACE); else strlcpy (THIS->__retvalue, "", MAXSTRINGLEN); %} diff --git a/tapset/scsi.stp b/tapset/scsi.stp index c9103cd6..7787b6d6 100644 --- a/tapset/scsi.stp +++ b/tapset/scsi.stp @@ -43,8 +43,13 @@ probe scsi.iodispatching dev_id = $cmd->device->id device_state = $cmd->device->sdev_state data_direction = $cmd->sc_data_direction +%( kernel_v >= "2.6.25" %? + request_buffer = $cmd->sdb->table->sgl + request_bufflen = $cmd->sdb->length +%: request_buffer = $cmd->request_buffer request_bufflen = $cmd->request_bufflen +%) req_addr = $cmd->request } diff --git a/tapset/tcp.stp b/tapset/tcp.stp index 3c3bd23b..7fa48000 100644 --- a/tapset/tcp.stp +++ b/tapset/tcp.stp @@ -71,7 +71,23 @@ function tcp_ts_get_info_state:long(sock:long) CATCH_DEREF_FAULT(); %} +global sockstate[13], sockstate_init_p function tcp_sockstate_str:string (state:long) { + if (! sockstate_init_p) { + sockstate_init_p = 1 + sockstate[1] = "TCP_ESTABLISHED" + sockstate[2] = "TCP_SYN_SENT" + sockstate[3] = "TCP_SYN_RECV" + sockstate[4] = "TCP_FIN_WAIT1" + sockstate[5] = "TCP_FIN_WAIT2" + sockstate[6] = "TCP_TIME_WAIT" + sockstate[7] = "TCP_CLOSE" + sockstate[8] = "TCP_CLOSE_WAIT" + sockstate[9] = "TCP_LAST_ACK" + sockstate[10] = "TCP_LISTEN" + sockstate[11] = "TCP_CLOSING" + sockstate[12] = "TCP_MAX_STATES" + } return (state in sockstate ? sockstate[state] : "UNDEF") } @@ -105,7 +121,25 @@ function tcp_ts_get_info_rcv_mss:long(sock:long) CATCH_DEREF_FAULT(); %} +global sockopt[15], sockopt_init_p function tcp_sockopt_str:string (optname:long) { + if (!sockopt_init_p) { + sockopt_init_p=1 + sockopt[1] = "TCP_NODELAY" + sockopt[2] = "TCP_MAXSEG" + sockopt[3] = "TCP_CORK" + sockopt[4] = "TCP_KEEPIDLE" + sockopt[5] = "TCP_KEEPINTVL" + sockopt[6] = "TCP_KEEPCNT" + sockopt[7] = "TCP_SYNCNT" + sockopt[8] = "TCP_LINGER2" + sockopt[9] = "TCP_DEFER_ACCEPT" + sockopt[10] = "TCP_WINDOW_CLAMP" + sockopt[11] = "TCP_INFO" + sockopt[12] = "TCP_QUICKACK" + sockopt[13] = "TCP_CONGESTION" + sockopt[14] = "TCP_MD5SIG" + } return (optname in sockopt ? sockopt[optname] : "UNDEF") } @@ -257,34 +291,3 @@ probe tcp.setsockopt.return = kernel.function("tcp_setsockopt").return { ret = $return } -global sockopt[15], sockstate[13] - -probe begin(-1) { - sockopt[1] = "TCP_NODELAY" - sockopt[2] = "TCP_MAXSEG" - sockopt[3] = "TCP_CORK" - sockopt[4] = "TCP_KEEPIDLE" - sockopt[5] = "TCP_KEEPINTVL" - sockopt[6] = "TCP_KEEPCNT" - sockopt[7] = "TCP_SYNCNT" - sockopt[8] = "TCP_LINGER2" - sockopt[9] = "TCP_DEFER_ACCEPT" - sockopt[10] = "TCP_WINDOW_CLAMP" - sockopt[11] = "TCP_INFO" - sockopt[12] = "TCP_QUICKACK" - sockopt[13] = "TCP_CONGESTION" - sockopt[14] = "TCP_MD5SIG" - - sockstate[1] = "TCP_ESTABLISHED" - sockstate[2] = "TCP_SYN_SENT" - sockstate[3] = "TCP_SYN_RECV" - sockstate[4] = "TCP_FIN_WAIT1" - sockstate[5] = "TCP_FIN_WAIT2" - sockstate[6] = "TCP_TIME_WAIT" - sockstate[7] = "TCP_CLOSE" - sockstate[8] = "TCP_CLOSE_WAIT" - sockstate[9] = "TCP_LAST_ACK" - sockstate[10] = "TCP_LISTEN" - sockstate[11] = "TCP_CLOSING" - sockstate[12] = "TCP_MAX_STATES" -} diff --git a/tapsets.cxx b/tapsets.cxx index 47bb20e2..d6867e35 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -218,6 +218,16 @@ common_probe_entryfn_prologue (translator_output* o, string statestr, o->newline() << "#ifdef STP_TIMING"; o->newline() << "c->statp = 0;"; o->newline() << "#endif"; + // NB: The following would actually be incorrect. + // That's because cycles_sum/cycles_base values are supposed to survive + // between consecutive probes. Periodically (STP_OVERLOAD_INTERVAL + // cycles), the values will be reset. + /* + o->newline() << "#ifdef STP_OVERLOAD"; + o->newline() << "c->cycles_sum = 0;"; + o->newline() << "c->cycles_base = 0;"; + o->newline() << "#endif"; + */ } @@ -869,7 +879,7 @@ struct dwflpp void setup(bool kernel, bool debuginfo_needed = true) { // XXX: this is where the session -R parameter could come in - static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug"; + static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build"; static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH"); static char *debuginfo_path = (debuginfo_env_arr ? @@ -2742,6 +2752,14 @@ dwarf_query::build_blacklist() blfn += "|raw_.*"; blfn += "|.*seq_.*lock.*"; + // atomic functions + blfn += "|atomic_.*"; + blfn += "|atomic64_.*"; + + // few other problematic cases + blfn += "|get_bh"; + blfn += "|put_bh"; + // Experimental blfn += "|.*apic.*|.*APIC.*"; blfn += "|.*softirq.*"; @@ -2885,7 +2903,13 @@ dwarf_query::blacklisted_p(const string& funcname, Dwarf_Addr addr) { if (section.substr(0, 6) == string(".init.") || - section.substr(0, 6) == string(".exit.")) + section.substr(0, 6) == string(".exit.") || + section.substr(0, 9) == string(".devinit.") || + section.substr(0, 9) == string(".devexit.") || + section.substr(0, 9) == string(".cpuinit.") || + section.substr(0, 9) == string(".cpuexit.") || + section.substr(0, 9) == string(".meminit.") || + section.substr(0, 9) == string(".memexit.")) { // NB: module .exit. routines could be probed in theory: // if the exit handler in "struct module" is diverted, @@ -3025,6 +3049,7 @@ dwarf_query::add_probe_point(const string& funcname, if (! bad) { + sess.unwindsym_modules.insert (module); probe = new dwarf_derived_probe(funcname, filename, line, module, reloc_section, addr, reloc_addr, *this, scope_die); results.push_back(probe); @@ -3071,7 +3096,6 @@ dwarf_query::assess_dbinfo_reqt() } - // The critical determining factor when interpreting a pattern // string is, perhaps surprisingly: "presence of a lineno". The // presence of a lineno changes the search strategy completely. @@ -3938,7 +3962,7 @@ dwarf_var_expanding_copy_visitor::visit_target_symbol (target_symbol *e) expr_statement* es = new expr_statement; es->tok = e->tok; es->value = a; - add_probe->body->statements.push_back (es); + add_probe->body = new block(add_probe->body, es); vardecl* vd = new vardecl; vd->tok = e->tok; @@ -3971,7 +3995,7 @@ dwarf_var_expanding_copy_visitor::visit_target_symbol (target_symbol *e) es->tok = e->tok; es->value = a; - add_probe->body->statements.push_back (es); + add_probe->body = new block(add_probe->body, es); // (4) Provide the '_dwarf_tvar_{name}_{num}_tmp' variable to // our parent so it can be used as a substitute for the target @@ -4144,12 +4168,12 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, if (!null_die(scope_die)) { dwarf_var_expanding_copy_visitor v (q, scope_die, dwfl_addr); - require <block*> (&v, &(this->body), this->body); + require <statement*> (&v, &(this->body), this->body); // If during target-variable-expanding the probe, we added a new block // of code, add it to the start of the probe. if (v.add_block) - this->body->statements.insert(this->body->statements.begin(), v.add_block); + this->body = new block(v.add_block, this->body); // If when target-variable-expanding the probe, we added a new // probe, add it in a new file to the list of files to be processed. @@ -4594,6 +4618,7 @@ dwarf_builder::build(systemtap_session & sess, q.statement_num_val, q.statement_num_val, q, 0); finished_results.push_back (p); + sess.unwindsym_modules.insert ("kernel"); return; } @@ -4896,6 +4921,648 @@ module_info::~module_info() // ------------------------------------------------------------------------ +// task_finder derived 'probes': These don't really exist. The whole +// purpose of the task_finder_derived_probe_group is to make sure that +// stap_start_task_finder()/stap_stop_task_finder() get called only +// once and in the right place. +// ------------------------------------------------------------------------ + +struct task_finder_derived_probe: public derived_probe +{ + // Dummy constructor for gcc 3.4 compatibility + task_finder_derived_probe (): derived_probe (0) { assert(0); } +}; + + +struct task_finder_derived_probe_group: public generic_dpg<task_finder_derived_probe> +{ +public: + static void create_session_group (systemtap_session& s); + + void emit_module_decls (systemtap_session& ) { } + void emit_module_init (systemtap_session& s); + void emit_module_exit (systemtap_session& s); +}; + + +void +task_finder_derived_probe_group::create_session_group (systemtap_session& s) +{ + if (! s.task_finder_derived_probes) + { + s.task_finder_derived_probes = new task_finder_derived_probe_group(); + + // Make sure we've got the stuff we need early in the output code. + embeddedcode *ec = new embeddedcode; + ec->tok = NULL; + ec->code = string("#if ! defined(CONFIG_UTRACE)\n") + + string("#error \"Need CONFIG_UTRACE!\"\n") + + string("#endif\n") + + string("#include <linux/utrace.h>\n") + + string("#include <linux/mount.h>\n") + + string("#include \"task_finder.c\"\n"); + + s.embeds.push_back(ec); + } +} + + +void +task_finder_derived_probe_group::emit_module_init (systemtap_session& s) +{ + s.op->newline(); + s.op->newline() << "/* ---- task finder ---- */"; + s.op->newline() << "rc = stap_start_task_finder();"; + + s.op->newline() << "if (rc) {"; + s.op->indent(1); + s.op->newline() << "stap_stop_task_finder();"; + s.op->newline(-1) << "}"; +} + + +void +task_finder_derived_probe_group::emit_module_exit (systemtap_session& s) +{ + s.op->newline(); + s.op->newline() << "/* ---- task finder ---- */"; + s.op->newline() << "stap_stop_task_finder();"; +} + + +// ------------------------------------------------------------------------ +// utrace user-space probes +// ------------------------------------------------------------------------ + +// Since we don't have access to <linux/utrace.h>, we'll have to +// define our own version of the UTRACE_EVENT flags. +enum utrace_derived_probe_flags { + UDPF_NONE, + UDPF_QUIESCE, // UTRACE_EVENT(QUIESCE) + UDPF_REAP, // UTRACE_EVENT(REAP) + UDPF_CLONE, // UTRACE_EVENT(CLONE) + UDPF_VFORK_DONE, // UTRACE_EVENT(VFORK_DONE) + UDPF_EXEC, // UTRACE_EVENT(EXEC) + UDPF_EXIT, // UTRACE_EVENT(EXIT) + UDPF_DEATH, // UTRACE_EVENT(DEATH) + UDPF_SYSCALL_ENTRY, // UTRACE_EVENT(SYSCALL_ENTRY) + UDPF_SYSCALL_EXIT, // UTRACE_EVENT(SYSCALL_EXIT) + UDPF_SIGNAL, // UTRACE_EVENT(SIGNAL) + UDPF_SIGNAL_IGN, // UTRACE_EVENT(SIGNAL_IGN) + UDPF_SIGNAL_STOP, // UTRACE_EVENT(SIGNAL_STOP) + UDPF_SIGNAL_TERM, // UTRACE_EVENT(SIGNAL_TERM) + UDPF_SIGNAL_CORE, // UTRACE_EVENT(SIGNAL_CORE) + UDPF_JCTL, // UTRACE_EVENT(JCTL) + UDPF_NFLAGS +}; + +struct utrace_derived_probe: public derived_probe +{ + bool has_path; + string path; + int64_t pid; + enum utrace_derived_probe_flags flags; + bool target_symbol_seen; + + utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l, + bool hp, string &pn, int64_t pd, + enum utrace_derived_probe_flags f); + void join_group (systemtap_session& s); +}; + + +struct utrace_derived_probe_group: public generic_dpg<utrace_derived_probe> +{ +private: + map<string, vector<utrace_derived_probe*> > probes_by_path; + typedef map<string, vector<utrace_derived_probe*> >::iterator p_b_path_iterator; + map<int64_t, vector<utrace_derived_probe*> > probes_by_pid; + typedef map<int64_t, vector<utrace_derived_probe*> >::iterator p_b_pid_iterator; + unsigned num_probes; + bool flags_seen[UDPF_NFLAGS]; + + void emit_probe_decl (systemtap_session& s, utrace_derived_probe *p); + +public: + utrace_derived_probe_group(): num_probes(0), flags_seen() { } + + void enroll (utrace_derived_probe* probe); + void emit_module_decls (systemtap_session& s); + void emit_module_init (systemtap_session& s); + void emit_module_exit (systemtap_session& s); +}; + + +struct utrace_var_expanding_copy_visitor: public var_expanding_copy_visitor +{ + utrace_var_expanding_copy_visitor(systemtap_session& s, const string& pn, + enum utrace_derived_probe_flags f): + sess (s), probe_name (pn), flags (f), target_symbol_seen (false) {} + + systemtap_session& sess; + string probe_name; + enum utrace_derived_probe_flags flags; + bool target_symbol_seen; + + void visit_target_symbol (target_symbol* e); +}; + + +utrace_derived_probe::utrace_derived_probe (systemtap_session &s, + probe* p, probe_point* l, + bool hp, string &pn, int64_t pd, + enum utrace_derived_probe_flags f): + derived_probe(p, l), has_path(hp), path(pn), pid(pd), flags(f), + target_symbol_seen(false) +{ + // Make a local-variable-expanded copy of the probe body + utrace_var_expanding_copy_visitor v (s, name, flags); + require <statement*> (&v, &(this->body), base->body); + target_symbol_seen = v.target_symbol_seen; +} + + +void +utrace_derived_probe::join_group (systemtap_session& s) +{ + if (! s.utrace_derived_probes) + { + s.utrace_derived_probes = new utrace_derived_probe_group (); + + // Make sure <linux/tracehook.h> is included early. + embeddedcode *ec = new embeddedcode; + ec->tok = NULL; + ec->code = string("#include <linux/tracehook.h>\n"); + s.embeds.push_back(ec); + } + s.utrace_derived_probes->enroll (this); + + task_finder_derived_probe_group::create_session_group (s); +} + + +void +utrace_var_expanding_copy_visitor::visit_target_symbol (target_symbol* e) +{ + assert(e->base_name.size() > 0 && e->base_name[0] == '$'); + + if (flags != UDPF_SYSCALL_ENTRY && flags != UDPF_SYSCALL_EXIT) + throw semantic_error ("only \"process(PATH_OR_PID).syscall\" and \"process(PATH_OR_PID).syscall.return\" probes support target symbols", + e->tok); + + if (e->base_name != "$syscall") + throw semantic_error ("invalid target symbol for utrace probe, $syscall expected", + e->tok); + + if (e->components.size() > 0) + { + switch (e->components[0].first) + { + case target_symbol::comp_literal_array_index: + throw semantic_error("utrace target variable '$syscall' may not be used as array", + e->tok); + break; + case target_symbol::comp_struct_member: + throw semantic_error("utrace target variable '$syscall' may not be used as a structure", + e->tok); + break; + default: + throw semantic_error ("invalid use of utrace target variable '$syscall'", + e->tok); + break; + } + } + + bool lvalue = is_active_lvalue(e); + if (lvalue) + throw semantic_error("utrace $syscall variable is read-only", e->tok); + + // Remember that we've seen a target variable. + target_symbol_seen = true; + + // Synthesize a function. + functiondecl *fdecl = new functiondecl; + fdecl->tok = e->tok; + embeddedcode *ec = new embeddedcode; + ec->tok = e->tok; + + string fname = (string("_utrace_syscall_get") + "_" + + lex_cast<string>(tick++)); + string locvalue = "CONTEXT->data"; + + ec->code = string("THIS->__retvalue = *tracehook_syscall_callno(CONTEXT->regs); /* pure */"); + + fdecl->name = fname; + fdecl->body = ec; + fdecl->type = pe_long; + + sess.functions.push_back(fdecl); + + // Synthesize a functioncall. + functioncall* n = new functioncall; + n->tok = e->tok; + n->function = fname; + n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session + + provide <functioncall*> (this, n); +} + + +struct utrace_builder: public derived_probe_builder +{ + utrace_builder() {} + virtual void build(systemtap_session & sess, + probe * base, + probe_point * location, + std::map<std::string, literal *> const & parameters, + vector<derived_probe *> & finished_results) + { + string path; + int64_t pid; + + bool has_path = get_param (parameters, TOK_PROCESS, path); + bool has_pid = get_param (parameters, TOK_PROCESS, pid); + enum utrace_derived_probe_flags flags = UDPF_NONE; + assert (has_path || has_pid); + + if (has_null_param (parameters, "death")) + flags = UDPF_DEATH; + else if (has_null_param (parameters, "syscall")) + { + if (has_null_param (parameters, TOK_RETURN)) + flags = UDPF_SYSCALL_EXIT; + else + flags = UDPF_SYSCALL_ENTRY; + } + else if (has_null_param (parameters, "clone")) + flags = UDPF_CLONE; + else if (has_null_param (parameters, "exec")) + flags = UDPF_EXEC; + + // If we have a path, we need to validate it. + if (has_path) + { + string::size_type start_pos, end_pos; + string component; + + // XXX: these checks should be done in terms of filesystem + // operations. + + // Make sure it starts with '/'. + if (path[0] != '/') + throw semantic_error ("process path must start with a '/'", + location->tok); + + start_pos = 1; // get past the initial '/' + while ((end_pos = path.find('/', start_pos)) != string::npos) + { + component = path.substr(start_pos, end_pos - start_pos); + // Make sure it isn't empty. + if (component.size() == 0) + throw semantic_error ("process path component cannot be empty", + location->tok); + // Make sure it isn't relative. + else if (component == "." || component == "..") + throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); + + start_pos = end_pos + 1; + } + component = path.substr(start_pos); + // Make sure it doesn't end with '/'. + if (component.size() == 0) + throw semantic_error ("process path cannot end with a '/'", location->tok); + // Make sure it isn't relative. + else if (component == "." || component == "..") + throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); + + sess.unwindsym_modules.insert (path); + } + + finished_results.push_back(new utrace_derived_probe(sess, base, location, + has_path, path, pid, + flags)); + } +}; + + +void +utrace_derived_probe_group::enroll (utrace_derived_probe* p) +{ + if (p->has_path) + probes_by_path[p->path].push_back(p); + else + probes_by_pid[p->pid].push_back(p); + num_probes++; + flags_seen[p->flags] = true; + + // XXX: multiple exec probes (for instance) for the same path (or + // pid) should all share a utrace report function, and have their + // handlers executed sequentially. +} + + +void +utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, + utrace_derived_probe *p) +{ + s.op->newline() << "{"; + s.op->line() << " .tgt={"; + + if (p->has_path) + { + s.op->line() << " .pathname=\"" << p->path << "\","; + s.op->line() << " .pid=0,"; + } + else + { + s.op->line() << " .pathname=NULL,"; + s.op->line() << " .pid=" << p->pid << ","; + } + + s.op->line() << " .callback=&_stp_utrace_probe_cb,"; + s.op->line() << " },"; + s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ","; + s.op->line() << " .ph=&" << p->name << ","; + + // Handle flags + switch (p->flags) + { + case UDPF_CLONE: + s.op->line() << " .ops={ .report_clone=stap_utrace_probe_clone, .report_death=stap_utrace_task_finder_report_death },"; + s.op->line() << " .flags=(UTRACE_EVENT(CLONE)|UTRACE_EVENT(DEATH)),"; + break; + case UDPF_EXEC: + // Notice we're not setting up a .ops/.report_exec handler here. + // Instead, we'll just call the probe directly when we get + // notified the exec happened. + s.op->line() << " .flags=(UTRACE_EVENT(EXEC)),"; + break; + case UDPF_DEATH: + // Notice we're not setting up a .ops/.report_death handler + // here. Instead, we'll just call the probe directly when we + // get notified the death happened. + s.op->line() << " .flags=(UTRACE_EVENT(DEATH)),"; + break; + case UDPF_SYSCALL_ENTRY: + s.op->line() << " .ops={ .report_syscall_entry=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },"; + s.op->line() << " .flags=(UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)),"; + break; + case UDPF_SYSCALL_EXIT: + s.op->line() << " .ops={ .report_syscall_exit=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },"; + s.op->line() << " .flags=(UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)),"; + break; + default: + throw semantic_error ("bad utrace probe flag"); + break; + } + s.op->line() << " .engine_attached=0,"; + s.op->line() << " },"; +} + + +void +utrace_derived_probe_group::emit_module_decls (systemtap_session& s) +{ + if (probes_by_path.empty() && probes_by_pid.empty()) + return; + + s.op->newline(); + s.op->newline() << "/* ---- utrace probes ---- */"; + s.op->newline() << "struct stap_utrace_probe {"; + s.op->indent(1); + s.op->newline() << "struct stap_task_finder_target tgt;"; + s.op->newline() << "const char *pp;"; + s.op->newline() << "void (*ph) (struct context*);"; + s.op->newline() << "struct utrace_engine_ops ops;"; + s.op->newline() << "unsigned long flags;"; + s.op->newline() << "int engine_attached;"; + s.op->newline(-1) << "};"; + + // Output handler function for CLONE events + if (flags_seen[UDPF_CLONE]) + { + s.op->newline() << "static u32 stap_utrace_probe_clone(struct utrace_attached_engine *engine, struct task_struct *parent, unsigned long clone_flags, struct task_struct *child) {"; + s.op->indent(1); + s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;"; + + common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING"); + s.op->newline() << "c->probe_point = p->pp;"; + + // call probe function + s.op->newline() << "(*p->ph) (c);"; + common_probe_entryfn_epilogue (s.op); + + s.op->newline() << "return UTRACE_ACTION_RESUME;"; + s.op->newline(-1) << "}"; + } + + // Output handler function for EXEC and DEATH events + if (flags_seen[UDPF_EXEC] || flags_seen[UDPF_DEATH]) + { + s.op->newline() << "static void stap_utrace_probe_handler(struct task_struct *tsk, struct stap_utrace_probe *p) {"; + s.op->indent(1); + + common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING"); + s.op->newline() << "c->probe_point = p->pp;"; + + // call probe function + s.op->newline() << "(*p->ph) (c);"; + common_probe_entryfn_epilogue (s.op); + + s.op->newline() << "return;"; + s.op->newline(-1) << "}"; + } + + // Output handler function for SYSCALL_ENTRY and SYSCALL_EXIT events + if (flags_seen[UDPF_SYSCALL_ENTRY] || flags_seen[UDPF_SYSCALL_EXIT]) + { + s.op->newline() << "static u32 stap_utrace_probe_syscall(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {"; + s.op->indent(1); + s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;"; + + common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING"); + s.op->newline() << "c->probe_point = p->pp;"; + s.op->newline() << "c->regs = regs;"; + + // call probe function + s.op->newline() << "(*p->ph) (c);"; + common_probe_entryfn_epilogue (s.op); + + s.op->newline() << "return UTRACE_ACTION_RESUME;"; + s.op->newline(-1) << "}"; + } + + // Output task finder callback routine that gets called for all + // utrace probe types. + s.op->newline() << "static int _stp_utrace_probe_cb(struct task_struct *tsk, int register_p, struct stap_task_finder_target *tgt) {"; + s.op->indent(1); + s.op->newline() << "int rc = 0;"; + s.op->newline() << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);"; + s.op->newline() << "struct utrace_attached_engine *engine;"; + + s.op->newline() << "if (register_p) {"; + s.op->indent(1); + + s.op->newline() << "switch (p->flags) {"; + s.op->indent(1); + // When registering an exec probe, we can't install a utrace engine, + // since we're already in a exec event. So, we just call the probe + // directly. Note that for existing threads, this won't really work + // since our state isn't STAP_SESSION_RUNNING yet. But that's OK, + // since this isn't really a 'exec' event - it is a notification + // that task_finder found an interesting process. + if (flags_seen[UDPF_EXEC]) + { + s.op->newline() << "case UTRACE_EVENT(EXEC):"; + s.op->indent(1); + s.op->newline() << "stap_utrace_probe_handler(tsk, p);"; + s.op->newline() << "break;"; + s.op->indent(-1); + } + // For death probes, do nothing at registration time. We'll handle + // these in the 'register_p == 0' case. + if (flags_seen[UDPF_DEATH]) + { + s.op->newline() << "case UTRACE_EVENT(DEATH):"; + s.op->indent(1); + s.op->newline() << "break;"; + s.op->indent(-1); + } + // Attach an engine for CLONE, SYSCALL_ENTRY, and SYSCALL_EXIT events. + if (flags_seen[UDPF_CLONE] || flags_seen[UDPF_SYSCALL_ENTRY] + || flags_seen[UDPF_SYSCALL_EXIT]) + { + s.op->newline() << "case (UTRACE_EVENT(CLONE)|UTRACE_EVENT(DEATH)):"; + s.op->newline() << "case (UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)):"; + s.op->newline() << "case (UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)):"; + s.op->indent(1); + s.op->newline() << "engine = utrace_attach(tsk, UTRACE_ATTACH_CREATE, &p->ops, p);"; + s.op->newline() << "if (IS_ERR(engine)) {"; + s.op->indent(1); + s.op->newline() << "int error = -PTR_ERR(engine);"; + s.op->newline() << "if (error != ENOENT) {"; + s.op->indent(1); + s.op->newline() << "_stp_error(\"utrace_attach returned error %d on pid %d\", error, (int)tsk->pid);"; + s.op->newline() << "rc = error;"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + s.op->newline() << "else if (unlikely(engine == NULL)) {"; + s.op->indent(1); + s.op->newline() << "_stp_error(\"utrace_attach returned NULL on pid %d!\", (int)tsk->pid);"; + s.op->newline() << "rc = ENOENT;"; + s.op->newline(-1) << "}"; + s.op->newline() << "else {"; + s.op->indent(1); + s.op->newline() << "utrace_set_flags(tsk, engine, p->flags);"; + s.op->newline() << "p->engine_attached = 1;"; + s.op->newline(-1) << "}"; + s.op->newline() << "break;"; + s.op->indent(-1); + } + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + // Since this engine could be attached to multiple threads, don't + // cleanup here. We'll cleanup at module unload time. + s.op->newline() << "else {"; + s.op->indent(1); + // For death probes, go ahead and call the probe directly. + if (flags_seen[UDPF_DEATH]) + { + s.op->newline() << "if (p->flags == UTRACE_EVENT(DEATH)) {"; + s.op->indent(1); + s.op->newline() << "stap_utrace_probe_handler(tsk, p);"; + s.op->newline(-1) << "}"; + } + s.op->newline(-1) << "}"; + s.op->newline() << "return rc;"; + s.op->newline(-1) << "}"; + + s.op->newline() << "struct stap_utrace_probe stap_utrace_probes[] = {"; + s.op->indent(1); + + // Set up 'process(PATH)' probes + if (! probes_by_path.empty()) + { + for (p_b_path_iterator it = probes_by_path.begin(); + it != probes_by_path.end(); it++) + { + for (unsigned i = 0; i < it->second.size(); i++) + { + utrace_derived_probe *p = it->second[i]; + emit_probe_decl(s, p); + } + } + } + + // Set up 'process(PID)' probes + if (! probes_by_pid.empty()) + { + for (p_b_pid_iterator it = probes_by_pid.begin(); + it != probes_by_pid.end(); it++) + { + for (unsigned i = 0; i < it->second.size(); i++) + { + utrace_derived_probe *p = it->second[i]; + emit_probe_decl(s, p); + } + } + } + s.op->newline(-1) << "};"; +} + + +void +utrace_derived_probe_group::emit_module_init (systemtap_session& s) +{ + if (probes_by_path.empty() && probes_by_pid.empty()) + return; + + s.op->newline(); + s.op->newline() << "/* ---- utrace probes ---- */"; + + s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {"; + s.op->indent(1); + s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];"; + s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);"; + s.op->newline(-1) << "}"; + + // rollback all utrace probes + s.op->newline() << "if (rc) {"; + s.op->indent(1); + s.op->newline() << "for (j=i-1; j>=0; j--) {"; + s.op->indent(1); + s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[j];"; + + s.op->newline() << "if (p->engine_attached) {"; + s.op->indent(1); + s.op->newline() << "stap_utrace_detach_ops(&p->ops);"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + + s.op->newline(-1) << "}"; +} + + +void +utrace_derived_probe_group::emit_module_exit (systemtap_session& s) +{ + if (probes_by_path.empty() && probes_by_pid.empty()) return; + + s.op->newline(); + s.op->newline() << "/* ---- utrace probes ---- */"; + s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {"; + s.op->indent(1); + s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];"; + + s.op->newline() << "if (p->engine_attached) {"; + s.op->indent(1); + s.op->newline() << "stap_utrace_detach_ops(&p->ops);"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; +} + + +// ------------------------------------------------------------------------ // user-space probes // ------------------------------------------------------------------------ @@ -5359,6 +6026,7 @@ profile_derived_probe_group::emit_module_exit (systemtap_session& s) } + // ------------------------------------------------------------------------ // procfs file derived probes // ------------------------------------------------------------------------ @@ -5426,7 +6094,7 @@ procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p, { // Make a local-variable-expanded copy of the probe body procfs_var_expanding_copy_visitor v (s, name, path, write); - require <block*> (&v, &(this->body), base->body); + require <statement*> (&v, &(this->body), base->body); target_symbol_seen = v.target_symbol_seen; } @@ -5837,6 +6505,7 @@ procfs_builder::build(systemtap_session & sess, } + // ------------------------------------------------------------------------ // statically inserted macro-based derived probes // ------------------------------------------------------------------------ @@ -6085,7 +6754,7 @@ mark_derived_probe::mark_derived_probe (systemtap_session &s, // Now make a local-variable-expanded copy of the probe body mark_var_expanding_copy_visitor v (sess, name, mark_args); - require <block*> (&v, &(this->body), base->body); + require <statement*> (&v, &(this->body), base->body); target_symbol_seen = v.target_symbol_seen; if (sess.verbose > 2) @@ -6237,7 +6906,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); } @@ -6316,12 +6997,6 @@ mark_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "/* ---- marker probes ---- */"; - // 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(); - s.op->newline() << "struct stap_marker_probe {"; s.op->newline(1) << "const char * const name;"; s.op->newline() << "const char * const format;"; @@ -6473,6 +7148,11 @@ mark_builder::build(systemtap_session & sess, string::size_type notwhite = format.find_first_not_of(" \t"); format.erase(0, notwhite); + // If the format is empty, make sure we add back a space + // character, which is what MARK_NOARGS expands to. + if (format.length() == 0) + format = " "; + if (sess.verbose>3) clog << "'" << name << "' '" << module << "' '" << format << "'" << endl; @@ -6535,6 +7215,7 @@ mark_builder::build(systemtap_session & sess, } + // ------------------------------------------------------------------------ // hrtimer derived probes // ------------------------------------------------------------------------ @@ -6828,6 +7509,7 @@ timer_builder::register_patterns(match_node *root) } + // ------------------------------------------------------------------------ // perfmon derived probes // ------------------------------------------------------------------------ @@ -6970,7 +7652,7 @@ perfmon_derived_probe::perfmon_derived_probe (probe* p, probe_point* l, // Now make a local-variable-expanded copy of the probe body perfmon_var_expanding_copy_visitor v (sess, probes_allocated-1); - require <block*> (&v, &(this->body), base->body); + require <statement*> (&v, &(this->body), base->body); if (sess.verbose > 1) clog << "perfmon-based probe" << endl; @@ -7278,6 +7960,28 @@ register_standard_tapsets(systemtap_session & s) ->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)->bind(TOK_RETURN) ->bind(new uprobe_builder ()); + // utrace user-space probes + s.pattern_root->bind_str(TOK_PROCESS)->bind("clone") + ->bind(new utrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("clone") + ->bind(new utrace_builder ()); + s.pattern_root->bind_str(TOK_PROCESS)->bind("exec") + ->bind(new utrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("exec") + ->bind(new utrace_builder ()); + s.pattern_root->bind_str(TOK_PROCESS)->bind("syscall") + ->bind(new utrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("syscall") + ->bind(new utrace_builder ()); + s.pattern_root->bind_str(TOK_PROCESS)->bind("syscall")->bind(TOK_RETURN) + ->bind(new utrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("syscall")->bind(TOK_RETURN) + ->bind(new utrace_builder ()); + s.pattern_root->bind_str(TOK_PROCESS)->bind("death") + ->bind(new utrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("death") + ->bind(new utrace_builder ()); + // marker-based parts s.pattern_root->bind("kernel")->bind_str("mark")->bind(new mark_builder()); s.pattern_root->bind("kernel")->bind_str("mark")->bind_str("format") @@ -7312,6 +8016,12 @@ all_session_groups(systemtap_session& s) DOONE(hrtimer); DOONE(perfmon); DOONE(procfs); + + // Another "order is important" item. We want to make sure we + // "register" the dummy task_finder probe group after all probe + // groups that use the task_finder. + DOONE(utrace); + DOONE(task_finder); #undef DOONE return g; } 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/ChangeLog b/testsuite/ChangeLog index 95c1f117..c951971b 100644 --- a/testsuite/ChangeLog +++ b/testsuite/ChangeLog @@ -1,3 +1,66 @@ +2008-04-29 Frank Ch. Eigler <fche@elastic.org> + + PR 6466. + * semko/twelve.stp, semok/twenty.stp, systemtap.base/cache.exp, + systemtap.base/maxactive.exp, systemtap.base/warnings.exp, + transok/eight.stp: Adapt to new elision capabilities; add new + side-effects or -w warning-suppression flags. + * systemtap.pass1-4/buildok.exp: Remove two kfails. + * semok/optimize.stp: Test new elision. + +2008-04-24 Will Cohen <wcohen@redhat.com> + + * systemtap.examples: examples moved into testsuite. + +2008-04-22 David Smith <dsmith@redhat.com> + + * systemtap.base/utrace_p5.exp: Simplfied a little. + + * systemtap.base/utrace_p5.exp: Added run-time utrace tests. + +2008-04-21 David Smith <dsmith@redhat.com> + + * parseko/utrace01.stp: Renamed from semko/utrace02.stp (since it + received a parse error, not a semantic error). + +2008-04-21 Martin Hunt <hunt@redhat.com> + + * systemtap.samples/args.exp: Remove obsolete "-d" option. + +2008-04-18 David Smith <dsmith@redhat.com> + + * systemtap.base/utrace_p4.exp: Added exec probe test. + + * buildok/utrace01.stp: Removed. + * buildok/utrace02.stp: Ditto. + * buildok/utrace03.stp: Ditto. + * systemtap.base/utrace_p4.exp: Rewrote buildok tests to check for + kernel utrace support. + +2008-04-17 David Smith <dsmith@redhat.com> + + * buildok/utrace01.stp: New test. + * buildok/utrace02.stp: Ditto. + * buildok/utrace03.stp: Ditto. + * semko/utrace01.stp: Ditto. + * semko/utrace02.stp: Ditto. + * semko/utrace03.stp: Ditto. + * semko/utrace04.stp: Ditto. + * semko/utrace05.stp: Ditto. + * semko/utrace06.stp: Ditto. + * semko/utrace07.stp: Ditto. + * semko/utrace08.stp: Ditto. + * semko/utrace09.stp: Ditto. + * semko/utrace10.stp: Ditto. + * semko/utrace11.stp: Ditto. + * semko/utrace12.stp: Ditto. + * semko/utrace13.stp: Ditto. + +2008-04-10 Frank Ch. Eigler <fche@elastic.org> + + PR 2949 + * systemtap.base/cmd_parse.exp: Add "-l" listing test. + 2008-04-04 Masami Hiramatsu <mhiramat@redhat.com> PR 5528 diff --git a/testsuite/parseko/utrace01.stp b/testsuite/parseko/utrace01.stp new file mode 100755 index 00000000..1cb4227f --- /dev/null +++ b/testsuite/parseko/utrace01.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# process NAME must be a string +probe process(/bin/cat).death { } diff --git a/testsuite/semko/twelve.stp b/testsuite/semko/twelve.stp index ab71d579..c91eb743 100755 --- a/testsuite/semko/twelve.stp +++ b/testsuite/semko/twelve.stp @@ -1,6 +1,6 @@ #! stap -p2 probe end { - for (a=0; "hello";) {} - while ("goodbye") {} + for (a=0; "hello";) {println("hello")} + while ("goodbye") {println("world")} } diff --git a/testsuite/semko/utrace01.stp b/testsuite/semko/utrace01.stp new file mode 100755 index 00000000..a4707008 --- /dev/null +++ b/testsuite/semko/utrace01.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# missing process NAME|PID +probe process.death { } diff --git a/testsuite/semko/utrace03.stp b/testsuite/semko/utrace03.stp new file mode 100755 index 00000000..c682410b --- /dev/null +++ b/testsuite/semko/utrace03.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# invalid probe type +probe process("/bin/cat").death.return { } diff --git a/testsuite/semko/utrace04.stp b/testsuite/semko/utrace04.stp new file mode 100755 index 00000000..6345f9f6 --- /dev/null +++ b/testsuite/semko/utrace04.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# death probes don't support target symbols +probe process("/bin/cat").death.return { print($syscall) } diff --git a/testsuite/semko/utrace05.stp b/testsuite/semko/utrace05.stp new file mode 100755 index 00000000..e99fd22f --- /dev/null +++ b/testsuite/semko/utrace05.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# can't write to $syscall +probe process("/bin/cat").syscall { $syscall = 1 } diff --git a/testsuite/semko/utrace06.stp b/testsuite/semko/utrace06.stp new file mode 100755 index 00000000..19e6043b --- /dev/null +++ b/testsuite/semko/utrace06.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# treat $syscall as a pointer +probe process("/bin/cat").syscall { print($syscall->foo) } diff --git a/testsuite/semko/utrace07.stp b/testsuite/semko/utrace07.stp new file mode 100755 index 00000000..2a9405b3 --- /dev/null +++ b/testsuite/semko/utrace07.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# treat $syscall as an array +probe process("/bin/cat").syscall { print($syscall[0]) } diff --git a/testsuite/semko/utrace08.stp b/testsuite/semko/utrace08.stp new file mode 100755 index 00000000..a558a5be --- /dev/null +++ b/testsuite/semko/utrace08.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# process path must be absolute +probe process("cat").death { } diff --git a/testsuite/semko/utrace09.stp b/testsuite/semko/utrace09.stp new file mode 100755 index 00000000..60c49cd2 --- /dev/null +++ b/testsuite/semko/utrace09.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# process path must be absolute +probe process("/foo/../bar").death { } diff --git a/testsuite/semko/utrace10.stp b/testsuite/semko/utrace10.stp new file mode 100755 index 00000000..b46baea9 --- /dev/null +++ b/testsuite/semko/utrace10.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# path can't contain an empty component +probe process("/foo//bar").death { } diff --git a/testsuite/semko/utrace11.stp b/testsuite/semko/utrace11.stp new file mode 100755 index 00000000..d78b602c --- /dev/null +++ b/testsuite/semko/utrace11.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# path can't end with '/' +probe process("/foo/bar/").death { } diff --git a/testsuite/semko/utrace12.stp b/testsuite/semko/utrace12.stp new file mode 100755 index 00000000..478aa1d3 --- /dev/null +++ b/testsuite/semko/utrace12.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# path can't end with '.' +probe process("/foo/bar/.").death { } diff --git a/testsuite/semko/utrace13.stp b/testsuite/semko/utrace13.stp new file mode 100755 index 00000000..16cc0391 --- /dev/null +++ b/testsuite/semko/utrace13.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# path can't end with '..' +probe process("/foo/bar/..").death { } diff --git a/testsuite/semok/optimize.stp b/testsuite/semok/optimize.stp index bcf8ac04..a728bd66 100755 --- a/testsuite/semok/optimize.stp +++ b/testsuite/semok/optimize.stp @@ -1,4 +1,4 @@ -#! stap -p2 +#! stap -wp2 # We count on the optimizer to blow away these ridiculous # expressions, since they have no effect on the output. @@ -21,3 +21,19 @@ probe begin { probe begin{for(i=1;i-=2;i++);} probe begin{while(i+=2);} probe begin{if(i=j);} + +global ii +probe begin{for(ii=1;ii-=2;ii++);} +probe begin{while(ii+=2);} +probe begin{if(ii=j);} + +# bug #6466 +global goo + +probe begin { while (24) ; } +probe begin { for (2<$i; zoo(333); poo) ; } +probe begin { foreach (x in goo) goo[x]+1; } +probe begin { foo = $bar; if (foo) {} } +probe begin { { { { } } } } +function useful () { return 1 } +probe begin { println (useful()) } /* don't elide this one! */ diff --git a/testsuite/semok/twenty.stp b/testsuite/semok/twenty.stp index 2e85c5e6..46dcefca 100755 --- a/testsuite/semok/twenty.stp +++ b/testsuite/semok/twenty.stp @@ -1,4 +1,4 @@ -#! stap -p2 +#! stap -wp2 probe kernel.function("*") {} probe module("*").function("*") {} diff --git a/testsuite/systemtap.base/cache.exp b/testsuite/systemtap.base/cache.exp index eaa5ca82..26d7b0ef 100644 --- a/testsuite/systemtap.base/cache.exp +++ b/testsuite/systemtap.base/cache.exp @@ -74,8 +74,8 @@ if [info exists env(SYSTEMTAP_DIR)] { set env(SYSTEMTAP_DIR) $local_systemtap_dir # Set up the scripts we'll use. -set basic_script1 "\"probe begin { }\"" -set basic_script2 "\"probe begin, end { }\"" +set basic_script1 "\"probe begin { println(1) }\"" +set basic_script2 "\"probe begin, end { println(2) }\"" set error_script "\"probe XbeginX { }\"" # Check basic functionality. The 1st compilation of a script won't diff --git a/testsuite/systemtap.base/cmd_parse.exp b/testsuite/systemtap.base/cmd_parse.exp index ff347a9d..cbce0455 100644 --- a/testsuite/systemtap.base/cmd_parse.exp +++ b/testsuite/systemtap.base/cmd_parse.exp @@ -75,3 +75,12 @@ expect { eof {fail "cmd_parse7: unexpected EOF"} } wait + +spawn stap -l {vm.*} +expect { + -timeout 60 + -re "vm.*" {pass "cmd_parse8"} + timeout {fail "cmd_parse8: unexpected timeout"} + eof {fail "cmd_parse8: unexpected EOF"} +} +wait diff --git a/testsuite/systemtap.base/maxactive.exp b/testsuite/systemtap.base/maxactive.exp index ca95ac53..7c03a1bf 100644 --- a/testsuite/systemtap.base/maxactive.exp +++ b/testsuite/systemtap.base/maxactive.exp @@ -13,8 +13,9 @@ proc sleep_five_sec {} { # Script1. For 5 seconds, probe the return of "sys_select" and # "sys_read". See if we skip any probes. set script1 { + global foo probe kernel.function("sys_select").return, - kernel.function("sys_read").return { } + kernel.function("sys_read").return { foo++ } probe timer.ms(5000) { exit(); } probe begin { log("systemtap starting probe"); log("systemtap ending probe");} @@ -29,8 +30,9 @@ set skipped1 $skipped_probes # "sys_read", with a limit of 1 probe active at a time. See if we # skip any probes. set script2 { + global foo probe kernel.function("sys_select").return.maxactive(1), - kernel.function("sys_read").return.maxactive(1) { } + kernel.function("sys_read").return.maxactive(1) { foo++ } probe timer.ms(5000) { exit(); } probe begin { log("systemtap starting probe"); log("systemtap ending probe");} diff --git a/testsuite/systemtap.base/utrace_p4.exp b/testsuite/systemtap.base/utrace_p4.exp new file mode 100644 index 00000000..eb6ea685 --- /dev/null +++ b/testsuite/systemtap.base/utrace_p4.exp @@ -0,0 +1,106 @@ +# Utrace compile (pass 4) tests. We can't run these as +# testsuite/buildok tests, since if the current kernel has no utrace +# support, those will fail - but not because of a problem with +# systemtap's utrace probes (but because of the lack of utrace). So, +# this test script checks for the existence of utrace in the kernel. +# If utrace exists in the kernel, it tries some compile tests. If +# utrace doesn't exist in the kernel, marks the tests as 'untested'. + +# stap_compile TEST_NAME flags script args +# - TEST_NAME is the name of the current test +# - compile indicates whether the script is supposed to compile +# - script is the script to compile +# Additional arguments are passed to stap as-is. +proc stap_compile { TEST_NAME compile script args } { + set cmd [concat {stap -v -p4 -e} $script $args] + + verbose -log "running $cmd" + eval spawn $cmd + set compile_errors 0 + expect { + -re {^Pass\ [1234]:[^\r]*\ in\ .*\ ms.\r\n} {exp_continue} + -re {^Pass\ [34]: using cached [^\r\n]+\r\n} {exp_continue} + # pass-4 output + -re {^/[^\r\n]+.ko\r\n} {exp_continue} + -re "parse error" { incr compile_errors 1; exp_continue} + -re "compilation failed" {incr compile_errors 1; exp_continue} + -re "semantic error:" {incr compile_errors 1; exp_continue} + } + catch close + wait + + # If we've got compile errors and the script was supposed to + # compile, fail. + if {$compile_errors > 0} { + if {$compile == 1} { + fail "$TEST_NAME compilation failed" + } else { + pass "$TEST_NAME compilation failed correctly" + } + } else { + if {$compile == 1} { + pass "$TEST_NAME compilation succeeded" + } else { + fail "$TEST_NAME compilation succeeded unexpectedly" + } + } +} + +# Initialize variables +set utrace_support_found 0 + +set clone_script {"probe process(\"/bin/ls\").clone { print(\"ls clone\") }"} +set death_script {"probe process(\"/bin/ls\").death { print(\"ls death\") }"} +set syscall_script {"probe process(\"/bin/ls\").syscall { printf(\"|%d\", \$syscall) }"} +set syscall_return_script {"probe process(\"/bin/ls\").syscall.return { printf(\"|%d\", \$syscall) }"} +set exec_script {"probe process(\"/bin/ls\").exec { print(\"ls exec\") }"} + +# Try to find utrace_attach symbol in /proc/kallsyms +set path "/proc/kallsyms" +if {! [catch {exec grep -q utrace_attach $path} dummy]} { + set utrace_support_found 1 +} + +# +# Do some utrace compile tests. +# + +set TEST_NAME "UTRACE_P4_01" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} else { + # Try compiling a clone script + stap_compile $TEST_NAME 1 $clone_script +} + +set TEST_NAME "UTRACE_P4_02" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} else { + # Try compiling a death script + stap_compile $TEST_NAME 1 $death_script +} + +set TEST_NAME "UTRACE_P4_03" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} else { + # Try compiling a syscall script + stap_compile $TEST_NAME 1 $syscall_script +} + +set TEST_NAME "UTRACE_P4_04" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} else { + # Try compiling a syscall return script + stap_compile $TEST_NAME 1 $syscall_return_script +} + +set TEST_NAME "UTRACE_P4_05" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} else { + # Try compiling an exec script + stap_compile $TEST_NAME 1 $exec_script +} diff --git a/testsuite/systemtap.base/utrace_p5.exp b/testsuite/systemtap.base/utrace_p5.exp new file mode 100644 index 00000000..cbb867d1 --- /dev/null +++ b/testsuite/systemtap.base/utrace_p5.exp @@ -0,0 +1,140 @@ +# Utrace run (pass 5) tests. + +# Initialize variables +set utrace_support_found 0 +set exepath "[pwd]/cat_[pid]" + +set death_script { + global death_probes_fired = 0 + probe begin { printf("systemtap starting probe\n") } + probe process("%s").death { death_probes_fired++ } + probe end { printf("systemtap ending probe\n") + printf("deaths = %%d\n", death_probes_fired) } +} +set death_script_output "deaths = 1\r\n" + +set exec_script { + global exec_probes_fired = 0 + probe begin { printf("systemtap starting probe\n") } + probe process("%s").exec { exec_probes_fired++ } + probe end { printf("systemtap ending probe\n") + printf("execs = %%d\n", exec_probes_fired) } +} +set exec_script_output "execs = 1\r\n" + +set syscall_script { + global syscall_probes_fired = 0 + probe begin { printf("systemtap starting probe\n") } + probe process("%s").syscall { syscall_probes_fired++ } + probe end { printf("systemtap ending probe\n") + if (syscall_probes_fired > 0) { + printf("syscalls = %%d\n", syscall_probes_fired) + } + } +} +set syscall_script_output "syscalls = \\d+\r\n" + +set syscall_return_script { + global syscall_return_probes_fired = 0 + probe begin { printf("systemtap starting probe\n") } + probe process("%s").syscall.return { syscall_return_probes_fired++ } + probe end { printf("systemtap ending probe\n") + if (syscall_return_probes_fired > 0) { + printf("syscall_returns = %%d\n", syscall_return_probes_fired) + } + } +} +set syscall_return_script_output "syscall_returns = \\d+\r\n" + +set clone_script { + global clone_probes_fired = 0 + probe begin { printf("systemtap starting probe\n") } + probe process(%d).clone { clone_probes_fired++ } + probe end { printf("systemtap ending probe\n") + if (clone_probes_fired > 0) { + printf("clones = %%d\n", clone_probes_fired) + } + } +} +set clone_script_output "clones = \\d+\r\n" + +# Try to find utrace_attach symbol in /proc/kallsyms +set path "/proc/kallsyms" +if {! [catch {exec grep -q utrace_attach $path} dummy]} { + set utrace_support_found 1 +} + +# Set up our own copy of /bin/cat, to make testing for a particular +# executable easy. We can't use 'ln' here, since we might be creating +# a cross-device link. We can't use 'ln -s' here, since the kernel +# resolves the symbolic link and reports that /bin/cat is being +# exec'ed (instead of our local copy). +if {[catch {exec cp /bin/cat $exepath} res]} { + fail "unable to copy /bin/cat: $res" + return +} + +# "load" generation function for stap_run. It spawns our own copy of +# /bin/cat, waits 5 seconds, then kills it. +proc run_cat_5_sec {} { + global exepath + + spawn $exepath + set exe_id $spawn_id + after 5000; + exec kill -INT -[exp_pid -i $exe_id] + return 0; +} + +set TEST_NAME "UTRACE_P5_01" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME" +} else { + set script [format $death_script $exepath] + stap_run $TEST_NAME run_cat_5_sec $death_script_output -e $script +} + +set TEST_NAME "UTRACE_P5_02" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME" +} else { + set script [format $exec_script $exepath] + stap_run $TEST_NAME run_cat_5_sec $exec_script_output -e $script +} + +set TEST_NAME "UTRACE_P5_03" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME" +} else { + set script [format $syscall_script $exepath] + stap_run $TEST_NAME run_cat_5_sec $syscall_script_output -e $script +} + +set TEST_NAME "UTRACE_P5_04" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME" +} else { + set script [format $syscall_return_script $exepath] + stap_run $TEST_NAME run_cat_5_sec $syscall_return_script_output -e $script +} + +set TEST_NAME "UTRACE_P5_05" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME" +} else { + set script [format $clone_script [pid]] + stap_run $TEST_NAME run_cat_5_sec $clone_script_output -e $script +} + +# Cleanup +exec rm -f $exepath diff --git a/testsuite/systemtap.base/warnings.exp b/testsuite/systemtap.base/warnings.exp index 90409d18..025bde89 100644 --- a/testsuite/systemtap.base/warnings.exp +++ b/testsuite/systemtap.base/warnings.exp @@ -9,8 +9,8 @@ expect { eof { } } wait -if {$ok == 6} { +if {$ok == 9} { pass $test } else { - fail $test + fail "$test ($ok)" } 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/examples/ChangeLog b/testsuite/systemtap.examples/ChangeLog index 78083901..ad46276c 100644 --- a/examples/ChangeLog +++ b/testsuite/systemtap.examples/ChangeLog @@ -1,3 +1,10 @@ +2008-05-01 William Cohen <wcohen@redhat.com> + + * helloworld.meta: New file. + +2008-04-27 William Cohen <wcohen@redhat.com> + + * check.exp: New script to run tests on cataloged examples. 2008-03-09 Wenji Huang <wenji.huang@oracle.com> diff --git a/examples/README b/testsuite/systemtap.examples/README index 6718a55a..6718a55a 100644 --- a/examples/README +++ b/testsuite/systemtap.examples/README diff --git a/testsuite/systemtap.examples/check.exp b/testsuite/systemtap.examples/check.exp new file mode 100644 index 00000000..2ac43050 --- /dev/null +++ b/testsuite/systemtap.examples/check.exp @@ -0,0 +1,75 @@ +# check.exp +# +# This script searches the systemtap.examples directory for .meta files +# The .meta files contain information categorizing the script. The +# .meta files are composed of lines of tags. Each tag is followed by a +# value. + +#open the file and read in all the lines of data in FILE +#return a string with the data +proc get_meta_data { FILE } { + set meta_data "" + + if [catch {open "$FILE" RDONLY} fl] { + puts "open $FILE failed: $err" + return "" + } else { + set meta_data [read -nonewline $fl] + close $fl + return "$meta_data" + } +} + +#extract value for TAG from string META_DATA +#if there is no matching tag return "" +proc extract_tag { META_DATA TAG } { + set taglines "" + set value "" + set expr "^$TAG:\[^\$\]*" + regexp -line -all $expr $META_DATA taglines + set expr "$TAG:" + regsub -line $expr $taglines "" value + return $value +} + +set curdir [pwd] + +set src_examples $srcdir/systemtap.examples +set meta_files [lsort [exec find $src_examples -path "*.meta"]] +foreach file $meta_files { + set dir [file dirname $file] + set test [regsub {.*/testsuite/} $file ""] + + cd $dir + + set meta_data [get_meta_data $file] + set test_check [extract_tag "$meta_data" "test_check"] + set test_installcheck [extract_tag "$meta_data" "test_installcheck"] + # Would like to run the tests (-p5), but pass fail logic too + # simple and fails for many examples + # FIXME following line prevents installcheck with "--tools_opts install" + set test_installcheck "" + if {[info procs installtest_p] != "" && [installtest_p] + && $test_installcheck != "" } then { + set command $test_installcheck + } else { + set command $test_check + } + + #FIXME tcl says that single quotes not dealt with + if { $command != "" } then { + verbose -log "attempting command $command" + set res [catch {eval exec $command} value] + verbose -log "OUT $value" + verbose -log "RC $res" + if {$res != 0 } { + fail $test + } else { + pass $test + } + } else { + untested $test + } +} + +cd $curdir diff --git a/examples/futexes.stp b/testsuite/systemtap.examples/futexes.stp index 16c62937..16c62937 100755 --- a/examples/futexes.stp +++ b/testsuite/systemtap.examples/futexes.stp diff --git a/examples/futexes.txt b/testsuite/systemtap.examples/futexes.txt index 51de4352..51de4352 100644 --- a/examples/futexes.txt +++ b/testsuite/systemtap.examples/futexes.txt diff --git a/testsuite/systemtap.examples/helloworld.meta b/testsuite/systemtap.examples/helloworld.meta new file mode 100644 index 00000000..f56b7ca3 --- /dev/null +++ b/testsuite/systemtap.examples/helloworld.meta @@ -0,0 +1,13 @@ +title: SystemTap "Hello World" Program +name: helloworld.stp +version: 1.0 +keywords: simple +author: anonymous +subsystem: none +status: production +exit: fixed +output: text +scope: system-wide +description: A basic "Hello World" program implemented in SystemTap script. It prints out "hello world" message and then immediately exits. +test_check: stap -p4 helloworld.stp +test_installcheck: stap helloworld.stp -c "sleep 1" diff --git a/examples/helloworld.stp b/testsuite/systemtap.examples/helloworld.stp index efe45b79..efe45b79 100755 --- a/examples/helloworld.stp +++ b/testsuite/systemtap.examples/helloworld.stp diff --git a/examples/iostat-scsi.stp b/testsuite/systemtap.examples/iostat-scsi.stp index ef778e53..ef778e53 100755 --- a/examples/iostat-scsi.stp +++ b/testsuite/systemtap.examples/iostat-scsi.stp diff --git a/examples/iostat-scsi.txt b/testsuite/systemtap.examples/iostat-scsi.txt index 8222f659..8222f659 100644 --- a/examples/iostat-scsi.txt +++ b/testsuite/systemtap.examples/iostat-scsi.txt diff --git a/examples/iotime.stp b/testsuite/systemtap.examples/iotime.stp index a5b36449..a5b36449 100755 --- a/examples/iotime.stp +++ b/testsuite/systemtap.examples/iotime.stp diff --git a/examples/nettop.stp b/testsuite/systemtap.examples/nettop.stp index 96db413a..96db413a 100755 --- a/examples/nettop.stp +++ b/testsuite/systemtap.examples/nettop.stp diff --git a/examples/nettop.txt b/testsuite/systemtap.examples/nettop.txt index 2bfd4967..2bfd4967 100644 --- a/examples/nettop.txt +++ b/testsuite/systemtap.examples/nettop.txt diff --git a/examples/pf2.stp b/testsuite/systemtap.examples/pf2.stp index 96fdb7e7..96fdb7e7 100755 --- a/examples/pf2.stp +++ b/testsuite/systemtap.examples/pf2.stp diff --git a/examples/pf2.txt b/testsuite/systemtap.examples/pf2.txt index 0fafe17e..0fafe17e 100644 --- a/examples/pf2.txt +++ b/testsuite/systemtap.examples/pf2.txt diff --git a/examples/sig_by_pid.stp b/testsuite/systemtap.examples/sig_by_pid.stp index 9c1493f5..9c1493f5 100755 --- a/examples/sig_by_pid.stp +++ b/testsuite/systemtap.examples/sig_by_pid.stp diff --git a/examples/sig_by_pid.txt b/testsuite/systemtap.examples/sig_by_pid.txt index 927b4607..927b4607 100644 --- a/examples/sig_by_pid.txt +++ b/testsuite/systemtap.examples/sig_by_pid.txt diff --git a/examples/sig_by_proc.stp b/testsuite/systemtap.examples/sig_by_proc.stp index ce845aed..ce845aed 100755 --- a/examples/sig_by_proc.stp +++ b/testsuite/systemtap.examples/sig_by_proc.stp diff --git a/examples/sig_by_proc.txt b/testsuite/systemtap.examples/sig_by_proc.txt index d09da9fe..d09da9fe 100644 --- a/examples/sig_by_proc.txt +++ b/testsuite/systemtap.examples/sig_by_proc.txt diff --git a/examples/sigmon.stp b/testsuite/systemtap.examples/sigmon.stp index 31d7822e..31d7822e 100755 --- a/examples/sigmon.stp +++ b/testsuite/systemtap.examples/sigmon.stp diff --git a/examples/sleeptime.stp b/testsuite/systemtap.examples/sleeptime.stp index 252e50cc..252e50cc 100755 --- a/examples/sleeptime.stp +++ b/testsuite/systemtap.examples/sleeptime.stp diff --git a/examples/small_demos/ansi_colors.stp b/testsuite/systemtap.examples/small_demos/ansi_colors.stp index 0d9d7c47..0d9d7c47 100755 --- a/examples/small_demos/ansi_colors.stp +++ b/testsuite/systemtap.examples/small_demos/ansi_colors.stp diff --git a/examples/small_demos/click.wav b/testsuite/systemtap.examples/small_demos/click.wav Binary files differindex 8214b229..8214b229 100644 --- a/examples/small_demos/click.wav +++ b/testsuite/systemtap.examples/small_demos/click.wav diff --git a/examples/small_demos/close.stp b/testsuite/systemtap.examples/small_demos/close.stp index 7ba2a036..7ba2a036 100755 --- a/examples/small_demos/close.stp +++ b/testsuite/systemtap.examples/small_demos/close.stp diff --git a/examples/small_demos/demo_script.txt b/testsuite/systemtap.examples/small_demos/demo_script.txt index f3166a49..f3166a49 100644 --- a/examples/small_demos/demo_script.txt +++ b/testsuite/systemtap.examples/small_demos/demo_script.txt diff --git a/examples/small_demos/fileopen.stp b/testsuite/systemtap.examples/small_demos/fileopen.stp index c1298f9c..c1298f9c 100755 --- a/examples/small_demos/fileopen.stp +++ b/testsuite/systemtap.examples/small_demos/fileopen.stp diff --git a/examples/small_demos/key.stp b/testsuite/systemtap.examples/small_demos/key.stp index 6d2d6c3f..6d2d6c3f 100755 --- a/examples/small_demos/key.stp +++ b/testsuite/systemtap.examples/small_demos/key.stp diff --git a/examples/small_demos/keyhack.stp b/testsuite/systemtap.examples/small_demos/keyhack.stp index 3137baad..3137baad 100755 --- a/examples/small_demos/keyhack.stp +++ b/testsuite/systemtap.examples/small_demos/keyhack.stp diff --git a/examples/small_demos/kmalloc.stp b/testsuite/systemtap.examples/small_demos/kmalloc.stp index 9157928d..9157928d 100755 --- a/examples/small_demos/kmalloc.stp +++ b/testsuite/systemtap.examples/small_demos/kmalloc.stp diff --git a/examples/small_demos/kmalloc2.stp b/testsuite/systemtap.examples/small_demos/kmalloc2.stp index 2622dd2f..2622dd2f 100755 --- a/examples/small_demos/kmalloc2.stp +++ b/testsuite/systemtap.examples/small_demos/kmalloc2.stp diff --git a/examples/small_demos/proc_snoop.stp b/testsuite/systemtap.examples/small_demos/proc_snoop.stp index 24499b4b..24499b4b 100755 --- a/examples/small_demos/proc_snoop.stp +++ b/testsuite/systemtap.examples/small_demos/proc_snoop.stp diff --git a/examples/small_demos/prof.stp b/testsuite/systemtap.examples/small_demos/prof.stp index 389f743a..389f743a 100755 --- a/examples/small_demos/prof.stp +++ b/testsuite/systemtap.examples/small_demos/prof.stp diff --git a/examples/small_demos/return.wav b/testsuite/systemtap.examples/small_demos/return.wav Binary files differindex 20f978cc..20f978cc 100644 --- a/examples/small_demos/return.wav +++ b/testsuite/systemtap.examples/small_demos/return.wav diff --git a/examples/small_demos/rwtiming.stp b/testsuite/systemtap.examples/small_demos/rwtiming.stp index d570c581..d570c581 100755 --- a/examples/small_demos/rwtiming.stp +++ b/testsuite/systemtap.examples/small_demos/rwtiming.stp diff --git a/examples/small_demos/sched_snoop.stp b/testsuite/systemtap.examples/small_demos/sched_snoop.stp index 623643dd..623643dd 100755 --- a/examples/small_demos/sched_snoop.stp +++ b/testsuite/systemtap.examples/small_demos/sched_snoop.stp diff --git a/examples/small_demos/sys.stp b/testsuite/systemtap.examples/small_demos/sys.stp index 2df20bc3..2df20bc3 100755 --- a/examples/small_demos/sys.stp +++ b/testsuite/systemtap.examples/small_demos/sys.stp diff --git a/examples/small_demos/top.stp b/testsuite/systemtap.examples/small_demos/top.stp index b46b9940..b46b9940 100755 --- a/examples/small_demos/top.stp +++ b/testsuite/systemtap.examples/small_demos/top.stp diff --git a/examples/socket-trace.stp b/testsuite/systemtap.examples/socket-trace.stp index 13ab8e06..13ab8e06 100755 --- a/examples/socket-trace.stp +++ b/testsuite/systemtap.examples/socket-trace.stp diff --git a/examples/socktop b/testsuite/systemtap.examples/socktop index 123e37e9..123e37e9 100755 --- a/examples/socktop +++ b/testsuite/systemtap.examples/socktop diff --git a/examples/socktop.txt b/testsuite/systemtap.examples/socktop.txt index 0ebce003..0ebce003 100644 --- a/examples/socktop.txt +++ b/testsuite/systemtap.examples/socktop.txt diff --git a/examples/syscalls_by_pid.stp b/testsuite/systemtap.examples/syscalls_by_pid.stp index 47aa4955..47aa4955 100755 --- a/examples/syscalls_by_pid.stp +++ b/testsuite/systemtap.examples/syscalls_by_pid.stp diff --git a/examples/syscalls_by_pid.txt b/testsuite/systemtap.examples/syscalls_by_pid.txt index 4943a139..4943a139 100644 --- a/examples/syscalls_by_pid.txt +++ b/testsuite/systemtap.examples/syscalls_by_pid.txt diff --git a/examples/syscalls_by_proc.stp b/testsuite/systemtap.examples/syscalls_by_proc.stp index af7d6932..af7d6932 100755 --- a/examples/syscalls_by_proc.stp +++ b/testsuite/systemtap.examples/syscalls_by_proc.stp diff --git a/examples/syscalls_by_proc.txt b/testsuite/systemtap.examples/syscalls_by_proc.txt index dd554083..dd554083 100644 --- a/examples/syscalls_by_proc.txt +++ b/testsuite/systemtap.examples/syscalls_by_proc.txt diff --git a/examples/syscalltimes b/testsuite/systemtap.examples/syscalltimes index 84ca77a9..84ca77a9 100755 --- a/examples/syscalltimes +++ b/testsuite/systemtap.examples/syscalltimes diff --git a/examples/syscalltimes.txt b/testsuite/systemtap.examples/syscalltimes.txt index d50f73ad..d50f73ad 100644 --- a/examples/syscalltimes.txt +++ b/testsuite/systemtap.examples/syscalltimes.txt diff --git a/examples/wait4time.stp b/testsuite/systemtap.examples/wait4time.stp index 568239b9..568239b9 100755 --- a/examples/wait4time.stp +++ b/testsuite/systemtap.examples/wait4time.stp diff --git a/testsuite/systemtap.pass1-4/buildok.exp b/testsuite/systemtap.pass1-4/buildok.exp index 77e77181..07580550 100644 --- a/testsuite/systemtap.pass1-4/buildok.exp +++ b/testsuite/systemtap.pass1-4/buildok.exp @@ -8,9 +8,7 @@ foreach file [lsort [glob -nocomplain $srcdir/$self/*.stp]] { buildok/perfmon01.stp {setup_kfail 909 *-*-*} buildok/twentysix.stp {setup_kfail 4105 *-*-*} buildok/twentyseven.stp {setup_kfail 4166 *-*-*} - buildok/process_test.stp {setup_kfail 1155 *-*-*} buildok/sched_test.stp {setup_kfail 1155 *-*-*} - buildok/socket.stp {setup_kfail 4413 *-*-*} } if {$rc == 0} { pass $test } else { fail $test } } diff --git a/testsuite/systemtap.samples/args.exp b/testsuite/systemtap.samples/args.exp index 54647998..8bed7c9e 100644 --- a/testsuite/systemtap.samples/args.exp +++ b/testsuite/systemtap.samples/args.exp @@ -47,7 +47,7 @@ if [file exists $modpath] { return } -spawn $staprunpath -d [pid] $modpath foo=hello bar=999 +spawn $staprunpath $modpath foo=hello bar=999 set ok 0 expect { -timeout 120 @@ -62,7 +62,7 @@ if {$ok == 1} { fail "$test run 1" } -spawn $staprunpath -d [pid] $modpath foo=goodbye bar=0 +spawn $staprunpath $modpath foo=goodbye bar=0 set ok 0 expect { -timeout 120 diff --git a/testsuite/transok/eight.stp b/testsuite/transok/eight.stp index 6723b993..9198536e 100755 --- a/testsuite/transok/eight.stp +++ b/testsuite/transok/eight.stp @@ -2,10 +2,11 @@ global foo global baz +global zoo function bar() { - return foo["hello"] + return foo["hello"]+(zoo++) } probe begin diff --git a/translate.cxx b/translate.cxx index 6aef37f0..b1037fef 100644 --- a/translate.cxx +++ b/translate.cxx @@ -849,6 +849,7 @@ translator_output::line () void c_unparser::emit_common_header () { + o->newline(); o->newline() << "typedef char string_t[MAXSTRINGLEN];"; o->newline(); o->newline() << "#define STAP_SESSION_STARTING 0"; @@ -4359,83 +4360,98 @@ 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) - { - 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)); - - s.op->newline() << "\n\n#include \"stap-symbols.h\""; +void +emit_symbol_data (systemtap_session& s) +{ + ofstream kallsyms_out ((s.tmpdir + "/stap-symbols.h").c_str()); + s.op->newline() << "\n\n#include \"stap-symbols.h\""; - 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"; + if (s.verbose > 1) + { + std::set<std::string>::iterator it = s.unwindsym_modules.begin(); + clog << "unwindsym modules: "; + while (it != s.unwindsym_modules.end()) + { + clog << *(it++) << " "; + } + clog << endl; } - return rc; + static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build"; + 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 = + { + 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); + + int rc = dwfl_linux_kernel_report_offline (dwfl, + s.kernel_release.c_str(), + kernel_filter); + dwfl_report_end (dwfl, NULL, NULL); + if (rc < 0) + throw semantic_error ("dwfl 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++; + } + + kallsyms_out << "};\n"; + kallsyms_out << "unsigned _stp_num_kernel_symbols = " << dec << i << ";\n"; } @@ -4452,9 +4468,6 @@ translate_pass (systemtap_session& s) { // This is at the very top of the file. - // XXX: the runtime uses #ifdef TEST_MODE to infer systemtap usage. - s.op->line() << "#define TEST_MODE 0\n"; - s.op->newline() << "#ifndef MAXNESTING"; s.op->newline() << "#define MAXNESTING 10"; s.op->newline() << "#endif"; @@ -4521,16 +4534,12 @@ 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; })"; s.op->newline() << "#endif"; - s.op->newline() << "#if defined(CONFIG_MARKERS)"; - s.op->newline() << "#include <linux/marker.h>"; - s.op->newline() << "#endif"; - s.up->emit_common_header (); // context etc. for (unsigned i=0; i<s.embeds.size(); i++) @@ -4605,16 +4614,16 @@ translate_pass (systemtap_session& s) s.up->emit_global_param (s.globals[i]); } - s.op->newline() << "MODULE_DESCRIPTION(\"systemtap probe\");"; - s.op->newline() << "MODULE_LICENSE(\"GPL\");"; // XXX + emit_symbol_data (s); + + s.op->newline() << "MODULE_DESCRIPTION(\"systemtap-generated probe\");"; + s.op->newline() << "MODULE_LICENSE(\"GPL\");"; } catch (const semantic_error& e) { s.print_error (e); } - rc |= emit_symbol_data (s); - s.op->line() << "\n"; delete s.op; diff --git a/vim/syntax/stap.vim b/vim/syntax/stap.vim index 86c7d260..e9ba136f 100644 --- a/vim/syntax/stap.vim +++ b/vim/syntax/stap.vim @@ -1,7 +1,7 @@ " Vim syntax file -" Language: SystemTap -" Maintainer: Josh Stone <joshua.i.stone@intel.com> -" Last Change: 2005 Dec 20 +" Language: SystemTap +" Maintainer: Josh Stone <joshua.i.stone@intel.com> +" Last Change: 2008 Apr 17 " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded @@ -11,11 +11,13 @@ elseif exists("b:current_syntax") finish endif -syn keyword stapStatement contained break continue return next delete containedin=stapBlock -syn keyword stapRepeat contained while for foreach in containedin=stapBlock +setlocal iskeyword=@,48-57,_,$ + +syn keyword stapStatement contained break continue return next delete containedin=stapBlock +syn keyword stapRepeat contained while for foreach in limit containedin=stapBlock syn keyword stapConditional contained if else containedin=stapBlock syn keyword stapDeclaration global probe function -syn keyword stapType string long +syn keyword stapType string long syn region stapProbeDec start="\<probe\>"lc=5 end="{"me=s-1 contains=stapString,stapNumber syn match stapProbe contained "\<\w\+\>" containedin=stapProbeDec @@ -27,17 +29,25 @@ syn match stapFunc contained "\<\w\+\>" containedin=stapFuncDec,stapFuncCall syn match stapStat contained "@\<\w\+\ze\(\s\|\n\)*(" containedin=stapBlock " decimal number -syn match stapNumber "\<\d\+\>" containedin=stapBlock +syn match stapNumber "\<\d\+\>" containedin=stapBlock " octal number -syn match stapNumber "\<0\o\+\>" contains=stapOctalZero containedin=stapBlock +syn match stapNumber "\<0\o\+\>" contains=stapOctalZero containedin=stapBlock " Flag the first zero of an octal number as something special -syn match stapOctalZero contained "\<0" +syn match stapOctalZero contained "\<0" " flag an octal number with wrong digits -syn match stapOctalError "\<0\o*[89]\d*" containedin=stapBlock +syn match stapOctalError "\<0\o*[89]\d*" containedin=stapBlock " hex number -syn match stapNumber "0x\x\+\>" containedin=stapBlock +syn match stapNumber "\<0x\x\+\>" containedin=stapBlock +" numeric arguments +syn match stapNumber "\<\$\d\+\>" containedin=stapBlock +syn match stapNumber "\<\$#" containedin=stapBlock + +syn region stapString oneline start=+"+ skip=+\\"+ end=+"+ containedin=stapBlock +" string arguments +syn match stapString "@\d\+\>" containedin=stapBlock +syn match stapString "@#" containedin=stapBlock -syn region stapString oneline start=+"+ skip=+\\"+ end=+"+ containedin=stapBlock +syn match stapTarget contained "\w\@<!\$\h\w*\>" containedin=stapBlock syn region stapPreProc fold start="%(" end="%)" contains=stapNumber,stapString containedin=ALL syn keyword stapPreProcCond contained kernel_v kernel_vr arch containedin=stapPreProc @@ -48,15 +58,15 @@ syn region stapCBlock fold matchgroup=stapCBlockDelims start="%{"rs=e end="%}"r syn region stapBlock fold matchgroup=stapBlockEnds start="{"rs=e end="}"re=s containedin=stapBlock -syn keyword stapTodo contained TODO FIXME XXX +syn keyword stapTodo contained TODO FIXME XXX -syn match stapComment "#.*" contains=stapTodo containedin=stapBlock -syn match stapComment "//.*" contains=stapTodo containedin=stapBlock -syn region stapComment matchgroup=stapComment start="/\*" end="\*/" contains=stapTodo,stapCommentBad containedin=stapBlock -syn match stapCommentBad contained "/\*" +syn match stapComment "#.*" contains=stapTodo containedin=stapBlock +syn match stapComment "//.*" contains=stapTodo containedin=stapBlock +syn region stapComment matchgroup=stapComment start="/\*" end="\*/" contains=stapTodo,stapCommentBad containedin=stapBlock +syn match stapCommentBad contained "/\*" " treat ^#! as special -syn match stapSharpBang "^#!.*" +syn match stapSharpBang "^#!.*" " define the default highlighting @@ -70,26 +80,27 @@ if version >= 508 || !exists("did_stap_syn_inits") command -nargs=+ HiLink hi def link <args> endif - HiLink stapNumber Number - HiLink stapOctalZero PreProc " c.vim does it this way... - HiLink stapOctalError Error - HiLink stapString String - HiLink stapTodo Todo - HiLink stapComment Comment + HiLink stapNumber Number + HiLink stapOctalZero PreProc " c.vim does it this way... + HiLink stapOctalError Error + HiLink stapString String + HiLink stapTodo Todo + HiLink stapComment Comment HiLink stapCommentBad Error - HiLink stapSharpBang PreProc - HiLink stapCBlockDelims Special + HiLink stapSharpBang PreProc + HiLink stapCBlockDelims Special HiLink stapCMacro Macro - HiLink stapStatement Statement - HiLink stapConditional Conditional - HiLink stapRepeat Repeat - HiLink stapType Type - HiLink stapProbe Function - HiLink stapFunc Function - HiLink stapStat Function - HiLink stapPreProc PreProc - HiLink stapPreProcCond Special - HiLink stapDeclaration Typedef + HiLink stapStatement Statement + HiLink stapConditional Conditional + HiLink stapRepeat Repeat + HiLink stapType Type + HiLink stapProbe Function + HiLink stapFunc Function + HiLink stapStat Function + HiLink stapPreProc PreProc + HiLink stapPreProcCond Special + HiLink stapDeclaration Typedef + HiLink stapTarget Special delcommand HiLink endif |