Skip to content

Commit c69ebe8

Browse files
committed
Enable the profiling of C functions (builtins and extensions)
1 parent a1dde13 commit c69ebe8

File tree

10 files changed

+145
-15
lines changed

10 files changed

+145
-15
lines changed

Doc/api/init.tex

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,16 +726,21 @@ \section{Profiling and Tracing \label{profiling}}
726726
The first parameter is the object passed to the registration
727727
function as \var{obj}, \var{frame} is the frame object to which the
728728
event pertains, \var{what} is one of the constants
729-
\constant{PyTrace_CALL}, \constant{PyTrace_EXCEPT},
730-
\constant{PyTrace_LINE} or \constant{PyTrace_RETURN}, and \var{arg}
729+
\constant{PyTrace_CALL}, \constant{PyTrace_EXCEPTION},
730+
\constant{PyTrace_LINE}, \constant{PyTrace_RETURN},
731+
\constant{PyTrace_C_CALL}, \constant{PyTrace_C_EXCEPTION},
732+
or \constant{PyTrace_C_RETURN}, and \var{arg}
731733
depends on the value of \var{what}:
732734

733735
\begin{tableii}{l|l}{constant}{Value of \var{what}}{Meaning of \var{arg}}
734736
\lineii{PyTrace_CALL}{Always \NULL.}
735-
\lineii{PyTrace_EXCEPT}{Exception information as returned by
737+
\lineii{PyTrace_EXCEPTION}{Exception information as returned by
736738
\function{sys.exc_info()}.}
737739
\lineii{PyTrace_LINE}{Always \NULL.}
738740
\lineii{PyTrace_RETURN}{Value being returned to the caller.}
741+
\lineii{PyTrace_C_CALL}{Name of function being called.}
742+
\lineii{PyTrace_C_EXCEPTION}{Always \NULL.}
743+
\lineii{PyTrace_C_RETURN}{Always \NULL.}
739744
\end{tableii}
740745
\end{ctypedesc}
741746

@@ -747,7 +752,7 @@ \section{Profiling and Tracing \label{profiling}}
747752
control transfer to the Python bytecode in the corresponding frame.
748753
\end{cvardesc}
749754

750-
\begin{cvardesc}{int}{PyTrace_EXCEPT}
755+
\begin{cvardesc}{int}{PyTrace_EXCEPTION}
751756
The value of the \var{what} parameter to a \ctype{Py_tracefunc}
752757
function when an exception has been raised. The callback function
753758
is called with this value for \var{what} when after any bytecode is
@@ -770,6 +775,21 @@ \section{Profiling and Tracing \label{profiling}}
770775
functions when a call is returning without propagating an exception.
771776
\end{cvardesc}
772777

778+
\begin{cvardesc}{int}{PyTrace_C_CALL}
779+
The value for the \var{what} parameter to \ctype{Py_tracefunc}
780+
functions when a C function is about to be called.
781+
\end{cvardesc}
782+
783+
\begin{cvardesc}{int}{PyTrace_C_EXCEPTION}
784+
The value for the \var{what} parameter to \ctype{Py_tracefunc}
785+
functions when a C function has thrown an exception.
786+
\end{cvardesc}
787+
788+
\begin{cvardesc}{int}{PyTrace_C_RETURN}
789+
The value for the \var{what} parameter to \ctype{Py_tracefunc}
790+
functions when a C function has returned.
791+
\end{cvardesc}
792+
773793
\begin{cfuncdesc}{void}{PyEval_SetProfile}{Py_tracefunc func, PyObject *obj}
774794
Set the profiler function to \var{func}. The \var{obj} parameter is
775795
passed to the function as its first parameter, and may be any Python

Doc/lib/libpdb.tex

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,9 @@ \section{How It Works \label{debugger-hooks}}
350350

351351
Trace functions have three arguments: \var{frame}, \var{event}, and
352352
\var{arg}. \var{frame} is the current stack frame. \var{event} is a
353-
string: \code{'call'}, \code{'line'}, \code{'return'} or
354-
\code{'exception'}. \var{arg} depends on the event type.
353+
string: \code{'call'}, \code{'line'}, \code{'return'}, \code{'exception'},
354+
\code{'c_call'}, \code{'c_return'}, or \code{'c_exception'}. \var{arg}
355+
depends on the event type.
355356

356357
The global trace function is invoked (with \var{event} set to
357358
\code{'call'}) whenever a new local scope is entered; it should return
@@ -390,6 +391,16 @@ \section{How It Works \label{debugger-hooks}}
390391
\var{traceback})}; the return value specifies the new local trace
391392
function.
392393

