Index: configure =================================================================== --- configure (revision 85046) +++ configure (working copy) @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84752 . +# From configure.in Revision: 84946 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.2. # @@ -611,6 +611,8 @@ MACHDEP_OBJS DYNLOADFILE DLINCLDIR +DTRACEHDRS +DTRACEOBJS THREADOBJ LDLAST USE_THREAD_MODULE @@ -747,6 +749,7 @@ with_tsc with_pymalloc with_valgrind +with_dtrace with_fpectl with_libm with_libc @@ -1417,6 +1420,7 @@ --with(out)-tsc enable/disable timestamp counter profile --with(out)-pymalloc disable/enable specialized mallocs --with-valgrind Enable Valgrind support + --with(out)-dtrace disable/enable dtrace/SystemTap support --with-fpectl enable SIGFPE catching --with-libm=STRING math library --with-libc=STRING C library @@ -9230,6 +9234,50 @@ OPT="-DDYNAMIC_ANNOTATIONS_ENABLED=1 $OPT" fi +# Check for dtrace/systemtap support +# On Linux, /usr/bin/dtrace is in fact a shim to SystemTap +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-dtrace" >&5 +$as_echo_n "checking for --with-dtrace... " >&6; } + +# Check whether --with-dtrace was given. +if test "${with_dtrace+set}" = set; then : + withval=$with_dtrace; +else + with_dtrace=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_dtrace" >&5 +$as_echo "$with_dtrace" >&6; } +if test ! -z "$with_dtrace" +then + if dtrace -G -o /dev/null -s $srcdir/Include/pydtrace.d 2>/dev/null + then + +$as_echo "#define WITH_DTRACE 1" >>confdefs.h + + with_dtrace="Sun" + DTRACEOBJS="Python/dtrace.o" + DTRADEHDRS="" + elif dtrace -h -o /dev/null -s $srcdir/Include/pydtrace.d + then + +$as_echo "#define WITH_DTRACE 1" >>confdefs.h + + with_dtrace="Apple" + DTRACEOBJS="" + DTRADEHDRS="pydtrace.h" + else + with_dtrace="no" + fi +else + with_dtrace="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_dtrace" >&5 +$as_echo "$with_dtrace" >&6; } + + + # -I${DLINCLDIR} is added to the compile rule for importdl.o DLINCLDIR=. @@ -14268,8 +14316,8 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. -config_files="`echo $ac_config_files`" -config_headers="`echo $ac_config_headers`" +config_files="$ac_config_files" +config_headers="$ac_config_headers" _ACEOF Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 85046) +++ Python/ceval.c (working copy) @@ -19,6 +19,10 @@ #include +#ifdef WITH_DTRACE +#include "pydtrace.h" +#endif + #ifndef WITH_TSC #define READ_TIMESTAMP(var) @@ -767,6 +771,70 @@ } +#ifdef WITH_DTRACE +struct frame_marker_info +{ + char *filename; + char *name; + int lineno; + + PyObject *utf8_filename; + PyObject *utf8_name; +}; + +static void +get_frame_marker_info(PyFrameObject *f, struct frame_marker_info *fmi) +{ + fmi->utf8_filename = PyUnicode_AsUTF8String(f->f_code->co_filename); + if (fmi->utf8_filename) { + fmi->filename = PyBytes_AsString(fmi->utf8_filename); + } else { + fmi->filename = NULL; + /* FIXME: clear the exception? */ + } + + fmi->utf8_name = PyUnicode_AsUTF8String(f->f_code->co_name); + if (fmi->utf8_name) { + fmi->name = PyBytes_AsString(fmi->utf8_name); + } else { + fmi->name = NULL; + /* FIXME: clear the exception? */ + } + + fmi->lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); +} + +static void +release_frame_marker_info(struct frame_marker_info *fmi) +{ + Py_XDECREF(fmi->utf8_filename); + Py_XDECREF(fmi->utf8_name); +} + +static void +dtrace_frame_entry(PyFrameObject *f) +{ + struct frame_marker_info fmi; + get_frame_marker_info(f, &fmi); + PYTHON_FRAME_ENTRY(fmi.filename, fmi.name, fmi.lineno); + release_frame_marker_info(&fmi); +} + +static void +dtrace_frame_exit(PyFrameObject *f) +{ + struct frame_marker_info fmi; + get_frame_marker_info(f, &fmi); + PYTHON_FRAME_EXIT(fmi.filename, fmi.name, fmi.lineno); + release_frame_marker_info(&fmi); +} +#else +#define PYTHON_FRAME_ENTRY_ENABLED() 0 +#define PYTHON_FRAME_EXIT_ENABLED() 0 +#define dtrace_frame_entry(f) +#define dtrace_frame_exit(f) +#endif + /* Interpreter main loop */ PyObject * @@ -1184,6 +1252,10 @@ } } + if (PYTHON_FRAME_ENTRY_ENABLED()) { + dtrace_frame_entry(f); + } + co = f->f_code; names = co->co_names; consts = co->co_consts; @@ -3045,6 +3117,9 @@ /* pop frame */ exit_eval_frame: + if (PYTHON_FRAME_EXIT_ENABLED()) { + dtrace_frame_exit(f); + } Py_LeaveRecursiveCall(); tstate->frame = f->f_back; Index: configure.in =================================================================== --- configure.in (revision 85046) +++ configure.in (working copy) @@ -2493,6 +2493,40 @@ OPT="-DDYNAMIC_ANNOTATIONS_ENABLED=1 $OPT" fi +# Check for dtrace/systemtap support +# On Linux, /usr/bin/dtrace is in fact a shim to SystemTap +AC_MSG_CHECKING([for --with-dtrace]) +AC_ARG_WITH([dtrace], + AC_HELP_STRING([--with(out)-dtrace], [disable/enable dtrace/SystemTap support]),, + with_dtrace=no) +AC_MSG_RESULT([$with_dtrace]) +if test ! -z "$with_dtrace" +then + if dtrace -G -o /dev/null -s $srcdir/Include/pydtrace.d 2>/dev/null + then + AC_DEFINE(WITH_DTRACE, 1, + [Define if you want to compile in Dtrace support]) + with_dtrace="Sun" + DTRACEOBJS="Python/dtrace.o" + DTRADEHDRS="" + elif dtrace -h -o /dev/null -s $srcdir/Include/pydtrace.d + then + AC_DEFINE(WITH_DTRACE, 1, + [Define if you want to compile in Dtrace support]) + with_dtrace="Apple" + DTRACEOBJS="" + DTRADEHDRS="pydtrace.h" + else + with_dtrace="no" + fi +else + with_dtrace="no" +fi + +AC_MSG_RESULT($with_dtrace) +AC_SUBST(DTRACEOBJS) +AC_SUBST(DTRACEHDRS) + # -I${DLINCLDIR} is added to the compile rule for importdl.o AC_SUBST(DLINCLDIR) DLINCLDIR=. Index: Lib/test/test_systemtap.py =================================================================== --- Lib/test/test_systemtap.py (revision 0) +++ Lib/test/test_systemtap.py (revision 0) @@ -0,0 +1,89 @@ +# Verify that systemtap static probes work +# +import subprocess +import sys +import unittest + +from test.support import run_unittest, findfile + +try: + _, stap_version = subprocess.Popen(["stap", "-V"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).communicate() +except OSError: + # This is what "no stap" looks like. There may, however, be other + # errors that manifest this way too. + raise unittest.SkipTest("Couldn't find stap on the path") + +def invoke_systemtap_script(script): + # Start a child python process, probing with the given systemtap script + # (passed as stdin to the "stap" tool) + # The script should be a bytes instance + # Return (stdout, stderr) pair + p = subprocess.Popen(["stap", "-", '-v', '-c', + '%s -c pass' % sys.executable], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = p.communicate(input=script) + return out, err + +# Verify that stap can run a simple "hello world"-style script +# This can fail for various reasons: +# - missing kernel headers +# - permissions (a non-root user needs to be in the "stapdev" group) + +out, err = invoke_systemtap_script(b'probe begin { println("hello world") exit () }') + +if out != b'hello world\n': + raise unittest.SkipTest("Test systemtap script did not run; stderr was: %s" % err) + +class SystemtapTests(unittest.TestCase): + + + def test_frame_entry(self): + script = ''' +probe process("%s").mark("frame__entry") { + filename = user_string($arg1); + funcname = user_string($arg2); + lineno = $arg3; + + printf("%%s => %%s in %%s:%%d\\n", thread_indent(1), funcname, filename, lineno); +} + +probe process("%s").mark("frame__exit") { + filename = user_string($arg1); + funcname = user_string($arg2); + lineno = $arg3; + + printf("%%s <= %%s in %%s:%%d\\n", thread_indent(-1), funcname, filename, lineno); +} +''' % (sys.executable, sys.executable) + #print(script) + out, err = invoke_systemtap_script(script.encode('utf-8')) + # stdout ought to contain lots of lines showing recursive frame entry + # and exit, of the form: + # Importing the site packages: + # 11408 python(8274): => __contains__ in Lib/_abcoll.py:362 + # 11414 python(8274): => __getitem__ in Lib/os.py:425 + # 11418 python(8274): => encode in Lib/os.py:490 + # 11424 python(8274): <= encode in Lib/os.py:493 + # 11428 python(8274): <= __getitem__ in Lib/os.py:426 + # 11433 python(8274): <= __contains__ in Lib/_abcoll.py:366 + # Executing the cmdline-supplied "pass": + # 0 python(8274): => in :1 + # 5 python(8274): <= in :1 + # + + # Uncomment this for debugging purposes: + # print(out.decode('utf-8')) + + self.assertIn(b'=> in :1', out, + msg="stdout: %s\nstderr: %s\n" % (out, err)) + +def test_main(): + run_unittest(SystemtapTests) + +if __name__ == "__main__": + test_main() Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (revision 85046) +++ Makefile.pre.in (working copy) @@ -326,6 +326,7 @@ Python/dtoa.o \ Python/formatter_unicode.o \ Python/$(DYNLOADFILE) \ + @DTRACEOBJS@ \ $(LIBOBJS) \ $(MACHDEP_OBJS) \ $(THREADOBJ) @@ -635,6 +636,18 @@ $(srcdir)/Objects/stringlib/formatter.h +# Only needed with --with-dtrace +buildinclude: + mkdir -p Include + +Include/pydtrace.h: buildinclude $(srcdir)/Include/pydtrace.d + dtrace -o $@ $(DFLAGS) -C -h -s $(srcdir)/Include/pydtrace.d + +Python/ceval.o: Include/pydtrace.h + +Python/dtrace.o: buildinclude $(srcdir)/Include/pydtrace.d Python/ceval.o + dtrace -o $@ $(DFLAGS) -C -G -s $(srcdir)/Include/pydtrace.d Python/ceval.o + ############################################################################ # Header files @@ -1284,7 +1297,7 @@ .PHONY: frameworkinstall frameworkinstallframework frameworkinstallstructure .PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools .PHONY: frameworkaltinstallunixtools recheck autoconf clean clobber distclean -.PHONY: smelly funny patchcheck +.PHONY: smelly funny patchcheck buildinclude .PHONY: gdbhooks # IF YOU PUT ANYTHING HERE IT WILL GO AWAY Index: pyconfig.h.in =================================================================== --- pyconfig.h.in (revision 85046) +++ pyconfig.h.in (working copy) @@ -30,7 +30,7 @@ /* Define if --enable-ipv6 is specified */ #undef ENABLE_IPV6 -/* Define if flock needs to be linked with bsd library */ +/* Define if flock needs to be linked with bsd library. */ #undef FLOCK_NEEDS_LIBBSD /* Define if getpgrp() must be called as getpgrp(0). */ @@ -217,7 +217,7 @@ /* Define to 1 if you have the `finite' function. */ #undef HAVE_FINITE -/* Define if you have the 'flock' function. */ +/* Define to 1 if you have the `flock' function. */ #undef HAVE_FLOCK /* Define to 1 if you have the `fork' function. */ @@ -1083,6 +1083,9 @@ /* Define if you want documentation strings in extension modules */ #undef WITH_DOC_STRINGS +/* Define if you want to compile in Dtrace support */ +#undef WITH_DTRACE + /* Define if you want to use the new-style (Openstep, Rhapsody, MacOS) dynamic linker (dyld) instead of the old-style (NextStep) dynamic linker (rld). Dyld is necessary to support frameworks. */