components/python/python27/patches/05-dtrace.patch
author Rich Burridge <rich.burridge@oracle.com>
Tue, 02 May 2017 17:33:26 -0700
changeset 7964 d9801318ed3d
parent 7516 09c933391a1b
permissions -rw-r--r--
25981468 Build ilmbase and openexr with the GNU compilers

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.12/Makefile.pre.in.~1~	2016-06-25 14:49:31.000000000 -0700
+++ Python-2.7.12/Makefile.pre.in	2016-07-07 13:45:26.859336399 -0700
@@ -232,6 +232,7 @@
 # Used of signalmodule.o is not available
 SIGNAL_OBJS=	@SIGNAL_OBJS@
 
+DTRACE_OBJS=Python/dtrace.o Python/phelper.o
 
 ##########################################################################
 # Grammar
@@ -365,6 +366,7 @@
 		Python/formatter_unicode.o \
 		Python/formatter_string.o \
 		Python/$(DYNLOADFILE) \
+		$(DTRACE_OBJS) \
 		$(LIBOBJS) \
 		$(MACHDEP_OBJS) \
 		$(THREADOBJ)
@@ -751,6 +753,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
+	$(patsubst $(CEVAL_PATSUBST),,$(CC) -c -I. -IPython $(BASECFLAGS) $(EXTRA_CFLAGS) $(CPPFLAGS) $(CFLAGSFORSHARED) $(CEVAL_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
@@ -736,6 +736,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.11rc1/Python/ceval.c.~1~	2015-11-21 13:43:46.000000000 -0800
+++ Python-2.7.11rc1/Python/ceval.c	2015-12-03 13:42:17.344605237 -0800
@@ -19,6 +19,11 @@
 
 #include <ctype.h>
 
+#define HAVE_DTRACE
+#ifdef HAVE_DTRACE
+#include "python.h"
+#endif
+
 #ifndef WITH_TSC
 
 #define READ_TIMESTAMP(var)
@@ -674,6 +679,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 */
 
@@ -685,9 +739,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 DYNAMIC_EXECUTION_PROFILE
   #undef USE_COMPUTED_GOTOS
 #endif
@@ -1009,6 +1138,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;
@@ -2983,6 +3117,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
@@ -3024,6 +3161,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);
@@ -3333,6 +3473,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.10/Lib/test/test_sys.py.~1~	2015-05-10 10:17:04 -0800
+++ Python-2.7.10/Lib/test/test_sys.py	2015-05-18 14:35:06 -0800
@@ -635,7 +635,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'))
--- Python-2.7.12/Include/ceval.h.~1~	2016-06-25 14:49:30.000000000 -0700
+++ Python-2.7.12/Include/ceval.h	2016-10-12 07:33:22.132802000 -0700
@@ -64,6 +64,7 @@
 PyAPI_FUNC(PyObject *) PyEval_GetCallStats(PyObject *);
 PyAPI_FUNC(PyObject *) PyEval_EvalFrame(struct _frame *);
 PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx(struct _frame *f, int exc);
+#pragma	no_inline(PyEval_EvalFrameEx)
 
 /* this used to be handled on a per-thread basis - now just two globals */
 PyAPI_DATA(volatile int) _Py_Ticker;