394+
\item[\code{'c_call'}]
395+
A C function is about to be called. This may be an extension function
396+
or a builtin. \var{arg} is the C function name.
397+
398+
\item[\code{'c_return'}]
399+
A C function has returned. \var{arg} is \code{None}.
400+
401+
\item[\code{'c_exception'}]
402+
A C function has thrown an exception. \var{arg} is \code{None}.
403+
393404
\end{description}
394405

395406
Note that as an exception is propagated down the chain of callers, an

Include/pystate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *);
4545
#define PyTrace_EXCEPTION 1
4646
#define PyTrace_LINE 2
4747
#define PyTrace_RETURN 3
48+
#define PyTrace_C_CALL 4
49+
#define PyTrace_C_EXCEPTION 5
50+
#define PyTrace_C_RETURN 6
4851

4952
typedef struct _ts {
5053

Lib/bdb.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ def trace_dispatch(self, frame, event, arg):
5252
return self.dispatch_return(frame, arg)
5353
if event == 'exception':
5454
return self.dispatch_exception(frame, arg)
55+
if event == 'c_call':
56+
return self.trace_dispatch
57+
if event == 'c_exception':
58+
return self.trace_dispatch
59+
if event == 'c_return':
60+
return self.trace_dispatch
5561
print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event)
5662
return self.trace_dispatch
5763

Lib/profile.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def __init__(self, timer=None, bias=None):
163163
self.timings = {}
164164
self.cur = None
165165
self.cmd = ""
166+
self.c_func_name = ""
166167

167168
if bias is None:
168169
bias = self.bias
@@ -214,6 +215,9 @@ def trace_dispatch(self, frame, event, arg):
214215
t = timer()
215216
t = t[0] + t[1] - self.t - self.bias
216217

218+
if event == "c_call":
219+
self.c_func_name = arg
220+
217221
if self.dispatch[event](self, frame,t):
218222
t = timer()
219223
self.t = t[0] + t[1]
@@ -227,7 +231,11 @@ def trace_dispatch(self, frame, event, arg):
227231
def trace_dispatch_i(self, frame, event, arg):
228232
timer = self.timer
229233
t = timer() - self.t - self.bias
230-
if self.dispatch[event](self, frame,t):
234+
235+
if event == "c_call":
236+
self.c_func_name = arg
237+
238+
if self.dispatch[event](self, frame, t):
231239
self.t = timer()
232240
else:
233241
self.t = timer() - t # put back unrecorded delta
@@ -238,6 +246,10 @@ def trace_dispatch_i(self, frame, event, arg):
238246
def trace_dispatch_mac(self, frame, event, arg):
239247
timer = self.timer
240248
t = timer()/60.0 - self.t - self.bias
249+
250+
if event == "c_call":
251+
self.c_func_name = arg
252+
241253
if self.dispatch[event](self, frame, t):
242254
self.t = timer()/60.0
243255
else:
@@ -249,6 +261,9 @@ def trace_dispatch_l(self, frame, event, arg):
249261
get_time = self.get_time
250262
t = get_time() - self.t - self.bias
251263

