python27: use absolute path to dtrace(8)
[unleashed-userland.git] / components / python / python27 / patches / 05-dtrace.patch
blob4404174f34e25a994cb3c87ee67c2345bcd71904
1 This patch adds Python dtrace support. Note it is necessary to modify
2 test_sys.py to add an integer to the frameobject structure size since this
3 patch adds "int f_calllineno" to the structure, so this test does not fail.
5 --- Python-2.7.6/Makefile.pre.in.~1~ 2013-11-09 23:36:41.000000000 -0800
6 +++ Python-2.7.6/Makefile.pre.in 2014-05-14 12:54:43.824219677 -0700
7 @@ -218,6 +218,7 @@
8 # Used of signalmodule.o is not available
9 SIGNAL_OBJS= @SIGNAL_OBJS@
11 +DTRACE_OBJS=Python/dtrace.o Python/phelper.o
13 ##########################################################################
14 # Grammar
15 @@ -341,6 +342,7 @@
16 Python/formatter_unicode.o \
17 Python/formatter_string.o \
18 Python/$(DYNLOADFILE) \
19 + $(DTRACE_OBJS) \
20 $(LIBOBJS) \
21 $(MACHDEP_OBJS) \
22 $(THREADOBJ)
23 @@ -664,6 +666,18 @@
24 Python/formatter_string.o: $(srcdir)/Python/formatter_string.c \
25 $(STRINGLIB_HEADERS)
27 +Python/phelper.o: $(srcdir)/Python/phelper.d
28 + /usr/sbin/dtrace -o $@ -DPHELPER $(DFLAGS) $(CPPFLAGS) -C -G -s $(srcdir)/Python/phelper.d
30 +Python/python.h: $(srcdir)/Python/python.d
31 + /usr/sbin/dtrace -o $@ $(DFLAGS) -C -h -s $(srcdir)/Python/python.d
33 +Python/ceval.o: Python/ceval.c Python/python.h
34 + $(CC) -c -I. -IPython $(BASECFLAGS) $(EXTRA_CFLAGS) $(CPPFLAGS) $(CFLAGSFORSHARED) $(CFLAGS) -DPy_BUILD_CORE -o $@ $<
36 +Python/dtrace.o: $(srcdir)/Python/python.d Python/ceval.o
37 + /usr/sbin/dtrace -o $@ $(DFLAGS) -C -G -s $(srcdir)/Python/python.d Python/ceval.o
39 ############################################################################
40 # Header files
42 --- Python-2.7.6/Include/frameobject.h.~1~ 2013-11-09 23:36:39.000000000 -0800
43 +++ Python-2.7.6/Include/frameobject.h 2014-05-14 13:03:19.938777249 -0700
44 @@ -44,6 +44,7 @@
45 PyCode_Addr2Line to calculate the line from the current
46 bytecode index. */
47 int f_lineno; /* Current line number */
48 + int f_calllineno; /* line number of call site */
49 int f_iblock; /* index in f_blockstack */
50 PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
51 PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
52 --- Python-2.7.6/Objects/frameobject.c.~1~ 2013-11-09 23:36:41.000000000 -0800
53 +++ Python-2.7.6/Objects/frameobject.c 2014-05-14 12:56:06.970076859 -0700
54 @@ -738,6 +738,7 @@
55 f->f_tstate = tstate;
57 f->f_lasti = -1;
58 + f->f_calllineno = code->co_firstlineno;
59 f->f_lineno = code->co_firstlineno;
60 f->f_iblock = 0;
62 --- Python-2.7.6/Python/ceval.c.~1~ 2013-11-09 23:36:41.000000000 -0800
63 +++ Python-2.7.6/Python/ceval.c 2014-05-14 13:04:01.334853206 -0700
64 @@ -19,6 +19,11 @@
66 #include <ctype.h>
68 +#define HAVE_DTRACE
69 +#ifdef HAVE_DTRACE
70 +#include "python.h"
71 +#endif
73 #ifndef WITH_TSC
75 #define READ_TIMESTAMP(var)
76 @@ -672,6 +677,55 @@
77 NULL);
80 +#ifdef HAVE_DTRACE
81 +static void
82 +dtrace_entry(PyFrameObject *f)
84 + const char *filename;
85 + const char *fname;
86 + int lineno;
88 + filename = PyString_AsString(f->f_code->co_filename);
89 + fname = PyString_AsString(f->f_code->co_name);
90 + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
92 + PYTHON_FUNCTION_ENTRY((char *)filename, (char *)fname, lineno);
94 + /*
95 + * Currently a USDT tail-call will not receive the correct arguments.
96 + * Disable the tail call here.
97 + */
98 +#if defined(__sparc)
99 + asm("nop");
100 +#endif
103 +static void
104 +dtrace_return(PyFrameObject *f)
106 + const char *filename;
107 + const char *fname;
108 + int lineno;
110 + filename = PyString_AsString(f->f_code->co_filename);
111 + fname = PyString_AsString(f->f_code->co_name);
112 + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
113 + PYTHON_FUNCTION_RETURN((char *)filename, (char *)fname, lineno);
115 + /*
116 + * Currently a USDT tail-call will not receive the correct arguments.
117 + * Disable the tail call here.
118 + */
119 +#if defined(__sparc)
120 + asm("nop");
121 +#endif
123 +#else
124 +#define PYTHON_FUNCTION_ENTRY_ENABLED 0
125 +#define PYTHON_FUNCTION_RETURN_ENABLED 0
126 +#define dtrace_entry()
127 +#define dtrace_return()
128 +#endif
130 /* Interpreter main loop */
132 @@ -683,9 +737,84 @@
133 return PyEval_EvalFrameEx(f, 0);
137 + * These shenanigans look like utter madness, but what we're actually doing is
138 + * making sure that the ustack helper will see the PyFrameObject pointer on the
139 + * stack. We have two tricky cases:
141 + * amd64
143 + * We use up the six registers for passing arguments, meaning the call can't
144 + * use a register for passing 'f', and has to push it onto the stack in a known
145 + * location.
147 + * And how does "throwflag" figure in to this? -PN
149 + * SPARC
151 + * Here the problem is that (on 32-bit) the compiler is re-using %i0 before
152 + * some calls inside PyEval_EvalFrameReal(), which means that when it's saved,
153 + * it's just some junk value rather than the real first argument. So, instead,
154 + * we trace our proxy PyEval_EvalFrame(), where we 'know' the compiler won't
155 + * decide to re-use %i0. We also need to defeat optimization of our proxy.
156 + */
158 +#if defined(HAVE_DTRACE)
160 +#if defined(__amd64)
161 +PyObject *PyEval_EvalFrameExReal(long, long, long, long, long, long,
162 + PyFrameObject *, int throwflag);
166 +PyObject *
167 +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
169 + volatile PyObject *f2;
170 + f2 = PyEval_EvalFrameExReal(0, 0, 0, 0, 0, 0, f, throwflag);
171 + return (PyObject *)f2;
174 +PyObject *
175 +PyEval_EvalFrameExReal(long a1, long a2, long a3, long a4, long a5, long a6,
176 + PyFrameObject *f, int throwflag)
179 +#elif defined(__sparc)
181 +PyObject *PyEval_EvalFrameExReal(PyFrameObject *f, int throwflag);
183 +volatile int dummy;
185 +PyObject *
186 +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
188 + volatile PyObject *f2;
189 + f2 = PyEval_EvalFrameExReal(f, throwflag);
190 + dummy = f->ob_refcnt;
191 + return (PyObject *)f2;
194 +PyObject *
195 +PyEval_EvalFrameExReal(PyFrameObject *f, int throwflag)
198 +#else /* __amd64 || __sparc */
200 PyObject *
201 PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
204 +#endif /* __amd64 || __sparc */
206 +#else /* don't HAVE_DTRACE */
208 +PyObject *
209 +PyEval_EvalFrameexEx(PyFrameObject *f, int throwflag))
212 +#endif /* HAVE_DTRACE */
214 #ifdef DYNAMIC_EXECUTION_PROFILE
215 #undef USE_COMPUTED_GOTOS
216 #endif
217 @@ -910,6 +1039,11 @@
221 +#ifdef HAVE_DTRACE
222 + if (PYTHON_FUNCTION_ENTRY_ENABLED())
223 + dtrace_entry(f);
224 +#endif
226 co = f->f_code;
227 names = co->co_names;
228 consts = co->co_consts;
229 @@ -2660,6 +2794,9 @@
230 PyObject **sp;
231 PCALL(PCALL_ALL);
232 sp = stack_pointer;
233 +#ifdef HAVE_DTRACE
234 + f->f_calllineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
235 +#endif
236 #ifdef WITH_TSC
237 x = call_function(&sp, oparg, &intr0, &intr1);
238 #else
239 @@ -2701,6 +2838,9 @@
240 } else
241 Py_INCREF(func);
242 sp = stack_pointer;
243 +#ifdef HAVE_DTRACE
244 + f->f_calllineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
245 +#endif
246 READ_TIMESTAMP(intr0);
247 x = ext_do_call(func, &sp, flags, na, nk);
248 READ_TIMESTAMP(intr1);
249 @@ -3001,6 +3141,10 @@
251 /* pop frame */
252 exit_eval_frame:
253 +#ifdef HAVE_DTRACE
254 + if (PYTHON_FUNCTION_RETURN_ENABLED())
255 + dtrace_return(f);
256 +#endif
257 Py_LeaveRecursiveCall();
258 tstate->frame = f->f_back;
260 --- /dev/null
261 +++ Python-2.6.4/Python/phelper.d
262 @@ -0,0 +1,151 @@
265 + * Python ustack helper. This relies on the first argument (PyFrame *) being
266 + * on the stack; see Python/ceval.c for the contortions we go through to ensure
267 + * this is the case.
269 + * On x86, the PyFrame * is two slots up from the frame pointer; on SPARC, it's
270 + * eight.
271 + */
274 + * Yes, this is as gross as it looks. DTrace cannot handle static functions,
275 + * and our stat_impl.h has them in ILP32.
276 + */
277 +#define _SYS_STAT_H
278 + /*
279 + * DTrace also has a problem with gcc's stdarg.h (since it refers to the type
280 + * __gnuc_va_list), so we use this hack to avoid including it and
281 + * sys/va_list.h. We'd like to use a typedef here for the va_list and __va_list
282 + * types, but it seems like there's a bug in the DTrace interpreter where cpp
283 + * linemarkers cause a typedef to not be effective in other included files
284 + * denoted by other linemarkers, so we have to resort to #define...
285 + */
286 +#define _STDARG_H
287 +#define _SYS_VA_LIST_H
288 +#define __va_list void*
289 +#define va_list void*
291 +#include <stdio.h>
292 +#include <sys/types.h>
294 +#include "pyport.h"
295 +#include "object.h"
296 +#include "pystate.h"
297 +#include "pyarena.h"
298 +#include "pythonrun.h"
299 +#include "compile.h"
300 +#include "frameobject.h"
301 +#include "stringobject.h"
303 +#if defined(__i386)
304 +#define startframe PyEval_EvalFrameEx
305 +#define endframe PyEval_EvalCodeEx
306 +#elif defined(__amd64)
307 +#define PyEval_EvalFrameEx PyEval_EvalFrameExReal
308 +#define startframe PyEval_EvalFrameExReal
309 +#define endframe PyEval_EvalCodeEx
310 +#elif defined(__sparc)
311 +#define PyEval_EvalFrameEx PyEval_EvalFrameExReal
312 +#define startframe PyEval_EvalFrameEx
313 +#define endframe PyEval_EvalFrameExReal
314 +#endif
316 +#ifdef __sparcv9
317 +#define STACK_BIAS (2048-1)
318 +#else
319 +#define STACK_BIAS 0
320 +#endif
323 + * Not defining PHELPER lets us test this code as a normal D script.
324 + */
325 +#ifdef PHELPER
327 +#define at_evalframe(addr) \
328 + ((uintptr_t)addr >= ((uintptr_t)&``startframe) && \
329 + (uintptr_t)addr < ((uintptr_t)&``endframe))
330 +#define probe dtrace:helper:ustack:
331 +#define print_result(r) (r)
333 +#if defined(__i386) || defined(__amd64)
334 +#define frame_ptr_addr ((uintptr_t)arg1 + sizeof(uintptr_t) * 2)
335 +#elif defined(__sparc)
336 +#define frame_ptr_addr ((uintptr_t)arg1 + STACK_BIAS + sizeof(uintptr_t) * 8)
337 +#else
338 +#error unknown architecture
339 +#endif
341 +#else /* PHELPER */
343 +#define at_evalframe(addr) (1)
344 +#define probe pid$target::PyEval_EvalFrame:entry
345 +#define print_result(r) (trace(r))
347 +#if defined(__i386) || defined(__amd64)
348 +#define frame_ptr_addr ((uintptr_t)uregs[R_SP] + sizeof(uintptr_t))
349 +#elif defined(__sparc)
351 + * Not implemented: we could just use R_I0, but what's the point?
352 + */
353 +#else
354 +#error unknown architecture
355 +#endif
357 +#endif /* PHELPER */
359 +extern uintptr_t PyEval_EvalFrameEx;
360 +extern uintptr_t PyEval_EvalCodeEx;
362 +#define copyin_obj(addr, obj) ((obj *)copyin((uintptr_t)addr, sizeof(obj)))
363 +#define pystr_addr(addr) ((char *)addr + offsetof(PyStringObject, ob_sval))
364 +#define copyin_str(dest, addr, obj) \
365 + (copyinto((uintptr_t)pystr_addr(addr), obj->ob_size, (dest)))
366 +#define add_str(addr, obj) \
367 + copyin_str(this->result + this->pos, addr, obj); \
368 + this->pos += obj->ob_size; \
369 + this->result[this->pos] = '\0';
370 +#define add_digit(nr, div) ((nr / div) ? \
371 + (this->result[this->pos++] = '0' + ((nr / div) % 10)) : \
372 + (this->result[this->pos] = '\0'))
373 +#define add_char(c) (this->result[this->pos++] = c)
375 +probe /at_evalframe(arg0)/
377 + this->framep = *(uintptr_t *)copyin(frame_ptr_addr, sizeof(uintptr_t));
378 + this->frameo = copyin_obj(this->framep, PyFrameObject);
379 + this->codep = this->frameo->f_code;
380 + this->lineno = this->frameo->f_calllineno;
381 + this->codeo = copyin_obj(this->codep, PyCodeObject);
382 + this->filenamep = this->codeo->co_filename;
383 + this->fnamep = this->codeo->co_name;
384 + this->filenameo = copyin_obj(this->filenamep, PyStringObject);
385 + this->fnameo = copyin_obj(this->fnamep, PyStringObject);
387 + this->len = 1 + this->filenameo->ob_size + 1 + 5 + 2 +
388 + this->fnameo->ob_size + 1 + 1;
390 + this->result = (char *)alloca(this->len);
391 + this->pos = 0;
393 + add_char('@');
394 + add_str(this->filenamep, this->filenameo);
395 + add_char(':');
396 + add_digit(this->lineno, 10000);
397 + add_digit(this->lineno, 1000);
398 + add_digit(this->lineno, 100);
399 + add_digit(this->lineno, 10);
400 + add_digit(this->lineno, 1);
401 + add_char(' ');
402 + add_char('(');
403 + add_str(this->fnamep, this->fnameo);
404 + add_char(')');
405 + this->result[this->pos] = '\0';
407 + print_result(stringof(this->result));
410 +probe /!at_evalframe(arg0)/
412 + NULL;
414 diff --git Python-2.6.4/Python/python.d Python-2.6.4/Python/python.d
415 new file mode 100644
416 --- /dev/null
417 +++ Python-2.6.4/Python/python.d
418 @@ -0,0 +1,10 @@
419 +provider python {
420 + probe function__entry(const char *, const char *, int);
421 + probe function__return(const char *, const char *, int);
424 +#pragma D attributes Evolving/Evolving/Common provider python provider
425 +#pragma D attributes Private/Private/Common provider python module
426 +#pragma D attributes Private/Private/Common provider python function
427 +#pragma D attributes Evolving/Evolving/Common provider python name
428 +#pragma D attributes Evolving/Evolving/Common provider python args
429 --- Python-2.7.6/Lib/test/test_sys.py.~1~ 2013-11-09 23:36:40.000000000 -0800
430 +++ Python-2.7.6/Lib/test/test_sys.py 2014-05-14 13:07:05.332748121 -0700
431 @@ -612,7 +612,7 @@
432 nfrees = len(x.f_code.co_freevars)
433 extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
434 ncells + nfrees - 1
435 - check(x, vsize('12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
436 + check(x, vsize('12P4i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
437 # function
438 def func(): pass
439 check(func, size('9P'))