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