264+
if event == "c_call":
265+
self.c_func_name = arg
266+
252267
if self.dispatch[event](self, frame, t):
253268
self.t = get_time()
254269
else:
@@ -291,6 +306,17 @@ def trace_dispatch_call(self, frame, t):
291306
timings[fn] = 0, 0, 0, 0, {}
292307
return 1
293308

309+
def trace_dispatch_c_call (self, frame, t):
310+
fn = ("", 0, self.c_func_name)
311+
self.cur = (t, 0, 0, fn, frame, self.cur)
312+
timings = self.timings
313+
if timings.has_key(fn):
314+
cc, ns, tt, ct, callers = timings[fn]
315+
timings[fn] = cc, ns+1, tt, ct, callers
316+
else:
317+
timings[fn] = 0, 0, 0, 0, {}
318+
return 1
319+
294320
def trace_dispatch_return(self, frame, t):
295321
if frame is not self.cur[-2]:
296322
assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3])
@@ -333,6 +359,9 @@ def trace_dispatch_return(self, frame, t):
333359
"call": trace_dispatch_call,
334360
"exception": trace_dispatch_exception,
335361
"return": trace_dispatch_return,
362+
"c_call": trace_dispatch_c_call,
363+
"c_exception": trace_dispatch_exception,
364+
"c_return": trace_dispatch_return,
336365
}
337366

338367

Lib/test/output/test_profile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
test_profile
2-
53 function calls in 1.000 CPU seconds
2+
74 function calls in 1.000 CPU seconds
33

44
Ordered by: standard name
55

66
ncalls tottime percall cumtime percall filename:lineno(function)
7+
12 0.000 0.000 0.012 0.001 :0(hasattr)
8+
8 0.000 0.000 0.000 0.000 :0(range)
9+
1 0.000 0.000 0.000 0.000 :0(setprofile)
710
1 0.000 0.000 1.000 1.000 <string>:1(?)
811
0 0.000 0.000 profile:0(profiler)
912
1 0.000 0.000 1.000 1.000 profile:0(testfunc())

Lib/test/test_profilehooks.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ def __init__(self):
1111
self.events = []
1212

1313
def callback(self, frame, event, arg):
14-
self.add_event(event, frame)
14+
if (event == "call"
15+
or event == "return"
16+
or event == "exception"):
17+
self.add_event(event, frame)
1518

1619
def add_event(self, event, frame=None):
1720
"""Add an event to the log."""
@@ -56,10 +59,16 @@ def trace_exception(self, frame):
5659
self.testcase.fail(
5760
"the profiler should never receive exception events")
5861

62+
def trace_pass(self, frame):
63+
pass
64+
5965
dispatch = {
6066
'call': trace_call,
6167
'exception': trace_exception,
6268
'return': trace_return,
69+
'c_call': trace_pass,
70+
'c_return': trace_pass,
71+
'c_exception': trace_pass,
6372
}
6473

6574

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 2.4 alpha 1?
1212
Core and builtins
1313
-----------------
1414

15+
- Enabled the profiling of C extension functions (and builtins) - check
16+
new documentation and modified profiler and bdb modules for more details
17+
1518
- Set file.name to the object passed to open (instead of a new string)
1619

1720
- Moved tracebackobject into traceback.h and renamed to PyTracebackObject

Python/ceval.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,9 +536,7 @@ _Py_CheckRecursiveCall(char *where)
536536
return 0;
537537
}
538538

539-
540539
/* Status code for main loop (reason for stack unwind) */
541-
542540
enum why_code {
543541
WHY_NOT, /* No error */
544542
WHY_EXCEPTION, /* Exception occurred */
@@ -3425,6 +3423,42 @@ err_args(PyObject *func, int flags, int nargs)
34253423
nargs);
34263424
}
34273425

