diff options
56 files changed, 2832 insertions, 86 deletions
@@ -14,6 +14,7 @@ config.status .deps loc2c-test run-stap +run-staprun stamp-h1 stap staprun diff --git a/Makefile.am b/Makefile.am index f5fedae1..33563719 100644 --- a/Makefile.am +++ b/Makefile.am @@ -265,7 +265,7 @@ uninstall-local: rm -rf $(DESTDIR)$(sysconfdir)/systemtap # XXX: leaves behind man pages -SUBDIRS = doc +SUBDIRS = doc grapher DIST_SUBDIRS = testsuite $(SUBDIRS) check-local: diff --git a/Makefile.in b/Makefile.in index a953be51..9c80e321 100644 --- a/Makefile.in +++ b/Makefile.in @@ -205,6 +205,8 @@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ +GRAPHER_CFLAGS = @GRAPHER_CFLAGS@ +GRAPHER_LIBS = @GRAPHER_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -230,6 +232,7 @@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIECFLAGS = @PIECFLAGS@ PIECXXFLAGS = @PIECXXFLAGS@ PIELDFLAGS = @PIELDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ PROCFLAGS = @PROCFLAGS@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ @@ -371,7 +374,7 @@ EXAMPLE_SOURCE_DIR = $(srcdir)/testsuite/systemtap.examples EXAMPLE_META_FILES = $(EXAMPLE_SOURCE_DIR)/*/*.meta TEST_COV_DIR = coverage # XXX: leaves behind man pages -SUBDIRS = doc +SUBDIRS = doc grapher DIST_SUBDIRS = testsuite $(SUBDIRS) # Any extra flags, such as: @@ -10,6 +10,12 @@ Such accesses can originate from $context expressions fueled by erroneous debug data, or by kernel_{long,string,...}() tapset calls. +- New probes kprobe.function(FUNCTION) and kprobe.function(FUNCTION).return + for dwarfless probing. These postpone function address resolution to + run-time and use the kprobe symbol-resolution mechanism. + Probing of absolute statements can be done using the + kprobe.statement(ADDRESS).absolute construct. + * What's new in version 0.9.5 - New probes process().insn and process().insn.block that allows @@ -19,6 +19,162 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically `autoreconf'.])]) +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant <scott@netsplit.com>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see <http://pkg-config.freedesktop.org/>.])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES + # Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation diff --git a/buildrun.cxx b/buildrun.cxx index e0f22f29..71753e9f 100644 --- a/buildrun.cxx +++ b/buildrun.cxx @@ -443,4 +443,61 @@ make_tracequery(systemtap_session& s, string& name, const vector<string>& extra_ return run_make_cmd(s, make_cmd); } + +// Build a tiny kernel module to query type information +int +make_typequery_kmod(systemtap_session& s, const string& header, string& name) +{ + static unsigned tick = 0; + string basename("typequery_kmod_" + lex_cast<string>(++tick)); + + // create a subdirectory for the module + string dir(s.tmpdir + "/" + basename); + if (create_dir(dir.c_str()) != 0) + { + if (! s.suppress_warnings) + cerr << "Warning: failed to create directory for querying types." << endl; + return 1; + } + + name = dir + "/" + basename + ".ko"; + + // create a simple Makefile + string makefile(dir + "/Makefile"); + ofstream omf(makefile.c_str()); + omf << "EXTRA_CFLAGS := -g -fno-eliminate-unused-debug-types" << endl; + omf << "CFLAGS_" << basename << ".o := -include " << header << endl; + omf << "obj-m := " + basename + ".o" << endl; + omf.close(); + + // create our empty source file + string source(dir + "/" + basename + ".c"); + ofstream osrc(source.c_str()); + osrc.close(); + + // make the module + string make_cmd = "make -C '" + s.kernel_build_tree + "'" + + " M='" + dir + "' modules"; + if (s.verbose < 4) + make_cmd += " >/dev/null 2>&1"; + return run_make_cmd(s, make_cmd); +} + + +// Build a tiny user module to query type information +int +make_typequery_umod(systemtap_session& s, const string& header, string& name) +{ + static unsigned tick = 0; + + name = s.tmpdir + "/typequery_umod_" + lex_cast<string>(++tick) + ".so"; + + // make the module + string cmd = "gcc -shared -g -fno-eliminate-unused-debug-types -o " + + name + " -xc /dev/null -include " + header; + if (s.verbose < 4) + cmd += " >/dev/null 2>&1"; + return stap_system (cmd.c_str()); +} + /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ @@ -15,6 +15,8 @@ int compile_pass (systemtap_session& s); int run_pass (systemtap_session& s); int make_tracequery(systemtap_session& s, std::string& name, const std::vector<std::string>& extra_headers); +int make_typequery_kmod(systemtap_session& s, const std::string& header, std::string& name); +int make_typequery_umod(systemtap_session& s, const std::string& header, std::string& name); #endif // BUILDRUN_H @@ -253,14 +253,28 @@ clean_cache(systemtap_session& s) globfree(&cache_glob); + //grab info for each typequery user module (.so) + glob_str = s.cache_path + "/*/*.so"; + glob(glob_str.c_str(), 0, NULL, &cache_glob); + for (unsigned int i = 0; i < cache_glob.gl_pathc; i++) + { + string cache_ent_path = cache_glob.gl_pathv[i]; + struct cache_ent_info cur_info(cache_ent_path, false); + if (cur_info.size != 0 && cur_info.weight != 0) + { + cache_size_b += cur_info.size; + cache_contents.insert(cur_info); + } + } + + globfree(&cache_glob); + //grab info for each stapconf cache entry (.h) glob_str = s.cache_path + "/*/*.h"; glob(glob_str.c_str(), 0, NULL, &cache_glob); for (unsigned int i = 0; i < cache_glob.gl_pathc; i++) { string cache_ent_path = cache_glob.gl_pathv[i]; - cache_ent_path.resize(cache_ent_path.length() - 3); - struct cache_ent_info cur_info(cache_ent_path, false); if (cur_info.size != 0 && cur_info.weight != 0) { @@ -647,6 +647,11 @@ stap_LIBS elfutils_abs_srcdir BUILD_ELFUTILS_FALSE BUILD_ELFUTILS_TRUE +BUILD_GRAPHER_FALSE +BUILD_GRAPHER_TRUE +GRAPHER_LIBS +GRAPHER_CFLAGS +PKG_CONFIG BUILD_SERVER_FALSE BUILD_SERVER_TRUE nspr_CFLAGS @@ -773,6 +778,7 @@ enable_crash enable_docs enable_refdocs enable_server +enable_grapher with_elfutils ' ac_precious_vars='build_alias @@ -787,6 +793,9 @@ CXX CXXFLAGS CCC CPP +PKG_CONFIG +GRAPHER_CFLAGS +GRAPHER_LIBS CXXCPP' ac_subdirs_all='testsuite' @@ -1438,6 +1447,8 @@ Optional Features: if xmlto etc. found and other documentation built). --enable-server enable building of stap-server/client (default on if nss etc. found). + --enable-grapher enable building of grapher (default on if gtkmm is + found). Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1456,6 +1467,11 @@ Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags CPP C preprocessor + PKG_CONFIG path to pkg-config utility + GRAPHER_CFLAGS + C compiler flags for GRAPHER, overriding pkg-config + GRAPHER_LIBS + linker flags for GRAPHER, overriding pkg-config CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help @@ -7104,6 +7120,205 @@ else fi +# Check whether --enable-grapher was given. +if test "${enable_grapher+set}" = set; then + enableval=$enable_grapher; enable_grapher=$enableval +else + enable_grapher="check" +fi + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_PKG_CONFIG+set}" = set; then + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi + +fi + +pkg_failed=no +{ $as_echo "$as_me:$LINENO: checking for GRAPHER" >&5 +$as_echo_n "checking for GRAPHER... " >&6; } + +if test -n "$GRAPHER_CFLAGS"; then + pkg_cv_GRAPHER_CFLAGS="$GRAPHER_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gtkmm-2.4 >= 2.8.0\"") >&5 + ($PKG_CONFIG --exists --print-errors "gtkmm-2.4 >= 2.8.0") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + pkg_cv_GRAPHER_CFLAGS=`$PKG_CONFIG --cflags "gtkmm-2.4 >= 2.8.0" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$GRAPHER_LIBS"; then + pkg_cv_GRAPHER_LIBS="$GRAPHER_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gtkmm-2.4 >= 2.8.0\"") >&5 + ($PKG_CONFIG --exists --print-errors "gtkmm-2.4 >= 2.8.0") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + pkg_cv_GRAPHER_LIBS=`$PKG_CONFIG --libs "gtkmm-2.4 >= 2.8.0" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + GRAPHER_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "gtkmm-2.4 >= 2.8.0" 2>&1` + else + GRAPHER_PKG_ERRORS=`$PKG_CONFIG --print-errors "gtkmm-2.4 >= 2.8.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$GRAPHER_PKG_ERRORS" >&5 + + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + have_gtkmm=no +elif test $pkg_failed = untried; then + have_gtkmm=no +else + GRAPHER_CFLAGS=$pkg_cv_GRAPHER_CFLAGS + GRAPHER_LIBS=$pkg_cv_GRAPHER_LIBS + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + have_gtkmm=yes +fi + if test "x${have_gtkmm}" == "xyes" -a x"$enable_grapher" != "xno"; then + BUILD_GRAPHER_TRUE= + BUILD_GRAPHER_FALSE='#' +else + BUILD_GRAPHER_TRUE='#' + BUILD_GRAPHER_FALSE= +fi + + build_elfutils=no # Check whether --with-elfutils was given. @@ -8074,7 +8289,7 @@ _ACEOF ac_config_headers="$ac_config_headers config.h:config.in" -ac_config_files="$ac_config_files Makefile doc/Makefile doc/SystemTap_Tapset_Reference/Makefile stap.1 stapprobes.3stap stapfuncs.3stap stapvars.3stap stapex.3stap staprun.8 stap-server.8 man/stapprobes.iosched.3stap man/stapprobes.netdev.3stap man/stapprobes.nfs.3stap man/stapprobes.nfsd.3stap man/stapprobes.pagefault.3stap man/stapprobes.kprocess.3stap man/stapprobes.rpc.3stap man/stapprobes.scsi.3stap man/stapprobes.signal.3stap man/stapprobes.socket.3stap man/stapprobes.tcp.3stap man/stapprobes.udp.3stap initscript/systemtap" +ac_config_files="$ac_config_files Makefile doc/Makefile doc/SystemTap_Tapset_Reference/Makefile grapher/Makefile stap.1 stapprobes.3stap stapfuncs.3stap stapvars.3stap stapex.3stap staprun.8 stap-server.8 man/stapprobes.iosched.3stap man/stapprobes.netdev.3stap man/stapprobes.nfs.3stap man/stapprobes.nfsd.3stap man/stapprobes.pagefault.3stap man/stapprobes.kprocess.3stap man/stapprobes.rpc.3stap man/stapprobes.scsi.3stap man/stapprobes.signal.3stap man/stapprobes.socket.3stap man/stapprobes.tcp.3stap man/stapprobes.udp.3stap initscript/systemtap" @@ -8244,6 +8459,13 @@ $as_echo "$as_me: error: conditional \"BUILD_SERVER\" was never defined. Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${BUILD_GRAPHER_TRUE}" && test -z "${BUILD_GRAPHER_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"BUILD_GRAPHER\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"BUILD_GRAPHER\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi if test -z "${BUILD_ELFUTILS_TRUE}" && test -z "${BUILD_ELFUTILS_FALSE}"; then { { $as_echo "$as_me:$LINENO: error: conditional \"BUILD_ELFUTILS\" was never defined. Usually this means the macro was only invoked conditionally." >&5 @@ -8764,6 +8986,7 @@ do "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "doc/SystemTap_Tapset_Reference/Makefile") CONFIG_FILES="$CONFIG_FILES doc/SystemTap_Tapset_Reference/Makefile" ;; + "grapher/Makefile") CONFIG_FILES="$CONFIG_FILES grapher/Makefile" ;; "stap.1") CONFIG_FILES="$CONFIG_FILES stap.1" ;; "stapprobes.3stap") CONFIG_FILES="$CONFIG_FILES stapprobes.3stap" ;; "stapfuncs.3stap") CONFIG_FILES="$CONFIG_FILES stapfuncs.3stap" ;; diff --git a/configure.ac b/configure.ac index a953e156..125626f8 100644 --- a/configure.ac +++ b/configure.ac @@ -253,6 +253,15 @@ if test "x${have_nss_includes}${have_nss_libs}" != "xyesyes"; then fi AM_CONDITIONAL([BUILD_SERVER], [test "x${have_nss_includes}${have_nss_libs}" == "xyesyes" -a "$enable_server" != "no"]) +dnl Handle the optional grapher +AC_ARG_ENABLE([grapher], + AS_HELP_STRING([--enable-grapher], + [enable building of grapher (default on if gtkmm is found).]), + [enable_grapher=$enableval], + [enable_grapher="check"]) +PKG_CHECK_MODULES([GRAPHER], [gtkmm-2.4 >= 2.8.0],have_gtkmm=yes,have_gtkmm=no) +AM_CONDITIONAL([BUILD_GRAPHER], [test "x${have_gtkmm}" == "xyes" -a x"$enable_grapher" != "xno"]) + dnl Handle elfutils. If '--with-elfutils=DIR' wasn't specified, used dnl the system's elfutils. build_elfutils=no @@ -356,7 +365,7 @@ dnl Don't use this directly (when not given it is set to NONE). AC_DEFINE_UNQUOTED(STAP_PREFIX, "$prefix", [configure prefix location]) AC_CONFIG_HEADERS([config.h:config.in]) -AC_CONFIG_FILES(Makefile doc/Makefile doc/SystemTap_Tapset_Reference/Makefile stap.1 stapprobes.3stap stapfuncs.3stap stapvars.3stap stapex.3stap staprun.8 stap-server.8 man/stapprobes.iosched.3stap man/stapprobes.netdev.3stap man/stapprobes.nfs.3stap man/stapprobes.nfsd.3stap man/stapprobes.pagefault.3stap man/stapprobes.kprocess.3stap man/stapprobes.rpc.3stap man/stapprobes.scsi.3stap man/stapprobes.signal.3stap man/stapprobes.socket.3stap man/stapprobes.tcp.3stap man/stapprobes.udp.3stap initscript/systemtap) +AC_CONFIG_FILES(Makefile doc/Makefile doc/SystemTap_Tapset_Reference/Makefile grapher/Makefile stap.1 stapprobes.3stap stapfuncs.3stap stapvars.3stap stapex.3stap staprun.8 stap-server.8 man/stapprobes.iosched.3stap man/stapprobes.netdev.3stap man/stapprobes.nfs.3stap man/stapprobes.nfsd.3stap man/stapprobes.pagefault.3stap man/stapprobes.kprocess.3stap man/stapprobes.rpc.3stap man/stapprobes.scsi.3stap man/stapprobes.signal.3stap man/stapprobes.socket.3stap man/stapprobes.tcp.3stap man/stapprobes.udp.3stap initscript/systemtap) AC_CONFIG_SUBDIRS(testsuite) AC_CONFIG_FILES([run-stap], [chmod +x run-stap]) AC_CONFIG_FILES([run-staprun], [chmod +x run-staprun]) diff --git a/doc/Makefile.in b/doc/Makefile.in index e23a6699..2818ae52 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -79,6 +79,8 @@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ +GRAPHER_CFLAGS = @GRAPHER_CFLAGS@ +GRAPHER_LIBS = @GRAPHER_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -104,6 +106,7 @@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIECFLAGS = @PIECFLAGS@ PIECXXFLAGS = @PIECXXFLAGS@ PIELDFLAGS = @PIELDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ PROCFLAGS = @PROCFLAGS@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ diff --git a/doc/SystemTap_Tapset_Reference/Makefile.in b/doc/SystemTap_Tapset_Reference/Makefile.in index 6fe6bab2..2ea897c6 100644 --- a/doc/SystemTap_Tapset_Reference/Makefile.in +++ b/doc/SystemTap_Tapset_Reference/Makefile.in @@ -82,6 +82,8 @@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ +GRAPHER_CFLAGS = @GRAPHER_CFLAGS@ +GRAPHER_LIBS = @GRAPHER_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -107,6 +109,7 @@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIECFLAGS = @PIECFLAGS@ PIECXXFLAGS = @PIECXXFLAGS@ PIELDFLAGS = @PIELDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ PROCFLAGS = @PROCFLAGS@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ diff --git a/elaborate.cxx b/elaborate.cxx index b760173f..47b77c9b 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -1441,6 +1441,7 @@ systemtap_session::systemtap_session (): user_file (0), be_derived_probes(0), dwarf_derived_probes(0), + kprobe_derived_probes(0), uprobe_derived_probes(0), utrace_derived_probes(0), itrace_derived_probes(0), diff --git a/grapher/CairoWidget.cxx b/grapher/CairoWidget.cxx new file mode 100644 index 00000000..86498a4f --- /dev/null +++ b/grapher/CairoWidget.cxx @@ -0,0 +1,42 @@ +#include "CairoWidget.hxx" + +#include <math.h> + +namespace systemtap +{ + void CairoPlayButton::draw(Cairo::RefPtr<Cairo::Context> cr) + { + if (!_visible) + return; + cr->save(); + cr->set_line_width(1.0); + // square with rounded corners + cr->move_to(_x0, _y0 + _radius); + cr->arc(_x0 + _radius, _y0 + _radius, _radius, M_PI, -M_PI_2); + cr->line_to(_x0 + _size - _radius, _y0); + cr->arc(_x0 + _size - _radius, _y0 + _radius, _radius, -M_PI_2, 0.0); + cr->line_to(_x0 + _size, _y0 + _size - _radius); + cr->arc(_x0 + _size - _radius, _y0 + _size - _radius, _radius, 0.0, M_PI_2); + cr->line_to(_x0 + _radius, _y0 + _size); + cr->arc(_x0 + _radius, _y0 + _size - _radius, _radius, M_PI_2, M_PI); + cr->close_path(); + //cr->rectangle(_x0, _y0, 50.0, 50.0); + cr->set_source_rgba(1.0, 1.0, 1.0, .8); + cr->stroke(); + // play equalateral triangle + cr->move_to(_x0 + .25 * _size, _y0 + (.5 - 1.0 / (sqrt(3.0) * 2.0)) * _size); + cr->line_to(_x0 + .75 * _size, _y0 + .5 * _size); + cr->line_to(_x0 + .25 * _size, _y0 + (.5 + 1.0 / (sqrt(3.0) * 2.0)) * _size); + cr->close_path(); + cr->fill(); + cr->restore(); + } + + bool CairoPlayButton::containsPoint(double x, double y) + { + if (x >= _x0 && (x < (_x0 + 50.0)) && (y >= _y0) && (y < (_y0 + 50))) + return true; + else + return false; + } +} diff --git a/grapher/CairoWidget.hxx b/grapher/CairoWidget.hxx new file mode 100644 index 00000000..077a4c7a --- /dev/null +++ b/grapher/CairoWidget.hxx @@ -0,0 +1,42 @@ +#ifndef SYSTEMTAP_CAIROWIDGET_H +#define SYSTEMTAP_CAIROWIDGET_H 1 + +#include <cairomm/context.h> +namespace systemtap +{ + class CairoWidget + { + public: + CairoWidget(bool visible = false) + : _visible(visible), _size(50.0), _radius(5) + {} + bool isVisible() const { return _visible; } + void setVisible(bool visible) { _visible = visible; } + void getOrigin(double &x, double &y) const + { + x = _x0; + y = _y0; + } + void setOrigin(double x, double y) + { + _x0 = x; + _y0 = y; + } + virtual void draw(Cairo::RefPtr<Cairo::Context> cr) = 0; + virtual bool containsPoint(double x, double y) { return false; } + protected: + bool _visible; + double _x0; + double _y0; + double _size; + double _radius; + }; + + class CairoPlayButton : public CairoWidget + { + public: + virtual void draw(Cairo::RefPtr<Cairo::Context> cr); + virtual bool containsPoint(double x, double y); + }; +} +#endif diff --git a/grapher/GraphData.hxx b/grapher/GraphData.hxx new file mode 100644 index 00000000..0f3b0b31 --- /dev/null +++ b/grapher/GraphData.hxx @@ -0,0 +1,44 @@ +#ifndef SYSTEMTAP_GRAPHDATA_HXX +#define SYSTEMTAP_GRAPHDATA_HXX 1 + +#include <utility> +#include <vector> + +namespace systemtap +{ + struct GraphData + { + public: + enum Style + { BAR, + DOT + }; + GraphData() : scale(1.0), style(BAR) + { + color[0] = 0.0; color[1] = 1.0; color[2] = 0.0; + } + typedef std::pair<double, double> Datum; + typedef std::vector<Datum> List; + // size of grid square at "normal" viewing + double scale; + double color[3]; + Style style; + List data; + struct Compare + { + bool operator() (const Datum& lhs, const Datum& rhs) const + { + return lhs.first < rhs.first; + } + bool operator() (double lhs, const Datum& rhs) const + { + return lhs < rhs.first; + } + bool operator() (const Datum& lhs, double rhs) const + { + return lhs.first < rhs; + } + }; + }; +} +#endif diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx new file mode 100644 index 00000000..a82a8bb6 --- /dev/null +++ b/grapher/GraphWidget.cxx @@ -0,0 +1,323 @@ +#include <algorithm> +#include <ctime> +#include <math.h> +#include <sstream> +#include <iomanip> +#include <cairomm/context.h> +#include "GraphWidget.hxx" +#include "CairoWidget.hxx" + +namespace systemtap +{ + GraphWidget::GraphWidget() + : _left(0.0), _right(1.0), _top(1.0), _bottom(0.0), _lineWidth(10), + _autoScaling(true), _autoScrolling(true), _zoomFactor(1.0), + _trackingDrag(false), _playButton(new CairoPlayButton) + { + add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK + | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK); + Glib::signal_timeout() + .connect(sigc::mem_fun(*this, &GraphWidget::on_timeout), 1000); + signal_expose_event() + .connect(sigc::mem_fun(*this, &GraphWidget::on_expose_event), false); + signal_button_press_event() + .connect(sigc::mem_fun(*this, &GraphWidget::on_button_press_event), + false); + signal_button_release_event() + .connect(sigc::mem_fun(*this, &GraphWidget::on_button_release_event), + false); + signal_motion_notify_event() + .connect(sigc::mem_fun(*this, &GraphWidget::on_motion_notify_event), + false); + signal_scroll_event() + .connect(sigc::mem_fun(*this, &GraphWidget::on_scroll_event), false); + } + + void GraphWidget::getExtents(double& left, double& right, double& top, + double& bottom) const + { + left = _left; + right = _right; + top = _top; + bottom = _bottom; + } + + void GraphWidget::setExtents(double left, double right, double top, + double bottom) + { + _left = left; + _right = right; + _top = top; + _bottom = bottom; + + } + GraphWidget::~GraphWidget() + { + } + + void GraphWidget::addGraphData(std::tr1::shared_ptr<GraphData> data) + { + _datasets.push_back(data); + } + + bool GraphWidget::on_expose_event(GdkEventExpose* event) + { + // This is where we draw on the window + Glib::RefPtr<Gdk::Window> window = get_window(); + if(!window) + return true; + + Gtk::Allocation allocation = get_allocation(); + + const int graphWidth = allocation.get_width(); + const int graphHeight = allocation.get_height(); + const int width = graphWidth - 20; + const int height = graphHeight - 20; + + Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context(); + if(event && !_autoScaling) + { + // clip to the area indicated by the expose event so that we only + // redraw the portion of the window that needs to be redrawn + cr->rectangle(event->area.x, event->area.y, + event->area.width, event->area.height); + cr->clip(); + } + if (_autoScaling) + { + // line separation + int linesPossible = width / ((int)_lineWidth + 2); + // Find latest time. + double latestTime = 0; + for (DatasetList::iterator ditr = _datasets.begin(), + de = _datasets.end(); + ditr != de; + ++ditr) + { + if (!(*ditr)->data.empty()) + { + double lastDataTime = (*ditr)->data.back().first; + if (lastDataTime > latestTime) + latestTime = lastDataTime; + } + } + double minDiff = 0.0; + double maxTotal = 0.0; + for (DatasetList::iterator ditr = _datasets.begin(), + de = _datasets.end(); + ditr != de; + ++ditr) + { + GraphData::List& gdata = (*ditr)->data; + if (gdata.size() <= 1) + continue; + double totalDiff = 0.0; + for (GraphData::List::reverse_iterator ritr = gdata.rbegin(), + re = gdata.rend(); + ritr + 1 != gdata.rend(); + ritr++) + { + double timeDiff = ritr->first - (ritr + 1)->first; + if (timeDiff < minDiff || (timeDiff != 0 && minDiff == 0)) + minDiff = timeDiff; + if (minDiff != 0 + && (totalDiff + timeDiff) / minDiff > linesPossible) + break; + totalDiff += timeDiff; + } + if (totalDiff > maxTotal) + maxTotal = totalDiff; + } + // Now we have a global scale. + _right = latestTime; + if (maxTotal != 0) + _left = latestTime - maxTotal; + else + _left = _right - 1.0; + } + cr->save(); + double horizScale = _zoomFactor * width / ( _right - _left); + cr->translate(20.0, 0.0); + cr->set_line_width(_lineWidth); + cr->save(); + cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); + cr->paint(); + cr->restore(); + + for (DatasetList::iterator itr = _datasets.begin(), e = _datasets.end(); + itr != e; + ++itr) + { + cr->save(); + cr->translate(0.0, height); + cr->scale(1.0, -1.0); + GraphData::List::iterator lower + = std::lower_bound((*itr)->data.begin(), (*itr)->data.end(), _left, + GraphData::Compare()); + GraphData::List::iterator upper + = std::upper_bound((*itr)->data.begin(), (*itr)->data.end(), _right, + GraphData::Compare()); + for (GraphData::List::iterator ditr = lower, de = upper; + ditr != de; + ++ditr) + { + cr->set_source_rgba((*itr)->color[0], (*itr)->color[1], + (*itr)->color[2], 1.0); + if ((*itr)->style == GraphData::BAR) + { + cr->move_to((ditr->first - _left) * horizScale, 0); + cr->line_to((ditr->first - _left) * horizScale, + ditr->second * height / (*itr)->scale); + cr->stroke(); + } + else + { + cr->arc((ditr->first - _left) * horizScale, + ditr->second * height / (*itr)->scale, + _lineWidth / 2.0, 0.0, M_PI * 2.0); + cr->fill(); + } + } + cr->restore(); + } + cr->restore(); + cr->save(); + cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, + Cairo::FONT_WEIGHT_BOLD); + cr->set_font_size(14.0); + cr->set_source_rgba(1.0, 1.0, 1.0, .8); + + if (!_title.empty()) + { + cr->move_to(20.0, 20.0); + cr->show_text(_title); + } + if (!_xAxisText.empty()) + { + cr->move_to(10.0, graphHeight - 5); + cr->show_text(_xAxisText); + } + if (!_yAxisText.empty()) + { + cr->save(); + cr->translate(10.0, height - 10.0); + cr->rotate(-M_PI / 2.0); + cr->move_to(10.0, 0.0); + cr->show_text(_yAxisText); + cr->restore(); + } + cr->restore(); + // Draw axes + double diff = _right - _left; + double majorUnit = pow(10.0, floor(log(diff) / log(10.0))); + double startTime = floor(_left / majorUnit) * majorUnit; + cr->save(); + cr->set_source_rgba(1.0, 1.0, 1.0, .9); + cr->set_line_cap(Cairo::LINE_CAP_BUTT); + cr->set_line_width(_lineWidth); + cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, + Cairo::FONT_WEIGHT_NORMAL); + cr->set_font_size(10.0); + cr->move_to(20.0, 0.0); + cr->line_to(20.0, height); + cr->move_to(20.0, height); + cr->line_to(graphWidth, height); + cr->stroke(); + std::valarray<double> dash(1); + dash[0] = height / 10; + cr->set_dash(dash, 0.0); + for (double tickVal = startTime; tickVal < _right; tickVal += majorUnit) + { + cr->move_to((tickVal - _left) * horizScale + 20.0, graphHeight - 5); + std::ostringstream stream; + stream << std::fixed << std::setprecision(0) << tickVal; + cr->show_text(stream.str()); + cr->move_to((tickVal - _left) * horizScale + 20.0, 0.0); + cr->line_to((tickVal - _left) * horizScale + 20.0, height); + cr->stroke(); + } + cr->stroke(); + cr->restore(); + + if (!_autoScrolling) + { + _playButton->setVisible(true); + _playButton->setOrigin(width / 2 - 25, .875 * height - 50); + _playButton->draw(cr); + } + + return true; + } + + bool GraphWidget::on_button_press_event(GdkEventButton* event) + { + if (!_autoScrolling && _playButton->containsPoint(event->x, event->y)) + { + _autoScaling = true; + _autoScrolling = true; + queue_draw(); + } + else + { + _trackingDrag = true; + _autoScaling = false; + _autoScrolling = false; + _dragOriginX = event->x; + _dragOriginY = event->y; + _dragOrigLeft = _left; + _dragOrigRight = _right; + } + return true; + } + + bool GraphWidget::on_button_release_event(GdkEventButton* event) + { + _trackingDrag = false; + return true; + } + + bool GraphWidget::on_motion_notify_event(GdkEventMotion* event) + { + Glib::RefPtr<Gdk::Window> win = get_window(); + if(!win) + return true; + double x; + double y; + // XXX Hint + if (event->is_hint) + { + } + else + { + x = event->x; + y = event->y; + } + if (_trackingDrag) + { + Gtk::Allocation allocation = get_allocation(); + const int width = allocation.get_width(); + double motion = (x - _dragOriginX) / (double) width; + double increment = motion * (_dragOrigLeft - _dragOrigRight); + _left = _dragOrigLeft + increment; + _right = _dragOrigRight + increment; + queue_draw(); + } + return true; + } + + bool GraphWidget::on_scroll_event(GdkEventScroll* event) + { + if (event->direction == GDK_SCROLL_UP) + _zoomFactor += .1; + else if (event->direction == GDK_SCROLL_DOWN) + _zoomFactor -= .1; + queue_draw(); + return true; + } + + bool GraphWidget::on_timeout() + { + queue_draw(); + return true; + } +} diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx new file mode 100644 index 00000000..46075b78 --- /dev/null +++ b/grapher/GraphWidget.hxx @@ -0,0 +1,62 @@ +#ifndef SYSTEMTAP_GRAPHWIDGET_H +#define SYSTEMTAP_GRAPHWIDGET_H + +#include <string> +#include <vector> +#include <tr1/memory> + +#include <gtkmm/drawingarea.h> +#include "GraphData.hxx" + +namespace systemtap +{ + class CairoPlayButton; + + class GraphWidget : public Gtk::DrawingArea + { + public: + GraphWidget(); + virtual ~GraphWidget(); + void addGraphData(std::tr1::shared_ptr<GraphData> data); + void getExtents(double& left, double& right, double& top, double& bottom) const; + void setExtents(double left, double right, double top, double bottom); + double getLineWidth() { return _lineWidth; } + void setLineWidth(double lineWidth) { _lineWidth = lineWidth; } + bool getAutoScaling() const { return _autoScaling; } + void setAutoScaling(bool val) { _autoScaling = val; } + std::string getTitle() const { return _title; } + void setTitle(const std::string& title) { _title = title; } + std::string getXAxisText() const { return _xAxisText; } + void setXAxisText(const std::string& text) { _xAxisText = text; } + std::string getYAxisText() const { return _yAxisText; } + void setYAxisText(const std::string& text) { _yAxisText = text; } + protected: + //Override default signal handler: + virtual bool on_expose_event(GdkEventExpose* event); + virtual bool on_motion_notify_event(GdkEventMotion* event); + virtual bool on_button_press_event(GdkEventButton* event); + virtual bool on_button_release_event(GdkEventButton* event); + virtual bool on_scroll_event(GdkEventScroll* event); + bool on_timeout(); + typedef std::vector<std::tr1::shared_ptr<GraphData> > DatasetList; + DatasetList _datasets; + double _left; + double _right; + double _top; + double _bottom; + double _lineWidth; + bool _autoScaling; + bool _autoScrolling; + double _zoomFactor; + bool _trackingDrag; + double _dragOriginX; + double _dragOriginY; + double _dragOrigLeft; + double _dragOrigRight; + std::string _title; + std::string _xAxisText; + std::string _yAxisText; + std::tr1::shared_ptr<CairoPlayButton> _playButton; + }; +} +#endif // SYSTEMTAP_GRAPHWIDGET_H diff --git a/grapher/Makefile.am b/grapher/Makefile.am new file mode 100644 index 00000000..fdb52ef7 --- /dev/null +++ b/grapher/Makefile.am @@ -0,0 +1,7 @@ +if BUILD_GRAPHER +bin_PROGRAMS = grapher + +grapher_CXXFLAGS = $(GRAPHER_CFLAGS) +grapher_SOURCES = grapher.cxx GraphWidget.cxx CairoWidget.cxx +grapher_LDADD = $(GRAPHER_LIBS) +endif
\ No newline at end of file diff --git a/grapher/Makefile.in b/grapher/Makefile.in new file mode 100644 index 00000000..020d9c01 --- /dev/null +++ b/grapher/Makefile.in @@ -0,0 +1,489 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +@BUILD_GRAPHER_TRUE@bin_PROGRAMS = grapher$(EXEEXT) +subdir = grapher +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am__grapher_SOURCES_DIST = grapher.cxx GraphWidget.cxx CairoWidget.cxx +@BUILD_GRAPHER_TRUE@am_grapher_OBJECTS = grapher-grapher.$(OBJEXT) \ +@BUILD_GRAPHER_TRUE@ grapher-GraphWidget.$(OBJEXT) \ +@BUILD_GRAPHER_TRUE@ grapher-CairoWidget.$(OBJEXT) +grapher_OBJECTS = $(am_grapher_OBJECTS) +am__DEPENDENCIES_1 = +@BUILD_GRAPHER_TRUE@grapher_DEPENDENCIES = $(am__DEPENDENCIES_1) +grapher_LINK = $(CXXLD) $(grapher_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(grapher_SOURCES) +DIST_SOURCES = $(am__grapher_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATE = @DATE@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GRAPHER_CFLAGS = @GRAPHER_CFLAGS@ +GRAPHER_LIBS = @GRAPHER_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PIECFLAGS = @PIECFLAGS@ +PIECXXFLAGS = @PIECXXFLAGS@ +PIELDFLAGS = @PIELDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +PROCFLAGS = @PROCFLAGS@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +U = @U@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +elfutils_abs_srcdir = @elfutils_abs_srcdir@ +exec_prefix = @exec_prefix@ +have_dvips = @have_dvips@ +have_latex = @have_latex@ +have_latex2html = @have_latex2html@ +have_ps2pdf = @have_ps2pdf@ +have_xmlto = @have_xmlto@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +nspr_CFLAGS = @nspr_CFLAGS@ +nss_CFLAGS = @nss_CFLAGS@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sqlite3_LIBS = @sqlite3_LIBS@ +srcdir = @srcdir@ +stap_LIBS = @stap_LIBS@ +staplog_CPPFLAGS = @staplog_CPPFLAGS@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@BUILD_GRAPHER_TRUE@grapher_CXXFLAGS = $(GRAPHER_CFLAGS) +@BUILD_GRAPHER_TRUE@grapher_SOURCES = grapher.cxx GraphWidget.cxx CairoWidget.cxx +@BUILD_GRAPHER_TRUE@grapher_LDADD = $(GRAPHER_LIBS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .cxx .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu grapher/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu grapher/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +grapher$(EXEEXT): $(grapher_OBJECTS) $(grapher_DEPENDENCIES) + @rm -f grapher$(EXEEXT) + $(grapher_LINK) $(grapher_OBJECTS) $(grapher_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grapher-CairoWidget.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grapher-GraphWidget.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grapher-grapher.Po@am__quote@ + +.cxx.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cxx.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +grapher-grapher.o: grapher.cxx +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -MT grapher-grapher.o -MD -MP -MF $(DEPDIR)/grapher-grapher.Tpo -c -o grapher-grapher.o `test -f 'grapher.cxx' || echo '$(srcdir)/'`grapher.cxx +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/grapher-grapher.Tpo $(DEPDIR)/grapher-grapher.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='grapher.cxx' object='grapher-grapher.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -c -o grapher-grapher.o `test -f 'grapher.cxx' || echo '$(srcdir)/'`grapher.cxx + +grapher-grapher.obj: grapher.cxx +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -MT grapher-grapher.obj -MD -MP -MF $(DEPDIR)/grapher-grapher.Tpo -c -o grapher-grapher.obj `if test -f 'grapher.cxx'; then $(CYGPATH_W) 'grapher.cxx'; else $(CYGPATH_W) '$(srcdir)/grapher.cxx'; fi` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/grapher-grapher.Tpo $(DEPDIR)/grapher-grapher.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='grapher.cxx' object='grapher-grapher.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -c -o grapher-grapher.obj `if test -f 'grapher.cxx'; then $(CYGPATH_W) 'grapher.cxx'; else $(CYGPATH_W) '$(srcdir)/grapher.cxx'; fi` + +grapher-GraphWidget.o: GraphWidget.cxx +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -MT grapher-GraphWidget.o -MD -MP -MF $(DEPDIR)/grapher-GraphWidget.Tpo -c -o grapher-GraphWidget.o `test -f 'GraphWidget.cxx' || echo '$(srcdir)/'`GraphWidget.cxx +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/grapher-GraphWidget.Tpo $(DEPDIR)/grapher-GraphWidget.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='GraphWidget.cxx' object='grapher-GraphWidget.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -c -o grapher-GraphWidget.o `test -f 'GraphWidget.cxx' || echo '$(srcdir)/'`GraphWidget.cxx + +grapher-GraphWidget.obj: GraphWidget.cxx +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -MT grapher-GraphWidget.obj -MD -MP -MF $(DEPDIR)/grapher-GraphWidget.Tpo -c -o grapher-GraphWidget.obj `if test -f 'GraphWidget.cxx'; then $(CYGPATH_W) 'GraphWidget.cxx'; else $(CYGPATH_W) '$(srcdir)/GraphWidget.cxx'; fi` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/grapher-GraphWidget.Tpo $(DEPDIR)/grapher-GraphWidget.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='GraphWidget.cxx' object='grapher-GraphWidget.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -c -o grapher-GraphWidget.obj `if test -f 'GraphWidget.cxx'; then $(CYGPATH_W) 'GraphWidget.cxx'; else $(CYGPATH_W) '$(srcdir)/GraphWidget.cxx'; fi` + +grapher-CairoWidget.o: CairoWidget.cxx +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -MT grapher-CairoWidget.o -MD -MP -MF $(DEPDIR)/grapher-CairoWidget.Tpo -c -o grapher-CairoWidget.o `test -f 'CairoWidget.cxx' || echo '$(srcdir)/'`CairoWidget.cxx +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/grapher-CairoWidget.Tpo $(DEPDIR)/grapher-CairoWidget.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='CairoWidget.cxx' object='grapher-CairoWidget.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -c -o grapher-CairoWidget.o `test -f 'CairoWidget.cxx' || echo '$(srcdir)/'`CairoWidget.cxx + +grapher-CairoWidget.obj: CairoWidget.cxx +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -MT grapher-CairoWidget.obj -MD -MP -MF $(DEPDIR)/grapher-CairoWidget.Tpo -c -o grapher-CairoWidget.obj `if test -f 'CairoWidget.cxx'; then $(CYGPATH_W) 'CairoWidget.cxx'; else $(CYGPATH_W) '$(srcdir)/CairoWidget.cxx'; fi` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/grapher-CairoWidget.Tpo $(DEPDIR)/grapher-CairoWidget.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='CairoWidget.cxx' object='grapher-CairoWidget.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(grapher_CXXFLAGS) $(CXXFLAGS) -c -o grapher-CairoWidget.obj `if test -f 'CairoWidget.cxx'; then $(CYGPATH_W) 'CairoWidget.cxx'; else $(CYGPATH_W) '$(srcdir)/CairoWidget.cxx'; fi` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx new file mode 100644 index 00000000..46182178 --- /dev/null +++ b/grapher/grapher.cxx @@ -0,0 +1,127 @@ +#include "GraphWidget.hxx" + +#include <cmath> +#include <sstream> +#include <string> +#include <map> + +#include <gtkmm/main.h> +#include <gtkmm/window.h> +#include <unistd.h> +#include <poll.h> + +using namespace systemtap; + +class StapParser +{ + Glib::ustring _buffer; + typedef std::map<std::string, std::tr1::shared_ptr<GraphData> > DataMap; + DataMap _dataSets; + Gtk::Window& _win; + GraphWidget& _widget; +public: + StapParser(Gtk::Window& win, + GraphWidget& widget) : _win(win), _widget(widget) {} + + bool ioCallback(Glib::IOCondition ioCondition) + { + if ((ioCondition & Glib::IO_IN) == 0) + return true; + char buf[256]; + ssize_t bytes_read = 0; + bytes_read = read(0, buf, sizeof(buf) - 1); + if (bytes_read <= 0) + { + _win.hide(); + return true; + } + buf[bytes_read] = '\0'; + _buffer += buf; + Glib::ustring::size_type ret = Glib::ustring::npos; + while ((ret = _buffer.find('\n')) != Glib::ustring::npos) + { + Glib::ustring dataString(_buffer, 0, ret); + if (dataString[0] == '%') + { + size_t found; + if ((found = dataString.find("%Title:") == 0)) + { + std::string title = dataString.substr(7); + _widget.setTitle(title); + } + else if ((found = dataString.find("%XAxisTitle:") == 0)) + { + _widget.setXAxisText(dataString.substr(12)); + } + else if ((found = dataString.find("%YAxisTitle:") == 0)) + { + _widget.setYAxisText(dataString.substr(12)); + } + else if ((found = dataString.find("%YMax:") == 0)) + { + double ymax; + std::istringstream stream(dataString.substr(6)); + stream >> ymax; + // _gdata->scale = ymax; + } + else if ((found = dataString.find("%DataSet:") == 0)) + { + std::tr1::shared_ptr<GraphData> dataSet(new GraphData); + std::string setName; + int hexColor; + std::string style; + std::istringstream stream(dataString.substr(9)); + stream >> setName >> dataSet->scale >> std::hex >> hexColor + >> style; + dataSet->color[0] = (hexColor >> 16) / 255.0; + dataSet->color[1] = ((hexColor >> 8) & 0xff) / 255.0; + dataSet->color[2] = (hexColor & 0xff) / 255.0; + if (style == "dot") + dataSet->style = GraphData::DOT; + _dataSets.insert(std::make_pair(setName, dataSet)); + _widget.addGraphData(dataSet); + } + } + else + { + std::string dataSet; + double time; + double data; + std::istringstream stream(dataString); + stream >> dataSet >> time >> data; + DataMap::iterator itr = _dataSets.find(dataSet); + if (itr != _dataSets.end()) + itr->second->data.push_back(std::make_pair(time, data)); + } + _buffer.erase(0, ret + 1); + } + return true; + } +}; + +int main(int argc, char** argv) +{ + Gtk::Main app(argc, argv); + + Gtk::Window win; + + win.set_title("Grapher"); + win.set_default_size(600, 200); + + GraphWidget w; + + w.setExtents(0.0, 1.0, 5.0, 0.0); + w.setLineWidth(2); + + StapParser stapParser(win, w); + Glib::signal_io().connect(sigc::mem_fun(stapParser, + &StapParser::ioCallback), + 0, + Glib::IO_IN); + win.add(w); + w.show(); + + Gtk::Main::run(win); + + return 0; +} @@ -273,4 +273,24 @@ find_tracequery_hash (systemtap_session& s) s.tracequery_path = hashdir + "/tracequery_" + result + ".ko"; } + +void +find_typequery_hash (systemtap_session& s, const string& name, string& module) +{ + hash h; + get_base_hash(s, h); + + // Add the typequery name to distinguish the hash + h.add(name); + + // Get the directory path to store our cached module + string result, hashdir; + h.result(result); + if (!create_hashdir(s, result, hashdir)) + return; + + module = hashdir + "/typequery_" + result + + (name[0] == 'k' ? ".ko" : ".so"); +} + /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ @@ -37,5 +37,7 @@ public: void find_hash (systemtap_session& s, const std::string& script); void find_tracequery_hash (systemtap_session& s); +void find_typequery_hash (systemtap_session& s, const std::string& name, + std::string& module); /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ diff --git a/runtime/syscall.h b/runtime/syscall.h index 5e538389..ffc21efc 100644 --- a/runtime/syscall.h +++ b/runtime/syscall.h @@ -124,6 +124,14 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs) static inline long syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { + if ((long)regs->cr_ifs < 0) /* Not a syscall */ + return -1; + +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(regs)) + return regs->r1; +#endif + return regs->r15; } #endif @@ -320,38 +328,119 @@ syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, #endif #if defined(__ia64__) -#define syscall_get_arguments(task, regs, i, n, args) \ - __ia64_syscall_get_arguments(task, regs, i, n, args, &c->unwaddr) -static inline void -__ia64_syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, - unsigned int i, unsigned int n, - unsigned long *args, unsigned long **cache) +/* Return TRUE if PT was created due to kernel-entry via a system-call. */ + +static inline int +in_syscall (struct pt_regs *pt) { - if (i + n > 6) { - _stp_error("invalid syscall arg request"); + return (long) pt->cr_ifs >= 0; +} + +struct syscall_get_set_args { + unsigned int i; + unsigned int n; + unsigned long *args; + struct pt_regs *regs; + int rw; +}; + +static void syscall_get_set_args_cb(struct unw_frame_info *info, void *data) +{ + struct syscall_get_set_args *args = data; + struct pt_regs *pt = args->regs; + unsigned long *krbs, cfm, ndirty; + int i, count; + + if (unw_unwind_to_user(info) < 0) return; + + cfm = pt->cr_ifs; + krbs = (unsigned long *)info->task + IA64_RBS_OFFSET/8; + ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19)); + + count = 0; + if (in_syscall(pt)) + count = min_t(int, args->n, cfm & 0x7f); + + for (i = 0; i < count; i++) { + if (args->rw) + *ia64_rse_skip_regs(krbs, ndirty + i + args->i) = + args->args[i]; + else + args->args[i] = *ia64_rse_skip_regs(krbs, + ndirty + i + args->i); } - switch (i) { - case 0: - if (!n--) break; - *args++ = ia64_fetch_register(32, regs, cache); - case 1: - if (!n--) break; - *args++ = ia64_fetch_register(33, regs, cache); - case 2: - if (!n--) break; - *args++ = ia64_fetch_register(34, regs, cache); - case 3: - if (!n--) break; - *args++ = ia64_fetch_register(35, regs, cache); - case 4: - if (!n--) break; - *args++ = ia64_fetch_register(36, regs, cache); - case 5: - if (!n--) break; - *args++ = ia64_fetch_register(37, regs, cache); + + if (!args->rw) { + while (i < args->n) { + args->args[i] = 0; + i++; + } + } +} + +void ia64_syscall_get_set_arguments(struct task_struct *task, + struct pt_regs *regs, unsigned int i, unsigned int n, + unsigned long *args, int rw) +{ + struct syscall_get_set_args data = { + .i = i, + .n = n, + .args = args, + .regs = regs, + .rw = rw, + }; + + if (task == current) + unw_init_running(syscall_get_set_args_cb, &data); + else { + struct unw_frame_info ufi; + memset(&ufi, 0, sizeof(ufi)); + unw_init_from_blocked_task(&ufi, task); + syscall_get_set_args_cb(&ufi, &data); + } +} + +static inline void syscall_get_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned int i, unsigned int n, + unsigned long *args) +{ + BUG_ON(i + n > 6); + +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(regs)) { + switch (i + n) { + case 6: + if (!n--) break; + *args++ = regs->r13; + case 5: + if (!n--) break; + *args++ = regs->r15; + case 4: + if (!n--) break; + *args++ = regs->r14; + case 3: + if (!n--) break; + *args++ = regs->r10; + case 2: + if (!n--) break; + *args++ = regs->r9; + case 1: + if (!n--) break; + *args++ = regs->r11; + case 0: + if (!n--) break; + default: + BUG(); + break; + } + + return; } +#endif + ia64_syscall_get_set_arguments(task, regs, i, n, args, 0); } #endif diff --git a/runtime/task_finder.c b/runtime/task_finder.c index fa6c296e..93b89cb9 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -1071,9 +1071,6 @@ __stp_utrace_task_finder_target_syscall_exit(enum utrace_resume_action action, int rc; struct mm_struct *mm; struct vm_area_struct *vma; -#if defined(__ia64__) - struct { unsigned long *unwaddr; } _c = {.unwaddr = NULL}, *c = &_c; -#endif if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) { debug_task_finder_detach(); diff --git a/runtime/uprobes/uprobes_i386.c b/runtime/uprobes/uprobes_i386.c index c43f87bf..7743f400 100644 --- a/runtime/uprobes/uprobes_i386.c +++ b/runtime/uprobes/uprobes_i386.c @@ -301,9 +301,20 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, return orig_ret_addr; } +/* + * On x86_32, if a function returns a struct or union, the return + * value is copied into an area created by the caller. The address + * of this area is passed on the stack as a "hidden" first argument. + * When such a function returns, it uses a "ret $4" instruction to pop + * not only the return address but also the hidden arg. To accommodate + * such functions, we add 4 bytes of slop when predicting the return + * address. See PR #10078. + */ +#define STRUCT_RETURN_SLOP 4 + static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, struct task_struct *tsk) { - return (unsigned long) (regs->esp + 4); + return (unsigned long) (regs->esp + 4 + STRUCT_RETURN_SLOP); } diff --git a/runtime/uprobes/uprobes_x86.c b/runtime/uprobes/uprobes_x86.c index 404c9518..93331715 100644 --- a/runtime/uprobes/uprobes_x86.c +++ b/runtime/uprobes/uprobes_x86.c @@ -716,12 +716,23 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, return orig_ret_addr; } +/* + * On x86_32, if a function returns a struct or union, the return + * value is copied into an area created by the caller. The address + * of this area is passed on the stack as a "hidden" first argument. + * When such a function returns, it uses a "ret $4" instruction to pop + * not only the return address but also the hidden arg. To accommodate + * such functions, we add 4 bytes of slop when predicting the return + * address. See PR #10078. + */ +#define STRUCT_RETURN_SLOP 4 + static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, struct task_struct *tsk) { if (test_tsk_thread_flag(tsk, TIF_IA32)) - return (unsigned long) (REGS_SP + 4); + return (unsigned long) (REGS_SP + 4 + STRUCT_RETURN_SLOP); else return (unsigned long) (REGS_SP + 8); } diff --git a/runtime/uprobes2/uprobes_x86.h b/runtime/uprobes2/uprobes_x86.h index ca3f4873..a07fa0d3 100644 --- a/runtime/uprobes2/uprobes_x86.h +++ b/runtime/uprobes2/uprobes_x86.h @@ -93,11 +93,22 @@ static inline unsigned long arch_get_cur_sp(struct pt_regs *regs) return (unsigned long) regs->sp; } +/* + * On x86_32, if a function returns a struct or union, the return + * value is copied into an area created by the caller. The address + * of this area is passed on the stack as a "hidden" first argument. + * When such a function returns, it uses a "ret $4" instruction to pop + * not only the return address but also the hidden arg. To accommodate + * such functions, we add 4 bytes of slop when predicting the return + * address. See PR #10078. + */ +#define STRUCT_RETURN_SLOP 4 + static inline unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, struct task_struct *tsk) { if (test_tsk_thread_flag(tsk, TIF_IA32)) - return (unsigned long) (regs->sp + 4); + return (unsigned long) (regs->sp + 4 + STRUCT_RETURN_SLOP); else return (unsigned long) (regs->sp + 8); } @@ -30,6 +30,7 @@ struct functiondecl; struct derived_probe; struct be_derived_probe_group; struct dwarf_derived_probe_group; +struct kprobe_derived_probe_group; struct uprobe_derived_probe_group; struct utrace_derived_probe_group; struct itrace_derived_probe_group; @@ -170,6 +171,7 @@ struct systemtap_session // session.probes vector. be_derived_probe_group* be_derived_probes; dwarf_derived_probe_group* dwarf_derived_probes; + kprobe_derived_probe_group* kprobe_derived_probes; uprobe_derived_probe_group* uprobe_derived_probes; utrace_derived_probe_group* utrace_derived_probes; itrace_derived_probe_group* itrace_derived_probes; diff --git a/stapprobes.3stap.in b/stapprobes.3stap.in index a8988c71..e60a8fe4 100644 --- a/stapprobes.3stap.in +++ b/stapprobes.3stap.in @@ -64,6 +64,7 @@ syscall.* kernel.function("no_such_function") ? module("awol").function("no_such_function") ! signal.*? if (switch) +kprobe.function("foo") .ESAMPLE @@ -378,6 +379,40 @@ Other local variables are not generally accessible, since by the time a ".return" probe hits, the probed function will have already returned. +.SS DWARFLESS +In absence of debugging information, entry & exit points of kernel & module +functions can be probed using the "kprobe" family of probes. +However, these do not permit looking up the arguments / local variables +of the function. +Following constructs are supported : +.SAMPLE +kprobe.function(FUNCTION) +kprobe.function(FUNCTION).return +kprobe.module(NAME).function(FUNCTION) +kprobe.module(NAME).function(FUNCTION).return +kprobe.statement.(ADDRESS).absolute +.ESAMPLE +.PP +Probes of type +.B function +are recommended for kernel functions, whereas probes of type +.B module +are recommended for probing functions of the specified module. +In case the absolute address of a kernel or module function is known, +.B statement +probes can be utilized. +.PP +Note that +.I FUNCTION +and +.I MODULE +names +.B must not +contain wildcards, or the probe will not be registered. +Also, statement probes must be run under guru-mode only. + + + .SS USER-SPACE Early prototype support for user-space probing is available in the form of a non-symbolic probe point: diff --git a/tapsets.cxx b/tapsets.cxx index 01c838d9..f99fbef4 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -2619,7 +2619,38 @@ struct uprobe_derived_probe: public derived_probe void join_group (systemtap_session& s); }; +struct kprobe_derived_probe: public derived_probe +{ + kprobe_derived_probe (probe *base, + probe_point *location, + const string& name, + int64_t stmt_addr, + bool has_return, + bool has_statement + ); + string symbol_name; + Dwarf_Addr addr; + bool has_return; + bool has_statement; + bool has_maxactive; + long maxactive_val; + bool access_var; + void printsig (std::ostream &o) const; + void join_group (systemtap_session& s); +}; +struct kprobe_derived_probe_group: public derived_probe_group +{ +private: + multimap<string,kprobe_derived_probe*> probes_by_module; + typedef multimap<string,kprobe_derived_probe*>::iterator p_b_m_iterator; + +public: + void enroll (kprobe_derived_probe* probe); + void emit_module_decls (systemtap_session& s); + void emit_module_init (systemtap_session& s); + void emit_module_exit (systemtap_session& s); +}; struct dwarf_derived_probe_group: public derived_probe_group { @@ -4449,7 +4480,6 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor }; - unsigned var_expanding_visitor::tick = 0; void @@ -5104,9 +5134,64 @@ struct dwarf_cast_expanding_visitor: public var_expanding_visitor dwarf_cast_expanding_visitor(systemtap_session& s, dwarf_builder& db): s(s), db(db) {} void visit_cast_op (cast_op* e); + void filter_special_modules(string& module); }; +void dwarf_cast_expanding_visitor::filter_special_modules(string& module) +{ + // look for "kmod<path/to/header>" or "umod<path/to/header>" + // for those cases, build a module including that header + if (module.rfind('>') == module.size() - 1 && + (module.compare(0, 5, "kmod<") == 0 || + module.compare(0, 5, "umod<") == 0)) + { + string cached_module; + if (s.use_cache) + { + // see if the cached module exists + find_typequery_hash(s, module, cached_module); + if (!cached_module.empty()) + { + int fd = open(cached_module.c_str(), O_RDONLY); + if (fd != -1) + { + if (s.verbose > 2) + clog << "Pass 2: using cached " << cached_module << endl; + module = cached_module; + close(fd); + return; + } + } + } + + // no cached module, time to make it + int rc; + string new_module, header = module.substr(5, module.size() - 6); + if (module[0] == 'k') + rc = make_typequery_kmod(s, header, new_module); + else + rc = make_typequery_umod(s, header, new_module); + if (rc == 0) + { + module = new_module; + + if (s.use_cache) + { + // try to save typequery in the cache + if (s.verbose > 2) + clog << "Copying " << new_module + << " to " << cached_module << endl; + if (copy_file(new_module.c_str(), + cached_module.c_str()) != 0) + cerr << "Copy failed (\"" << new_module << "\" to \"" + << cached_module << "\"): " << strerror(errno) << endl; + } + } + } +} + + void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) { bool lvalue = is_active_lvalue(e); @@ -5125,6 +5210,7 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) size_t mod_begin = mod_end + 1; mod_end = e->module.find(':', mod_begin); string module = e->module.substr(mod_begin, mod_end - mod_begin); + filter_special_modules(module); // NB: This uses '/' to distinguish between kernel modules and userspace, // which means that userspace modules won't get any PATH searching. @@ -5134,10 +5220,21 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) // kernel or kernel module target if (! db.kern_dw) { - db.kern_dw = new dwflpp(s); - db.kern_dw->setup_kernel(true); + dw = new dwflpp(s); + try + { + dw->setup_kernel(true); + } + catch (const semantic_error& er) + { + /* ignore and go to the next module */ + delete dw; + continue; + } + db.kern_dw = dw; } - dw = db.kern_dw; + else + dw = db.kern_dw; } else { @@ -5148,7 +5245,16 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) if (db.user_dw.find(module) == db.user_dw.end()) { dw = new dwflpp(s); - dw->setup_user(module); + try + { + dw->setup_user(module); + } + catch (const semantic_error& er) + { + /* ignore and go to the next module */ + delete dw; + continue; + } db.user_dw[module] = dw; } else @@ -5468,6 +5574,23 @@ dwarf_derived_probe_group::enroll (dwarf_derived_probe* p) // sequentially. } +/* +void +dwarf_derived_probe_group::enroll (kprobe_derived_probe* p) +{ + dwarf_derived_probe *dw_probe = new dwarf_derived_probe (p->symbol_name, + "",0, + p->module_name, + p->section_name, + 0,0, + p->q,NULL); + probes_by_module.insert (make_pair (p->module, p)); + + // XXX: probes put at the same address should all share a + // single kprobe/kretprobe, and have their handlers executed + // sequentially. +} +*/ void dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) @@ -5550,6 +5673,7 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) CALCIT(module); CALCIT(section); CALCIT(pp); +#undef CALCIT s.op->newline() << "const unsigned long address;"; s.op->newline() << "void (* const ph) (struct context*);"; @@ -6319,7 +6443,6 @@ emit_vma_callback_probe_decl (systemtap_session& s, s.op->line() << " },"; } - // ------------------------------------------------------------------------ // task_finder derived 'probes': These don't really exist. The whole // purpose of the task_finder_derived_probe_group is to make sure that @@ -6728,17 +6851,24 @@ public: struct utrace_var_expanding_visitor: public var_expanding_visitor { - utrace_var_expanding_visitor(systemtap_session& s, const string& pn, + utrace_var_expanding_visitor(systemtap_session& s, probe_point* l, + const string& pn, enum utrace_derived_probe_flags f): - sess (s), probe_name (pn), flags (f), target_symbol_seen (false) {} + sess (s), base_loc (l), probe_name (pn), flags (f), + target_symbol_seen (false), add_block(NULL), add_probe(NULL) {} systemtap_session& sess; + probe_point* base_loc; string probe_name; enum utrace_derived_probe_flags flags; bool target_symbol_seen; + block *add_block; + probe *add_probe; + std::map<std::string, symbol *> return_ts_map; void visit_target_symbol_arg (target_symbol* e); void visit_target_symbol_context (target_symbol* e); + void visit_target_symbol_cached (target_symbol* e); void visit_target_symbol (target_symbol* e); }; @@ -6753,10 +6883,23 @@ utrace_derived_probe::utrace_derived_probe (systemtap_session &s, target_symbol_seen(false) { // Expand local variables in the probe body - utrace_var_expanding_visitor v (s, name, flags); + utrace_var_expanding_visitor v (s, l, name, flags); this->body = v.require (this->body); target_symbol_seen = v.target_symbol_seen; + // If during target-variable-expanding the probe, we added a new block + // of code, add it to the start of the probe. + if (v.add_block) + this->body = new block(v.add_block, this->body); + // If when target-variable-expanding the probe, we added a new + // probe, add it in a new file to the list of files to be processed. + if (v.add_probe) + { + stapfile *f = new stapfile; + f->probes.push_back(v.add_probe); + s.files.push_back(f); + } + // Reset the sole element of the "locations" vector as a // "reverse-engineered" form of the incoming (q.base_loc) probe // point. This allows a user to see what program etc. @@ -6816,6 +6959,216 @@ utrace_derived_probe::join_group (systemtap_session& s) void +utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e) +{ + // Get the full name of the target symbol. + stringstream ts_name_stream; + e->print(ts_name_stream); + string ts_name = ts_name_stream.str(); + + // Check and make sure we haven't already seen this target + // variable in this return probe. If we have, just return our + // last replacement. + map<string, symbol *>::iterator i = return_ts_map.find(ts_name); + if (i != return_ts_map.end()) + { + provide (i->second); + return; + } + + // We've got to do several things here to handle target + // variables in return probes. + + // (1) Synthesize a global array which is the cache of the + // target variable value. We don't need a nesting level counter + // like the dwarf_var_expanding_visitor::visit_target_symbol() + // does since a particular thread can only be in one system + // calls at a time. The array will look like this: + // + // _utrace_tvar_{name}_{num} + string aname = (string("_utrace_tvar_") + + e->base_name.substr(1) + + "_" + lex_cast<string>(tick++)); + vardecl* vd = new vardecl; + vd->name = aname; + vd->tok = e->tok; + sess.globals.push_back (vd); + + // (2) Create a new code block we're going to insert at the + // beginning of this probe to get the cached value into a + // temporary variable. We'll replace the target variable + // reference with the temporary variable reference. The code + // will look like this: + // + // _utrace_tvar_tid = tid() + // _utrace_tvar_{name}_{num}_tmp + // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid] + // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid] + + // (2a) Synthesize the tid temporary expression, which will look + // like this: + // + // _utrace_tvar_tid = tid() + symbol* tidsym = new symbol; + tidsym->name = string("_utrace_tvar_tid"); + tidsym->tok = e->tok; + + if (add_block == NULL) + { + add_block = new block; + add_block->tok = e->tok; + + // Synthesize a functioncall to grab the thread id. + functioncall* fc = new functioncall; + fc->tok = e->tok; + fc->function = string("tid"); + + // Assign the tid to '_utrace_tvar_tid'. + assignment* a = new assignment; + a->tok = e->tok; + a->op = "="; + a->left = tidsym; + a->right = fc; + + expr_statement* es = new expr_statement; + es->tok = e->tok; + es->value = a; + add_block->statements.push_back (es); + } + + // (2b) Synthesize an array reference and assign it to a + // temporary variable (that we'll use as replacement for the + // target variable reference). It will look like this: + // + // _utrace_tvar_{name}_{num}_tmp + // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid] + + arrayindex* ai_tvar = new arrayindex; + ai_tvar->tok = e->tok; + + symbol* sym = new symbol; + sym->name = aname; + sym->tok = e->tok; + ai_tvar->base = sym; + + ai_tvar->indexes.push_back(tidsym); + + symbol* tmpsym = new symbol; + tmpsym->name = aname + "_tmp"; + tmpsym->tok = e->tok; + + assignment* a = new assignment; + a->tok = e->tok; + a->op = "="; + a->left = tmpsym; + a->right = ai_tvar; + + expr_statement* es = new expr_statement; + es->tok = e->tok; + es->value = a; + + add_block->statements.push_back (es); + + // (2c) Delete the array value. It will look like this: + // + // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid] + + delete_statement* ds = new delete_statement; + ds->tok = e->tok; + ds->value = ai_tvar; + add_block->statements.push_back (ds); + + // (3) We need an entry probe that saves the value for us in the + // global array we created. Create the entry probe, which will + // look like this: + // + // probe process(PATH_OR_PID).syscall { + // _utrace_tvar_tid = tid() + // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param} + // } + // + // Why the temporary for tid()? If we end up caching more + // than one target variable, we can reuse the temporary instead + // of calling tid() multiple times. + + if (add_probe == NULL) + { + add_probe = new probe; + add_probe->tok = e->tok; + + // We need the name of the current probe point, minus the + // ".return". Create a new probe point, copying all the + // components, stopping when we see the ".return" + // component. + probe_point* pp = new probe_point; + for (unsigned c = 0; c < base_loc->components.size(); c++) + { + if (base_loc->components[c]->functor == "return") + break; + else + pp->components.push_back(base_loc->components[c]); + } + pp->tok = e->tok; + pp->optional = base_loc->optional; + add_probe->locations.push_back(pp); + + add_probe->body = new block; + add_probe->body->tok = e->tok; + + // Synthesize a functioncall to grab the thread id. + functioncall* fc = new functioncall; + fc->tok = e->tok; + fc->function = string("tid"); + + // Assign the tid to '_utrace_tvar_tid'. + assignment* a = new assignment; + a->tok = e->tok; + a->op = "="; + a->left = tidsym; + a->right = fc; + + expr_statement* es = new expr_statement; + es->tok = e->tok; + es->value = a; + add_probe->body = new block(add_probe->body, es); + + vardecl* vd = new vardecl; + vd->tok = e->tok; + vd->name = tidsym->name; + vd->type = pe_long; + vd->set_arity(0); + add_probe->locals.push_back(vd); + } + + // Save the value, like this: + // + // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param} + a = new assignment; + a->tok = e->tok; + a->op = "="; + a->left = ai_tvar; + a->right = e; + + es = new expr_statement; + es->tok = e->tok; + es->value = a; + + add_probe->body = new block(add_probe->body, es); + + // (4) Provide the '_utrace_tvar_{name}_{num}_tmp' variable to + // our parent so it can be used as a substitute for the target + // symbol. + provide (tmpsym); + + // (5) Remember this replacement since we might be able to reuse + // it later if the same return probe references this target + // symbol again. + return_ts_map[ts_name] = tmpsym; + return; +} + + +void utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) { string argnum_s = e->base_name.substr(4,e->base_name.length()-4); @@ -6903,8 +7256,30 @@ utrace_var_expanding_visitor::visit_target_symbol_context (target_symbol* e) throw semantic_error ("only \"process(PATH_OR_PID).syscall.return\" support $return.", e->tok); fname = "_utrace_syscall_return"; } + else if (sname == "$syscall") + { + // If we've got a syscall entry probe, we can just call the + // right function. + if (flags == UDPF_SYSCALL) { + fname = "_utrace_syscall_nr"; + } + // If we're in a syscal return probe, we can't really access + // $syscall. So, similar to what + // dwarf_var_expanding_visitor::visit_target_symbol() does, + // we'll create an syscall entry probe to cache $syscall, then + // we'll access the cached value in the syscall return probe. + else { + visit_target_symbol_cached (e); + + // Remember that we've seen a target variable. + target_symbol_seen = true; + return; + } + } else - fname = "_utrace_syscall_nr"; + { + throw semantic_error ("unknown target variable", e->tok); + } // Remember that we've seen a target variable. target_symbol_seen = true; @@ -7258,7 +7633,7 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->indent(1); s.op->newline() << "switch (p->flags) {"; s.op->indent(1); - // For death probes, go ahead and call the probe directly. + // For end probes, go ahead and call the probe directly. if (flags_seen[UDPF_END]) { s.op->newline() << "case UDPF_END:"; @@ -7952,8 +8327,402 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s) s.op->newline() << "mutex_destroy (& stap_uprobes_lock);"; } +// ------------------------------------------------------------------------ +// Kprobe derived probes +// ------------------------------------------------------------------------ + +static string TOK_KPROBE("kprobe"); + +kprobe_derived_probe::kprobe_derived_probe (probe *base, + probe_point *location, + const string& name, + int64_t stmt_addr, + bool if_return, + bool if_statement +): + derived_probe (base, location), + symbol_name (name), addr (stmt_addr), + has_return (if_return), has_statement (if_statement) +{ + this->tok = base->tok; + this->access_var = false; + +#ifndef USHRT_MAX +#define USHRT_MAX 32767 +#endif + + // Expansion of $target variables in the probe body produces an error during translate phase + vector<probe_point::component*> comps; + + if (has_return) + comps.push_back (new probe_point::component(TOK_RETURN)); + + this->sole_location()->components = comps; +} + +void kprobe_derived_probe::printsig (ostream& o) const +{ + sole_location()->print (o); + o << " /* " << " name = " << symbol_name << "*/"; + printsig_nested (o); +} + +void kprobe_derived_probe::join_group (systemtap_session& s) +{ + + if (! s.kprobe_derived_probes) + s.kprobe_derived_probes = new kprobe_derived_probe_group (); + s.kprobe_derived_probes->enroll (this); +} +void kprobe_derived_probe_group::enroll (kprobe_derived_probe* p) +{ + probes_by_module.insert (make_pair (p->symbol_name, p)); + // probes of same symbol should share single kprobe/kretprobe +} + +void +kprobe_derived_probe_group::emit_module_decls (systemtap_session& s) +{ + if (probes_by_module.empty()) return; + + s.op->newline() << "/* ---- kprobe-based probes ---- */"; + + // Warn of misconfigured kernels + s.op->newline() << "#if ! defined(CONFIG_KPROBES)"; + s.op->newline() << "#error \"Need CONFIG_KPROBES!\""; + s.op->newline() << "#endif"; + s.op->newline(); + + // Forward declare the master entry functions + s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,"; + s.op->line() << " struct pt_regs *regs);"; + s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,"; + s.op->line() << " struct pt_regs *regs);"; + + // Emit an array of kprobe/kretprobe pointers + s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)"; + s.op->newline() << "static void * stap_unreg_kprobes[" << probes_by_module.size() << "];"; + s.op->newline() << "#endif"; + + // Emit the actual probe list. + + s.op->newline() << "static struct stap_dwarfless_kprobe {"; + s.op->newline(1) << "union { struct kprobe kp; struct kretprobe krp; } u;"; + s.op->newline() << "#ifdef __ia64__"; + s.op->newline() << "struct kprobe dummy;"; + s.op->newline() << "#endif"; + s.op->newline(-1) << "} stap_dwarfless_kprobes[" << probes_by_module.size() << "];"; + // NB: bss! + + s.op->newline() << "static struct stap_dwarfless_probe {"; + s.op->newline(1) << "const unsigned return_p:1;"; + s.op->newline() << "const unsigned maxactive_p:1;"; + s.op->newline() << "const unsigned statement_p:1;"; + s.op->newline() << "unsigned registered_p:1;"; + s.op->newline() << "const unsigned short maxactive_val;"; + + // Function Names are mostly small and uniform enough to justify putting + // char[MAX]'s into the array instead of relocated char*'s. + + size_t pp_name_max = 0, symbol_string_name_max = 0; + size_t pp_name_tot = 0, symbol_string_name_tot = 0; + for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++) + { + kprobe_derived_probe* p = it->second; +#define DOIT(var,expr) do { \ + size_t var##_size = (expr) + 1; \ + var##_max = max (var##_max, var##_size); \ + var##_tot += var##_size; } while (0) + DOIT(pp_name, lex_cast_qstring(*p->sole_location()).size()); + DOIT(symbol_string_name, p->symbol_name.size()); +#undef DOIT + } + +#define CALCIT(var) \ + s.op->newline() << "const char " << #var << "[" << var##_name_max << "] ;"; + + CALCIT(pp); + CALCIT(symbol_string); +#undef CALCIT + + s.op->newline() << "const unsigned long address;"; + s.op->newline() << "void (* const ph) (struct context*);"; + s.op->newline(-1) << "} stap_dwarfless_probes[] = {"; + s.op->indent(1); + + for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++) + { + kprobe_derived_probe* p = it->second; + s.op->newline() << "{"; + if (p->has_return) + s.op->line() << " .return_p=1,"; + + if (p->has_maxactive) + { + s.op->line() << " .maxactive_p=1,"; + assert (p->maxactive_val >= 0 && p->maxactive_val <= USHRT_MAX); + s.op->line() << " .maxactive_val=" << p->maxactive_val << ","; + } + if (p->has_statement) + { + s.op->line() << " .statement_p=1,"; + s.op->line() << " .address=(unsigned long)0x" << hex << p->addr << dec << "ULL,"; + s.op->line() << " .symbol_string=\"" << "\","; + } + else + { + s.op->line() << " .address=(unsigned long)0x" << hex << 0 << dec << "ULL,"; + s.op->line() << " .symbol_string=\"" << p->symbol_name << "\","; + } + + s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ","; + s.op->line() << " .ph=&" << p->name; + s.op->line() << " },"; + } + + s.op->newline(-1) << "};"; + + // Emit the kprobes callback function + s.op->newline(); + s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,"; + s.op->line() << " struct pt_regs *regs) {"; + // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS + s.op->newline(1) << "int kprobe_idx = ((uintptr_t)inst-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);"; + // Check that the index is plausible + s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes["; + s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?"; + s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption + // XXX: it would be nice to give a more verbose error though; BUG_ON later? + s.op->line() << "];"; + common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp"); + s.op->newline() << "c->regs = regs;"; + s.op->newline() << "(*sdp->ph) (c);"; + common_probe_entryfn_epilogue (s.op); + s.op->newline() << "return 0;"; + s.op->newline(-1) << "}"; + + // Same for kretprobes + s.op->newline(); + s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,"; + s.op->line() << " struct pt_regs *regs) {"; + s.op->newline(1) << "struct kretprobe *krp = inst->rp;"; + + // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS + s.op->newline() << "int kprobe_idx = ((uintptr_t)krp-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);"; + // Check that the index is plausible + s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes["; + s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?"; + s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption + // XXX: it would be nice to give a more verbose error though; BUG_ON later? + s.op->line() << "];"; + + common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp"); + s.op->newline() << "c->regs = regs;"; + s.op->newline() << "c->pi = inst;"; // for assisting runtime's backtrace logic + s.op->newline() << "(*sdp->ph) (c);"; + common_probe_entryfn_epilogue (s.op); + s.op->newline() << "return 0;"; + s.op->newline(-1) << "}"; +} + + +void +kprobe_derived_probe_group::emit_module_init (systemtap_session& s) +{ +#define CHECK_STMT(var) \ + s.op->newline() << "if (sdp->statement_p) {"; \ + s.op->newline() << var << ".symbol_name = NULL;"; \ + s.op->newline() << "} else {"; \ + s.op->newline() << var << ".symbol_name = sdp->symbol_string;"; \ + s.op->newline() << "}"; + + s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {"; + s.op->newline() << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];"; + s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];"; + s.op->newline() << "unsigned long relocated_addr = sdp->address;"; + s.op->newline() << "probe_point = sdp->pp;"; // for error messages + s.op->newline() << "if (sdp->return_p) {"; + s.op->newline(1) << "kp->u.krp.kp.addr = (void *) relocated_addr;"; + CHECK_STMT("kp->u.krp.kp"); + s.op->newline() << "if (sdp->maxactive_p) {"; + s.op->newline(1) << "kp->u.krp.maxactive = sdp->maxactive_val;"; + s.op->newline(-1) << "} else {"; + s.op->newline(1) << "kp->u.krp.maxactive = max(10, 4*NR_CPUS);"; + s.op->newline(-1) << "}"; + s.op->newline() << "kp->u.krp.handler = &enter_kretprobe_probe;"; + // to ensure safeness of bspcache, always use aggr_kprobe on ia64 + s.op->newline() << "#ifdef __ia64__"; + s.op->newline() << "kp->dummy.pre_handler = NULL;"; + s.op->newline() << "kp->dummy.addr = kp->u.krp.kp.addr;"; + CHECK_STMT("kp->dummy"); + s.op->newline() << "rc = register_kprobe (& kp->dummy);"; + s.op->newline() << "if (rc == 0) {"; + s.op->newline(1) << "rc = register_kretprobe (& kp->u.krp);"; + s.op->newline() << "if (rc != 0)"; + s.op->newline(1) << "unregister_kprobe (& kp->dummy);"; + s.op->newline(-2) << "}"; + s.op->newline() << "#else"; + s.op->newline() << "rc = register_kretprobe (& kp->u.krp);"; + s.op->newline() << "#endif"; + s.op->newline(-1) << "} else {"; + // to ensure safeness of bspcache, always use aggr_kprobe on ia64 + s.op->newline(1) << "kp->u.kp.addr = (void *) relocated_addr;"; + CHECK_STMT("kp->u.kp"); + s.op->newline(1) << "kp->u.kp.pre_handler = &enter_kprobe_probe;"; + s.op->newline() << "#ifdef __ia64__"; + s.op->newline() << "kp->dummy.addr = kp->u.kp.addr;"; + s.op->newline() << "kp->dummy.pre_handler = NULL;"; + CHECK_STMT("kp->dummy"); + s.op->newline() << "rc = register_kprobe (& kp->dummy);"; + s.op->newline() << "if (rc == 0) {"; + s.op->newline(1) << "rc = register_kprobe (& kp->u.kp);"; + s.op->newline() << "if (rc != 0)"; + s.op->newline(1) << "unregister_kprobe (& kp->dummy);"; + s.op->newline(-2) << "}"; + s.op->newline() << "#else"; + s.op->newline() << "rc = register_kprobe (& kp->u.kp);"; + s.op->newline() << "#endif"; + s.op->newline(-1) << "}"; + s.op->newline() << "if (rc) {"; // PR6749: tolerate a failed register_*probe. + s.op->newline(1) << "sdp->registered_p = 0;"; + s.op->newline() << "if (rc == -EINVAL)"; + s.op->newline() << "{"; + s.op->newline() << " _stp_error (\"Error registering kprobe,possibly an incorrect name %s OR addr = %p, rc = %d \", sdp->symbol_string, sdp->address, rc);"; + s.op->newline() << " atomic_set (&session_state, STAP_SESSION_ERROR);"; + s.op->newline() << " goto out;"; + s.op->newline() << "}"; + s.op->newline() << "else"; + s.op->newline() << "_stp_warn (\"probe %s for %s registration error (rc %d)\", probe_point, sdp->pp, rc);"; + s.op->newline() << "rc = 0;"; // continue with other probes + // XXX: shall we increment numskipped? + s.op->newline(-1) << "}"; + + s.op->newline() << "else sdp->registered_p = 1;"; + s.op->newline(-1) << "}"; // for loop +#undef CHECK_STMT +} + +void +kprobe_derived_probe_group::emit_module_exit (systemtap_session& s) +{ + //Unregister kprobes by batch interfaces. + s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)"; + s.op->newline() << "j = 0;"; + s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {"; + s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];"; + s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];"; + s.op->newline() << "if (! sdp->registered_p) continue;"; + s.op->newline() << "if (!sdp->return_p)"; + s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.kp;"; + s.op->newline(-2) << "}"; + s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);"; + s.op->newline() << "j = 0;"; + s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {"; + s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];"; + s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];"; + s.op->newline() << "if (! sdp->registered_p) continue;"; + s.op->newline() << "if (sdp->return_p)"; + s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.krp;"; + s.op->newline(-2) << "}"; + s.op->newline() << "unregister_kretprobes((struct kretprobe **)stap_unreg_kprobes, j);"; + s.op->newline() << "#ifdef __ia64__"; + s.op->newline() << "j = 0;"; + s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {"; + s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];"; + s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];"; + s.op->newline() << "if (! sdp->registered_p) continue;"; + s.op->newline() << "stap_unreg_kprobes[j++] = &kp->dummy;"; + s.op->newline(-1) << "}"; + s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);"; + s.op->newline() << "#endif"; + s.op->newline() << "#endif"; + + s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {"; + s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];"; + s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];"; + s.op->newline() << "if (! sdp->registered_p) continue;"; + s.op->newline() << "if (sdp->return_p) {"; + s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)"; + s.op->newline(1) << "unregister_kretprobe (&kp->u.krp);"; + s.op->newline() << "#endif"; + s.op->newline() << "atomic_add (kp->u.krp.nmissed, & skipped_count);"; + s.op->newline() << "#ifdef STP_TIMING"; + s.op->newline() << "if (kp->u.krp.nmissed)"; + s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/1 on '%s': %d\\n\", sdp->pp, kp->u.krp.nmissed);"; + s.op->newline(-1) << "#endif"; + s.op->newline() << "atomic_add (kp->u.krp.kp.nmissed, & skipped_count);"; + s.op->newline() << "#ifdef STP_TIMING"; + s.op->newline() << "if (kp->u.krp.kp.nmissed)"; + s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/2 on '%s': %d\\n\", sdp->pp, kp->u.krp.kp.nmissed);"; + s.op->newline(-1) << "#endif"; + s.op->newline(-1) << "} else {"; + s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)"; + s.op->newline(1) << "unregister_kprobe (&kp->u.kp);"; + s.op->newline() << "#endif"; + s.op->newline() << "atomic_add (kp->u.kp.nmissed, & skipped_count);"; + s.op->newline() << "#ifdef STP_TIMING"; + s.op->newline() << "if (kp->u.kp.nmissed)"; + s.op->newline(1) << "_stp_warn (\"Skipped due to missed kprobe on '%s': %d\\n\", sdp->pp, kp->u.kp.nmissed);"; + s.op->newline(-1) << "#endif"; + s.op->newline(-1) << "}"; + s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES) && defined(__ia64__)"; + s.op->newline() << "unregister_kprobe (&kp->dummy);"; + s.op->newline() << "#endif"; + s.op->newline() << "sdp->registered_p = 0;"; + s.op->newline(-1) << "}"; +} + +struct kprobe_builder: public derived_probe_builder +{ + kprobe_builder() {} + virtual void build(systemtap_session & sess, + probe * base, + probe_point * location, + literal_map_t const & parameters, + vector<derived_probe *> & finished_results); +}; + + +void +kprobe_builder::build(systemtap_session & sess, + probe * base, + probe_point * location, + literal_map_t const & parameters, + vector<derived_probe *> & finished_results) +{ + string function_string_val, module_string_val; + int64_t statement_num_val = 0; + bool has_function_str, has_module_str, has_statement_num, has_absolute, has_return; + + has_function_str = this->get_param(parameters, TOK_FUNCTION, function_string_val); + has_module_str = this->get_param(parameters, TOK_MODULE, module_string_val); + has_return = this->has_null_param (parameters, TOK_RETURN); + has_statement_num = this->get_param(parameters, TOK_STATEMENT, statement_num_val); + has_absolute = this->has_null_param (parameters, TOK_ABSOLUTE); + + if ( has_function_str ) + { + if ( has_module_str ) + function_string_val = module_string_val + ":" + function_string_val; + finished_results.push_back ( new kprobe_derived_probe ( base, + location, function_string_val, + 0, has_return, + has_statement_num) ); + } + else + { + // assert guru mode for absolute probes + if ( has_statement_num && has_absolute && !base->privileged ) + throw semantic_error ("absolute statement probe in unprivileged script", base->tok); + + finished_results.push_back(new kprobe_derived_probe ( base, + location,"", + statement_num_val, has_return, + has_statement_num)); + } +} // ------------------------------------------------------------------------ // timer derived probes // ------------------------------------------------------------------------ @@ -10977,6 +11746,10 @@ perfmon_derived_probe_group::emit_module_init (translator_output* o) } #endif +// ------------------------------------------------------------------------ +// kprobes-based probes,which postpone symbol resolution until runtime. +// ------------------------------------------------------------------------ + // ------------------------------------------------------------------------ // Standard tapset registry. @@ -11075,6 +11848,18 @@ register_standard_tapsets(systemtap_session & s) s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder()); s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE) ->bind(new procfs_builder()); + + // Kprobe based probe + s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION) + ->bind(new kprobe_builder()); + s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_MODULE) + ->bind_str(TOK_FUNCTION)->bind(new kprobe_builder()); + s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)->bind(TOK_RETURN) + ->bind(new kprobe_builder()); + s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_MODULE) + ->bind_str(TOK_FUNCTION)->bind(TOK_RETURN)->bind(new kprobe_builder()); + s.pattern_root->bind(TOK_KPROBE)->bind_num(TOK_STATEMENT) + ->bind(TOK_ABSOLUTE)->bind(new kprobe_builder()); } @@ -11097,6 +11882,7 @@ all_session_groups(systemtap_session& s) DOONE(profile); DOONE(mark); DOONE(tracepoint); + DOONE(kprobe); DOONE(hrtimer); DOONE(perfmon); DOONE(procfs); diff --git a/testsuite/parseko/utrace01.stp b/testsuite/parseko/utrace01.stp index 1cb4227f..9f3619b5 100755 --- a/testsuite/parseko/utrace01.stp +++ b/testsuite/parseko/utrace01.stp @@ -1,4 +1,4 @@ #! stap -p2 # process NAME must be a string -probe process(/bin/cat).death { } +probe process(/bin/cat).end { } diff --git a/testsuite/semko/utrace01.stp b/testsuite/semko/utrace01.stp deleted file mode 100755 index a4707008..00000000 --- a/testsuite/semko/utrace01.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# missing process NAME|PID -probe process.death { } diff --git a/testsuite/semko/utrace03.stp b/testsuite/semko/utrace03.stp index c682410b..92177ffd 100755 --- a/testsuite/semko/utrace03.stp +++ b/testsuite/semko/utrace03.stp @@ -1,4 +1,4 @@ #! stap -p2 # invalid probe type -probe process("/bin/cat").death.return { } +probe process("/bin/cat").end.return { } diff --git a/testsuite/semko/utrace04.stp b/testsuite/semko/utrace04.stp index 6345f9f6..1d26a43c 100755 --- a/testsuite/semko/utrace04.stp +++ b/testsuite/semko/utrace04.stp @@ -1,4 +1,4 @@ #! stap -p2 -# death probes don't support target symbols -probe process("/bin/cat").death.return { print($syscall) } +# end probes don't support target symbols +probe process("/bin/cat").end { print($syscall) } diff --git a/testsuite/semko/utrace08.stp b/testsuite/semko/utrace08.stp deleted file mode 100755 index a558a5be..00000000 --- a/testsuite/semko/utrace08.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# process path must be absolute -probe process("cat").death { } diff --git a/testsuite/semko/utrace09.stp b/testsuite/semko/utrace09.stp deleted file mode 100755 index 60c49cd2..00000000 --- a/testsuite/semko/utrace09.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# process path must be absolute -probe process("/foo/../bar").death { } diff --git a/testsuite/semko/utrace10.stp b/testsuite/semko/utrace10.stp deleted file mode 100755 index b46baea9..00000000 --- a/testsuite/semko/utrace10.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# path can't contain an empty component -probe process("/foo//bar").death { } diff --git a/testsuite/semko/utrace11.stp b/testsuite/semko/utrace11.stp deleted file mode 100755 index d78b602c..00000000 --- a/testsuite/semko/utrace11.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# path can't end with '/' -probe process("/foo/bar/").death { } diff --git a/testsuite/semko/utrace12.stp b/testsuite/semko/utrace12.stp deleted file mode 100755 index 478aa1d3..00000000 --- a/testsuite/semko/utrace12.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# path can't end with '.' -probe process("/foo/bar/.").death { } diff --git a/testsuite/semko/utrace13.stp b/testsuite/semko/utrace13.stp deleted file mode 100755 index 16cc0391..00000000 --- a/testsuite/semko/utrace13.stp +++ /dev/null @@ -1,4 +0,0 @@ -#! stap -p2 - -# path can't end with '..' -probe process("/foo/bar/..").death { } diff --git a/testsuite/semok/cast.stp b/testsuite/semok/cast.stp index 93da18ef..d30823cd 100755 --- a/testsuite/semok/cast.stp +++ b/testsuite/semok/cast.stp @@ -10,4 +10,8 @@ probe begin { // would be nice to test usermode @cast too, // but who knows what debuginfo is installed... + + // check modules generated from headers + println(@cast(0, "task_struct", "kmod<linux/sched.h>")->tgid) + println(@cast(0, "timeval", "umod<sys/time.h>")->tv_sec) } diff --git a/testsuite/semok/utrace01.stp b/testsuite/semok/utrace01.stp new file mode 100755 index 00000000..864bdf15 --- /dev/null +++ b/testsuite/semok/utrace01.stp @@ -0,0 +1,4 @@ +#! stap -p2 + +# process path doesn't need to be absolute +probe process("cat").end { } diff --git a/testsuite/systemtap.base/bz10078.c b/testsuite/systemtap.base/bz10078.c new file mode 100644 index 00000000..9075fbc7 --- /dev/null +++ b/testsuite/systemtap.base/bz10078.c @@ -0,0 +1,22 @@ +#include <stdlib.h> +#include <stdio.h> + +struct point { int x, y; }; + +struct point mkpoint2(void) +{ + struct point p = { 1, 2 }; + return p; +} + +struct point mkpoint1(void) +{ + return mkpoint2(); +} + +main() +{ + struct point p = mkpoint1(); + printf("%d,%d\n", p.x, p.y); + exit(0); +} diff --git a/testsuite/systemtap.base/bz10078.exp b/testsuite/systemtap.base/bz10078.exp new file mode 100644 index 00000000..cad3a3a8 --- /dev/null +++ b/testsuite/systemtap.base/bz10078.exp @@ -0,0 +1,35 @@ +set test bz10078 + +catch {exec gcc -g -o $test $srcdir/$subdir/$test.c} err +if {$err == "" && [file exists $test]} then { pass "$test compile" } else { fail "$test compile" } + +if {![utrace_p]} { + catch {exec rm -f $test} + untested "$test -p4" + untested "$test -p5" + return +} + +set rc [stap_run_batch $srcdir/$subdir/$test.stp] +if {$rc == 0} then { pass "$test -p4" } else { fail "$test -p4" } + +if {! [installtest_p]} { + catch {exec rm -f $test} + untested "$test -p5" + return +} + +# Pick up the stap being tested. +set stapexe [exec /usr/bin/which stap] +spawn sudo $stapexe $srcdir/$subdir/$test.stp -c ./$test +set ok 0 +expect { + -timeout 60 + -re {mkpoint[^\r\n]* returns\r\n} { incr ok; exp_continue } + -re {1,2\r\n} { incr ok; exp_continue } + timeout { fail "$test (timeout)" } + eof { } +} +wait +if {$ok == 3} then { pass "$test -p5" } else { fail "$test -p5 ($ok)" } +exec rm -f $test diff --git a/testsuite/systemtap.base/bz10078.stp b/testsuite/systemtap.base/bz10078.stp new file mode 100644 index 00000000..0318e4e9 --- /dev/null +++ b/testsuite/systemtap.base/bz10078.stp @@ -0,0 +1,4 @@ +#! stap -p4 +probe process("./bz10078").function("mkpoint*").return { + printf("%s returns\n", probefunc()) +} diff --git a/testsuite/systemtap.base/cast.exp b/testsuite/systemtap.base/cast.exp index df3246e8..74c4d72a 100644 --- a/testsuite/systemtap.base/cast.exp +++ b/testsuite/systemtap.base/cast.exp @@ -1,4 +1,6 @@ set test "cast" set ::result_string {PID OK -execname OK} -stap_run2 $srcdir/$subdir/$test.stp +PID2 OK +execname OK +tv_sec OK} +stap_run2 $srcdir/$subdir/$test.stp -g diff --git a/testsuite/systemtap.base/cast.stp b/testsuite/systemtap.base/cast.stp index bec0cc9b..33a14a28 100644 --- a/testsuite/systemtap.base/cast.stp +++ b/testsuite/systemtap.base/cast.stp @@ -10,6 +10,13 @@ probe begin else printf("PID %d != %d\n", pid, cast_pid) + // Compare PIDs using a generated kernel module + cast_pid = @cast(curr, "task_struct", "kmod<linux/sched.h>")->tgid + if (pid == cast_pid) + println("PID2 OK") + else + printf("PID2 %d != %d\n", pid, cast_pid) + // Compare execnames name = execname() cast_name = kernel_string(@cast(curr, "task_struct")->comm) @@ -18,5 +25,19 @@ probe begin else printf("execname \"%s\" != \"%s\"\n", name, cast_name) + // Compare tv_sec using a generated user module + sec = 42 + cast_sec = @cast(get_timeval(sec), "timeval", "umod<sys/time.h>")->tv_sec + if (sec == cast_sec) + println("tv_sec OK") + else + printf("tv_sec %d != %d\n", sec, cast_sec) + exit() } + +function get_timeval:long(sec:long) %{ + static struct timeval mytime = {0}; + mytime.tv_sec = THIS->sec; + THIS->__retvalue = (long)&mytime; +%} diff --git a/testsuite/systemtap.base/kprobes.exp b/testsuite/systemtap.base/kprobes.exp new file mode 100644 index 00000000..240ecd82 --- /dev/null +++ b/testsuite/systemtap.base/kprobes.exp @@ -0,0 +1,2 @@ +set test "kprobes" +stap_run $srcdir/$subdir/$test.stp diff --git a/testsuite/systemtap.base/kprobes.stp b/testsuite/systemtap.base/kprobes.stp new file mode 100644 index 00000000..62c18347 --- /dev/null +++ b/testsuite/systemtap.base/kprobes.stp @@ -0,0 +1,21 @@ +/* + * kprobes.stp + * Probe to test the functionality of kprobe-based probes + * (Dwarfless Probing) + */ + +probe begin +{ + println("\n Systemtap starting probe"); +} + +probe kprobe.function("vfs_read") +{ + printf("\n probe point hit"); + exit(); +} + +probe end +{ + println("\n Systemtap starting probe"); +} diff --git a/testsuite/systemtap.base/utrace_syscall_args.stp b/testsuite/systemtap.base/utrace_syscall_args.stp index 166e1ace..6c9e14fc 100644 --- a/testsuite/systemtap.base/utrace_syscall_args.stp +++ b/testsuite/systemtap.base/utrace_syscall_args.stp @@ -113,18 +113,23 @@ probe process("utrace_syscall_args").syscall.return { if (syscalls_seen >= 4) { if (syscalls_seen == 4) { mmap_args[7] = $return + mmap_args[8] = $syscall } else if (syscalls_seen == 5) { munmap_args[3] = $return + munmap_args[4] = $syscall } else if (syscalls_seen == 6) { close_args[2] = $return + close_args[3] = $syscall } else if (syscalls_seen == 7) { dup_args[7] = $return + dup_args[8] = $syscall } else if (syscalls_seen == 8) { bad_syscall_args[7] = $return + bad_syscall_args[8] = $syscall syscalls_seen = 0 } } @@ -159,6 +164,13 @@ probe end failures += 1 printf("mmap bad arg 6: 0x%x vs 0x0\n", mmap_args[6]) } + + # Validate syscall number + if (mmap_args[0] != mmap_args[8]) { + failures += 1 + printf("mmap $syscall mismatch: %d vs. %d\n", + mmap_args[0], mmap_args[8]) + } } # print munmap info @@ -191,6 +203,13 @@ probe end failures += 1 printf("munmap bad return value: 0x%x vs 0x0\n", munmap_args[7]) } + + # Validate syscall number + if (munmap_args[0] != munmap_args[4]) { + failures += 1 + printf("munmap $syscall mismatch: %d vs. %d\n", + munmap_args[0], munmap_args[4]) + } } # print close info @@ -207,6 +226,13 @@ probe end printf("close bad arg 1: 0x%x vs 0x%x\n", close_args[0], mmap_args[5]) } + + # Validate syscall number + if (close_args[0] != close_args[3]) { + failures += 1 + printf("close $syscall mismatch: %d vs. %d\n", + close_args[0], close_args[3]) + } } # print dup info @@ -278,6 +304,13 @@ probe end printf("dup bad arg 6: 0x%x vs 0xe38e38e3\n", dup_args[6]) } } + + # Validate syscall number + if (dup_args[0] != dup_args[8]) { + failures += 1 + printf("dup $syscall mismatch: %d vs. %d\n", + dup_args[0], dup_args[8]) + } } # print bad_syscall info @@ -355,6 +388,13 @@ probe end bad_syscall_args[6]) } } + + # Validate syscall number + if (bad_syscall_args[0] != bad_syscall_args[8]) { + failures += 1 + printf("bad_syscall $syscall mismatch: %d vs. %d\n", + bad_syscall_args[0], bad_syscall_args[8]) + } } if (failures == 0) { diff --git a/testsuite/systemtap.examples/general/grapher.stp b/testsuite/systemtap.examples/general/grapher.stp new file mode 100644 index 00000000..4f326ec1 --- /dev/null +++ b/testsuite/systemtap.examples/general/grapher.stp @@ -0,0 +1,32 @@ +#! /usr/bin/stap + +probe begin +{ +printf ("%%Title:CPU utilization\n"); +printf ("%%XAxisTitle:Time"); +printf ("%%YAxisTitle:Percent"); +printf ("%%DataSet:cpu 100 00ff00 bar"); +printf ("%%DataSet:kbd 100 ff0000 dot"); +} + +# CPU utilization +probe begin { qnames["cpu"] ++; qsq_start ("cpu") } +probe scheduler.cpu_on { if (!idle) {qs_wait ("cpu") qs_run ("cpu") }} +probe scheduler.cpu_off { if (!idle) qs_done ("cpu") } + +global qnames + +function qsq_util_reset(q) { + u=qsq_utilization (q, 100) + qsq_start (q) + return u +} + +probe timer.ms(100) { # collect utilization percentages frequently + foreach (q in qnames) + printf("cpu %d %d\n", gettimeofday_ms(), qsq_util_reset(q)) +} + +probe kernel.function("kbd_event") { + printf("kbd %d %d\n", gettimeofday_ms(), 75) +} diff --git a/testsuite/systemtap.examples/profiling/latencytap.stp b/testsuite/systemtap.examples/profiling/latencytap.stp index 28956129..d202ec81 100755 --- a/testsuite/systemtap.examples/profiling/latencytap.stp +++ b/testsuite/systemtap.examples/profiling/latencytap.stp @@ -23,7 +23,7 @@ function _get_sleep_time:long(rq_param:long, p_param:long) function task_backtrace:string (task:long) %{ _stp_stack_snprint_tsk(THIS->__retvalue, MAXSTRINGLEN, - (struct task_struct *)THIS->task, 0, MAXTRACE); + (struct task_struct *)(unsigned long)THIS->task, 0, MAXTRACE); %} probe kernel.function("enqueue_task_fair") { diff --git a/testsuite/systemtap.printf/basic6.exp b/testsuite/systemtap.printf/basic6.exp new file mode 100644 index 00000000..72bf8f57 --- /dev/null +++ b/testsuite/systemtap.printf/basic6.exp @@ -0,0 +1,3 @@ +set test "basic6" +set ::result_string {Hello%World} +stap_run2 $srcdir/$subdir/$test.stp diff --git a/testsuite/systemtap.printf/basic6.stp b/testsuite/systemtap.printf/basic6.stp new file mode 100644 index 00000000..69721188 --- /dev/null +++ b/testsuite/systemtap.printf/basic6.stp @@ -0,0 +1,5 @@ +probe begin +{ + printf("Hello%%World"); + exit() +} diff --git a/translate.cxx b/translate.cxx index 46fea6e7..f7868ceb 100644 --- a/translate.cxx +++ b/translate.cxx @@ -4172,7 +4172,8 @@ c_unparser::visit_print_format (print_format* e) int use_print = 0; string format_string = print_format::components_to_string(components); - if (tmp.size() == 0 || (tmp.size() == 1 && format_string == "%s")) + if ((tmp.size() == 0 && format_string.find("%%") == std::string::npos) + || (tmp.size() == 1 && format_string == "%s")) use_print = 1; else if (tmp.size() == 1 && e->args[0]->tok->type == tok_string |