diff -ur Python-2.6.2.orig/configure.in Python-2.6.2/configure.in --- Python-2.6.2.orig/configure.in 2009-03-30 19:56:14.000000000 +0200 +++ Python-2.6.2/configure.in 2009-12-06 22:18:40.780379528 +0100 @@ -2383,6 +2383,38 @@ fi AC_MSG_RESULT($with_pymalloc) +# Check for dtrace support +AC_MSG_CHECKING(for --with-dtrace) +AC_ARG_WITH(dtrace, + AC_HELP_STRING(--with(out)-dtrace, disable/enable dtrace support)) + +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/phelper.o 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="phelper.h pydtrace.h" + else + with_dtrace="no" + fi +else + with_dtrace="no" +fi + +AC_MSG_RESULT($with_dtrace) +AC_SUBST(DTRACEOBJS) +AC_SUBST(DTRACEHDRS) + # Check for --with-wctype-functions AC_MSG_CHECKING(for --with-wctype-functions) AC_ARG_WITH(wctype-functions, diff -ur Python-2.6.2.orig/Include/frameobject.h Python-2.6.2/Include/frameobject.h --- Python-2.6.2.orig/Include/frameobject.h 2008-02-14 13:47:33.000000000 +0100 +++ Python-2.6.2/Include/frameobject.h 2009-12-06 20:28:48.727531540 +0100 @@ -41,6 +41,9 @@ /* As of 2.3 f_lineno is only valid when tracing is active (i.e. when f_trace is set) -- at other times use PyCode_Addr2Line instead. */ int f_lineno; /* Current line number */ +#ifdef WITH_DTRACE + int f_calllineno; /* line number of call site */ +#endif int f_iblock; /* index in f_blockstack */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ diff -ur Python-2.6.2.orig/Makefile.pre.in Python-2.6.2/Makefile.pre.in --- Python-2.6.2.orig/Makefile.pre.in 2009-02-24 12:07:44.000000000 +0100 +++ Python-2.6.2/Makefile.pre.in 2009-12-06 22:48:48.211630012 +0100 @@ -290,6 +290,7 @@ Python/formatter_unicode.o \ Python/formatter_string.o \ Python/$(DYNLOADFILE) \ + @DTRACEOBJS@ \ $(LIBOBJS) \ $(MACHDEP_OBJS) \ $(THREADOBJ) @@ -577,6 +578,23 @@ Python/formatter_string.o: $(srcdir)/Python/formatter_string.c \ $(STRINGLIB_HEADERS) +# Only needed with --with-dtrace +buildinclude: + mkdir -p Include +Python/phelper.o: buildinclude $(srcdir)/Include/phelper.d + dtrace -o $@ -DPHELPER $(DFLAGS) $(CPPFLAGS) -C -G -s $(srcdir)/Include/phelper.d + +Include/pydtrace.h: buildinclude $(srcdir)/Include/pydtrace.d + dtrace -o $@ $(DFLAGS) -C -h -s $(srcdir)/Include/pydtrace.d + +Include/phelper.h: buildinclude $(srcdir)/Include/phelper.d + dtrace -o $@ $(DFLAGS) -C -h -s $(srcdir)/Python/python.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 @@ -1212,6 +1230,6 @@ .PHONY: frameworkinstall frameworkinstallframework frameworkinstallstructure .PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools .PHONY: frameworkaltinstallunixtools recheck autoconf clean clobber distclean -.PHONY: smelly funny patchcheck +.PHONY: smelly funny patchcheck buildinclude # IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff -ur Python-2.6.2.orig/Objects/frameobject.c Python-2.6.2/Objects/frameobject.c --- Python-2.6.2.orig/Objects/frameobject.c 2009-03-18 22:49:29.000000000 +0100 +++ Python-2.6.2/Objects/frameobject.c 2009-12-06 20:28:48.728531275 +0100 @@ -703,6 +703,9 @@ f->f_tstate = tstate; f->f_lasti = -1; +#ifdef WITH_DTRACE + f->f_calllineno = code->co_firstlineno; +#endif f->f_lineno = code->co_firstlineno; f->f_iblock = 0; diff -ur Python-2.6.2.orig/Python/ceval.c Python-2.6.2/Python/ceval.c --- Python-2.6.2.orig/Python/ceval.c 2008-12-12 22:25:13.000000000 +0100 +++ Python-2.6.2/Python/ceval.c 2009-12-06 20:28:48.721531943 +0100 @@ -19,6 +19,10 @@ #include +#ifdef WITH_DTRACE +#include "pydtrace.h" +#endif + #ifndef WITH_TSC #define READ_TIMESTAMP(var) @@ -527,6 +531,55 @@ NULL); } +#ifdef WITH_DTRACE +static void +dtrace_entry(PyFrameObject *f) +{ + const char *filename; + const char *fname; + int lineno; + + filename = PyString_AsString(f->f_code->co_filename); + fname = PyString_AsString(f->f_code->co_name); + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); + + PYTHON_FUNCTION_ENTRY((char *)filename, (char *)fname, lineno); + + /* + * Currently a USDT tail-call will not receive the correct arguments. + * Disable the tail call here. + */ +#if defined(__sparc) + asm("nop"); +#endif +} + +static void +dtrace_return(PyFrameObject *f) +{ + const char *filename; + const char *fname; + int lineno; + + filename = PyString_AsString(f->f_code->co_filename); + fname = PyString_AsString(f->f_code->co_name); + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); + PYTHON_FUNCTION_RETURN((char *)filename, (char *)fname, lineno); + + /* + * Currently a USDT tail-call will not receive the correct arguments. + * Disable the tail call here. + */ +#if defined(__sparc) + asm("nop"); +#endif +} +#else +#define PYTHON_FUNCTION_ENTRY_ENABLED() 0 +#define PYTHON_FUNCTION_RETURN_ENABLED() 0 +#define dtrace_entry(f) +#define dtrace_return(f) +#endif /* Interpreter main loop */ @@ -538,9 +591,84 @@ return PyEval_EvalFrameEx(f, 0); } +/* + * These shenanigans look like utter madness, but what we're actually doing is + * making sure that the ustack helper will see the PyFrameObject pointer on the + * stack. We have two tricky cases: + * + * amd64 + * + * We use up the six registers for passing arguments, meaning the call can't + * use a register for passing 'f', and has to push it onto the stack in a known + * location. + * + * And how does "throwflag" figure in to this? -PN + * + * SPARC + * + * Here the problem is that (on 32-bit) the compiler is re-using %i0 before + * some calls inside PyEval_EvalFrameReal(), which means that when it's saved, + * it's just some junk value rather than the real first argument. So, instead, + * we trace our proxy PyEval_EvalFrame(), where we 'know' the compiler won't + * decide to re-use %i0. We also need to defeat optimization of our proxy. + */ + +#if defined(WITH_DTRACE) + +#if defined(__amd64) +PyObject *PyEval_EvalFrameExReal(long, long, long, long, long, long, + PyFrameObject *, int throwflag); + + + PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { + volatile PyObject *f2; + f2 = PyEval_EvalFrameExReal(0, 0, 0, 0, 0, 0, f, throwflag); + return (PyObject *)f2; +} + +PyObject * +PyEval_EvalFrameExReal(long a1, long a2, long a3, long a4, long a5, long a6, + PyFrameObject *f, int throwflag) +{ + +#elif defined(__sparc) + +PyObject *PyEval_EvalFrameExReal(PyFrameObject *f, int throwflag); + +volatile int dummy; + +PyObject * +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) +{ + volatile PyObject *f2; + f2 = PyEval_EvalFrameExReal(f, throwflag); + dummy = f->ob_refcnt; + return (PyObject *)f2; +} + +PyObject * +PyEval_EvalFrameExReal(PyFrameObject *f, int throwflag) +{ + +#else /* __amd64 || __sparc */ + +PyObject * +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) +{ + +#endif /* __amd64 || __sparc */ + +#else /* WITH_DTRACE not defined */ + +PyObject * +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) +{ + +#endif /* WITH_DTRACE */ + #ifdef DXPAIRS int lastopcode = 0; #endif @@ -763,6 +891,9 @@ } } + if (PYTHON_FUNCTION_ENTRY_ENABLED()) + dtrace_entry(f); + co = f->f_code; names = co->co_names; consts = co->co_consts; @@ -2383,6 +2514,10 @@ PyObject **sp; PCALL(PCALL_ALL); sp = stack_pointer; +#ifdef WITH_DTRACE + f->f_calllineno = PyCode_Addr2Line(f->f_code, + f->f_lasti); +#endif #ifdef WITH_TSC x = call_function(&sp, oparg, &intr0, &intr1); #else @@ -2425,6 +2560,11 @@ } else Py_INCREF(func); sp = stack_pointer; +#ifdef WITH_DTRACE + f->f_calllineno = PyCode_Addr2Line(f->f_code, + f->f_lasti); +#endif + READ_TIMESTAMP(intr0); x = ext_do_call(func, &sp, flags, na, nk); READ_TIMESTAMP(intr1); @@ -2723,6 +2863,8 @@ /* pop frame */ exit_eval_frame: + if (PYTHON_FUNCTION_RETURN_ENABLED()) + dtrace_return(f); Py_LeaveRecursiveCall(); tstate->frame = f->f_back; --- /dev/null 2009-12-05 20:12:56.645327655 +0100 +++ Python-2.6.2/Include/pydtrace.d 2009-12-06 20:28:48.726530967 +0100 @@ -0,0 +1,10 @@ +provider python { + probe function__entry(const char *, const char *, int); + probe function__return(const char *, const char *, int); +}; + +#pragma D attributes Evolving/Evolving/Common provider python provider +#pragma D attributes Private/Private/Common provider python module +#pragma D attributes Private/Private/Common provider python function +#pragma D attributes Evolving/Evolving/Common provider python name +#pragma D attributes Evolving/Evolving/Common provider python args --- /dev/null 2009-12-05 20:12:56.645327655 +0100 +++ Python-2.6.2/Include/phelper.d 2009-12-06 20:28:48.727531540 +0100 @@ -0,0 +1,139 @@ + +/* + * Python ustack helper. This relies on the first argument (PyFrame *) being + * on the stack; see Python/ceval.c for the contortions we go through to ensure + * this is the case. + * + * On x86, the PyFrame * is two slots up from the frame pointer; on SPARC, it's + * eight. + */ + +/* + * Yes, this is as gross as it looks. DTrace cannot handle static functions, + * and our stat_impl.h has them in ILP32. + */ +#define _SYS_STAT_H + +#include +#include + +#include "pyport.h" +#include "object.h" +#include "pystate.h" +#include "pyarena.h" +#include "pythonrun.h" +#include "compile.h" +#include "frameobject.h" +#include "stringobject.h" + +#if defined(__i386) +#define startframe PyEval_EvalFrameEx +#define endframe PyEval_EvalCodeEx +#elif defined(__amd64) +#define PyEval_EvalFrameEx PyEval_EvalFrameExReal +#define startframe PyEval_EvalFrameExReal +#define endframe PyEval_EvalCodeEx +#elif defined(__sparc) +#define PyEval_EvalFrameEx PyEval_EvalFrameExReal +#define startframe PyEval_EvalFrameEx +#define endframe PyEval_EvalFrameExReal +#endif + +#ifdef __sparcv9 +#define STACK_BIAS (2048-1) +#else +#define STACK_BIAS 0 +#endif + +/* + * Not defining PHELPER lets us test this code as a normal D script. + */ +#ifdef PHELPER + +#define at_evalframe(addr) \ + ((uintptr_t)addr >= ((uintptr_t)&``startframe) && \ + (uintptr_t)addr < ((uintptr_t)&``endframe)) +#define probe dtrace:helper:ustack: +#define print_result(r) (r) + +#if defined(__i386) || defined(__amd64) +#define frame_ptr_addr ((uintptr_t)arg1 + sizeof(uintptr_t) * 2) +#elif defined(__sparc) +#define frame_ptr_addr ((uintptr_t)arg1 + STACK_BIAS + sizeof(uintptr_t) * 8) +#else +#error unknown architecture +#endif + +#else /* PHELPER */ + +#define at_evalframe(addr) (1) +#define probe pid$target::PyEval_EvalFrame:entry +#define print_result(r) (trace(r)) + +#if defined(__i386) || defined(__amd64) +#define frame_ptr_addr ((uintptr_t)uregs[R_SP] + sizeof(uintptr_t)) +#elif defined(__sparc) +/* + * Not implemented: we could just use R_I0, but what's the point? + */ +#else +#error unknown architecture +#endif + +#endif /* PHELPER */ + +extern uintptr_t PyEval_EvalFrameEx; +extern uintptr_t PyEval_EvalCodeEx; + +#define copyin_obj(addr, obj) ((obj *)copyin((uintptr_t)addr, sizeof(obj))) +#define pystr_addr(addr) ((char *)addr + offsetof(PyStringObject, ob_sval)) +#define copyin_str(dest, addr, obj) \ + (copyinto((uintptr_t)pystr_addr(addr), obj->ob_size, (dest))) +#define add_str(addr, obj) \ + copyin_str(this->result + this->pos, addr, obj); \ + this->pos += obj->ob_size; \ + this->result[this->pos] = '\0'; +#define add_digit(nr, div) ((nr / div) ? \ + (this->result[this->pos++] = '0' + ((nr / div) % 10)) : \ + (this->result[this->pos] = '\0')) +#define add_char(c) (this->result[this->pos++] = c) + +probe /at_evalframe(arg0)/ +{ + this->framep = *(uintptr_t *)copyin(frame_ptr_addr, sizeof(uintptr_t)); + this->frameo = copyin_obj(this->framep, PyFrameObject); + this->codep = this->frameo->f_code; + this->lineno = this->frameo->f_calllineno; + this->codeo = copyin_obj(this->codep, PyCodeObject); + this->filenamep = this->codeo->co_filename; + this->fnamep = this->codeo->co_name; + this->filenameo = copyin_obj(this->filenamep, PyStringObject); + this->fnameo = copyin_obj(this->fnamep, PyStringObject); + + this->len = 1 + this->filenameo->ob_size + 1 + 5 + 2 + + this->fnameo->ob_size + 1 + 1; + + this->result = (char *)alloca(this->len); + this->pos = 0; + + add_char('@'); + add_str(this->filenamep, this->filenameo); + add_char(':'); + add_digit(this->lineno, 10000); + add_digit(this->lineno, 1000); + add_digit(this->lineno, 100); + add_digit(this->lineno, 10); + add_digit(this->lineno, 1); + add_char(' '); + add_char('('); + add_str(this->fnamep, this->fnameo); + add_char(')'); + this->result[this->pos] = '\0'; + + print_result(stringof(this->result)); +} + +probe /!at_evalframe(arg0)/ +{ + NULL; +}