3426+
#define BEGIN_C_TRACE \
3427+
if (tstate->use_tracing) { \
3428+
if (tstate->c_profilefunc != NULL) { \
3429+
PyObject *func_name = \
3430+
PyString_FromString (((PyCFunctionObject *) \
3431+
func)->m_ml->ml_name); \
3432+
are_tracing = 1; \
3433+
if (call_trace(tstate->c_profilefunc, \
3434+
tstate->c_profileobj, \
3435+
tstate->frame, PyTrace_C_CALL, \
3436+
func_name)) \
3437+
{ return NULL; } \
3438+
Py_DECREF (func_name); \
3439+
} \
3440+
}
3441+
3442+
#define END_C_TRACE \
3443+
if (tstate->use_tracing && are_tracing) { \
3444+
if (tstate->c_profilefunc != NULL) { \
3445+
if (x == NULL) { \
3446+
if (call_trace (tstate->c_profilefunc, \
3447+
tstate->c_profileobj, \
3448+
tstate->frame, PyTrace_C_EXCEPTION, \
3449+
NULL)) \
3450+
{ return NULL; } \
3451+
} else { \
3452+
if (call_trace(tstate->c_profilefunc, \
3453+
tstate->c_profileobj, \
3454+
tstate->frame, PyTrace_C_RETURN, \
3455+
NULL)) \
3456+
{ return NULL; } \
3457+
} \
3458+
} \
3459+
}
3460+
3461+
34283462
static PyObject *
34293463
call_function(PyObject ***pp_stack, int oparg)
34303464
{
@@ -3435,6 +3469,10 @@ call_function(PyObject ***pp_stack, int oparg)
34353469
PyObject *func = *pfunc;
34363470
PyObject *x, *w;
34373471

3472+
int are_tracing = 0;
3473+
3474+
PyThreadState *tstate = PyThreadState_GET();
3475+
34383476
/* Always dispatch PyCFunction first, because these are
34393477
presumed to be the most frequent callable object.
34403478
*/
@@ -3444,11 +3482,16 @@ call_function(PyObject ***pp_stack, int oparg)
34443482
if (flags & (METH_NOARGS | METH_O)) {
34453483
PyCFunction meth = PyCFunction_GET_FUNCTION(func);
34463484
PyObject *self = PyCFunction_GET_SELF(func);
3447-
if (flags & METH_NOARGS && na == 0)
3485+
if (flags & METH_NOARGS && na == 0) {
3486+
BEGIN_C_TRACE
34483487
x = (*meth)(self, NULL);
3488+
END_C_TRACE
3489+
}
34493490
else if (flags & METH_O && na == 1) {
34503491
PyObject *arg = EXT_POP(*pp_stack);
3492+
BEGIN_C_TRACE
34513493
x = (*meth)(self, arg);
3494+
END_C_TRACE
34523495
Py_DECREF(arg);
34533496
}
34543497
else {
@@ -3459,7 +3502,9 @@ call_function(PyObject ***pp_stack, int oparg)
34593502
else {
34603503
PyObject *callargs;
34613504
callargs = load_args(pp_stack, na);
3505+
BEGIN_C_TRACE
34623506
x = PyCFunction_Call(func, callargs, NULL);
3507+
END_C_TRACE
34633508
Py_XDECREF(callargs);
34643509
}
34653510
} else {

Python/sysmodule.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,15 +272,16 @@ operating system filenames."
272272
* Cached interned string objects used for calling the profile and
273273
* trace functions. Initialized by trace_init().
274274
*/
275-
static PyObject *whatstrings[4] = {NULL, NULL, NULL, NULL};
275+
static PyObject *whatstrings[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
276276

277277
static int
278278
trace_init(void)
279279
{
280-
static char *whatnames[4] = {"call", "exception", "line", "return"};
280+
static char *whatnames[7] = {"call", "exception", "line", "return",
281+
"c_call", "c_exception", "c_return"};
281282
PyObject *name;
282283
int i;
283-
for (i = 0; i < 4; ++i) {
284+
for (i = 0; i < 7; ++i) {
284285
if (whatstrings[i] == NULL) {
285286
name = PyString_InternFromString(whatnames[i]);
286287
if (name == NULL)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy