components/python/python27/patches/05-dtrace.patch
author John Beck <John.Beck@Oracle.COM>
Mon, 06 Oct 2014 13:15:36 -0700
branchs11u2-sru
changeset 3379 e99da14b537a
parent 458 2edc011b559e
child 3565 2d729d36ded7
permissions -rw-r--r--
PSARC 2014/183 Python 2.7.6 18251953 update Python 2.7 line to version 2.7.6 19004605 update Python 2.7 line to version 2.7.7 19308541 update Python 2.7 line to version 2.7.8 19284990 python 2.7.7 segfaults while under memory stress 17431625 64-bit python should use long rather than int for os.sysconf() return value 19164544 Python 2.7 test_tcl fails 19030238 Python 2.7 test_sysconfig fails - no module named _osx_support 19030198 Python 2.7 tests fail - import name error 19032456 more Python 2.7 tests failing with import errors 19022543 Python 2.7 test_lib2to3 fails

This patch adds Python dtrace support.  Note it is necessary to modify
test_sys.py to add an integer to the frameobject structure size since this
patch adds "int f_calllineno" to the structure, so this test does not fail.
The patch comes from upstream:
http://www.jcea.es/artic/python_dtrace-2_7_7_af8ecf2352e1.txt
http://www.jcea.es/artic/python_dtrace.htm
Follow http://bugs.python.org/issue13405 for plans to get it
integrated into the main tree.

--- Python-2.7.6/Makefile.pre.in.~1~	2013-11-09 23:36:41.000000000 -0800
+++ Python-2.7.6/Makefile.pre.in	2014-05-14 12:54:43.824219677 -0700
@@ -218,6 +218,7 @@
 # Used of signalmodule.o is not available
 SIGNAL_OBJS=	@SIGNAL_OBJS@
 
+DTRACE_OBJS=Python/dtrace.o Python/phelper.o
 
 ##########################################################################
 # Grammar
@@ -341,6 +342,7 @@
 		Python/formatter_unicode.o \
 		Python/formatter_string.o \
 		Python/$(DYNLOADFILE) \
+		$(DTRACE_OBJS) \
 		$(LIBOBJS) \
 		$(MACHDEP_OBJS) \
 		$(THREADOBJ)
@@ -664,6 +666,18 @@
 Python/formatter_string.o: $(srcdir)/Python/formatter_string.c \
 				$(STRINGLIB_HEADERS)
 
+Python/phelper.o: $(srcdir)/Python/phelper.d
+	dtrace -o $@ -DPHELPER $(DFLAGS) $(CPPFLAGS) -C -G -s $(srcdir)/Python/phelper.d
+
+Python/python.h: $(srcdir)/Python/python.d
+	dtrace -o $@ $(DFLAGS) -C -h -s $(srcdir)/Python/python.d
+
+Python/ceval.o: Python/ceval.c Python/python.h
+	$(CC) -c -I. -IPython $(BASECFLAGS) $(EXTRA_CFLAGS) $(CPPFLAGS) $(CFLAGSFORSHARED) $(CFLAGS) -DPy_BUILD_CORE -o $@ $<
+
+Python/dtrace.o: $(srcdir)/Python/python.d Python/ceval.o
+	dtrace -o $@ $(DFLAGS) -C -G -s $(srcdir)/Python/python.d Python/ceval.o
+
 ############################################################################
 # Header files
 
--- Python-2.7.6/Include/frameobject.h.~1~	2013-11-09 23:36:39.000000000 -0800
+++ Python-2.7.6/Include/frameobject.h	2014-05-14 13:03:19.938777249 -0700
@@ -44,6 +44,7 @@
        PyCode_Addr2Line to calculate the line from the current
        bytecode index. */
     int f_lineno;		/* Current line number */
+    int f_calllineno;		/* line number of call site */
     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 */
--- Python-2.7.6/Objects/frameobject.c.~1~	2013-11-09 23:36:41.000000000 -0800
+++ Python-2.7.6/Objects/frameobject.c	2014-05-14 12:56:06.970076859 -0700
@@ -738,6 +738,7 @@
     f->f_tstate = tstate;
 
     f->f_lasti = -1;
+    f->f_calllineno = code->co_firstlineno;
     f->f_lineno = code->co_firstlineno;
     f->f_iblock = 0;
 
--- Python-2.7.8/Python/ceval.c.~1~	2014-06-29 19:05:46.000000000 -0700
+++ Python-2.7.8/Python/ceval.c	2014-07-17 20:39:49.843868592 -0700
@@ -19,6 +19,11 @@
 
 #include <ctype.h>
 
+#define HAVE_DTRACE
+#ifdef HAVE_DTRACE
+#include "python.h"
+#endif
+
 #ifndef WITH_TSC
 
 #define READ_TIMESTAMP(var)
@@ -678,6 +683,55 @@
                       NULL);
 }
 
+#ifdef HAVE_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()
+#define	dtrace_return()
+#endif
 
 /* Interpreter main loop */
 
@@ -689,9 +743,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(HAVE_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 /* don't HAVE_DTRACE */
+
+PyObject *
+PyEval_EvalFrameexEx(PyFrameObject *f, int throwflag))
+{
+
+#endif /* HAVE_DTRACE */
+
 #ifdef DXPAIRS
     int lastopcode = 0;
 #endif
@@ -916,6 +1045,11 @@
         }
     }
 
+#ifdef HAVE_DTRACE
+    if (PYTHON_FUNCTION_ENTRY_ENABLED())
+        dtrace_entry(f);
+#endif
+
     co = f->f_code;
     names = co->co_names;
     consts = co->co_consts;
@@ -2673,6 +2807,9 @@
             PyObject **sp;
             PCALL(PCALL_ALL);
             sp = stack_pointer;
+#ifdef HAVE_DTRACE
+            f->f_calllineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
+#endif
 #ifdef WITH_TSC
             x = call_function(&sp, oparg, &intr0, &intr1);
 #else
@@ -2714,6 +2851,9 @@
             } else
                 Py_INCREF(func);
             sp = stack_pointer;
+#ifdef HAVE_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);
@@ -3014,6 +3154,10 @@
 
     /* pop frame */
 exit_eval_frame:
+#ifdef HAVE_DTRACE
+    if (PYTHON_FUNCTION_RETURN_ENABLED())
+        dtrace_return(f);
+#endif
     Py_LeaveRecursiveCall();
     tstate->frame = f->f_back;
 
--- /dev/null
+++ Python-2.6.4/Python/phelper.d
@@ -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 <stdio.h>
+#include <sys/types.h>
+
+#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;
+}
diff --git Python-2.6.4/Python/python.d Python-2.6.4/Python/python.d
new file mode 100644
--- /dev/null
+++ Python-2.6.4/Python/python.d
@@ -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
--- Python-2.7.7/Lib/test/test_sys.py.~1~	2014-05-31 11:58:39.000000000 -0700
+++ Python-2.7.7/Lib/test/test_sys.py	2014-06-02 10:44:39.318709539 -0700
@@ -594,7 +594,7 @@
         nfrees = len(x.f_code.co_freevars)
         extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
                  ncells + nfrees - 1
-        check(x, vsize('12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
+        check(x, vsize('12P4i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
         # function
         def func(): pass
         check(func, size('9P'))