Branch data Line data Source code
1 : : #ifndef Py_BUILD_CORE_BUILTIN
2 : : # define Py_BUILD_CORE_MODULE 1
3 : : #endif
4 : :
5 : : #include "Python.h"
6 : : #include "pycore_call.h" // _PyObject_CallNoArgs()
7 : : #include "pycore_pystate.h" // _PyThreadState_GET()
8 : : #include "rotatingtree.h"
9 : :
10 : : /************************************************************/
11 : : /* Written by Brett Rosen and Ted Czotter */
12 : :
13 : : struct _ProfilerEntry;
14 : :
15 : : /* represents a function called from another function */
16 : : typedef struct _ProfilerSubEntry {
17 : : rotating_node_t header;
18 : : _PyTime_t tt;
19 : : _PyTime_t it;
20 : : long callcount;
21 : : long recursivecallcount;
22 : : long recursionLevel;
23 : : } ProfilerSubEntry;
24 : :
25 : : /* represents a function or user defined block */
26 : : typedef struct _ProfilerEntry {
27 : : rotating_node_t header;
28 : : PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
29 : : _PyTime_t tt; /* total time in this entry */
30 : : _PyTime_t it; /* inline time in this entry (not in subcalls) */
31 : : long callcount; /* how many times this was called */
32 : : long recursivecallcount; /* how many times called recursively */
33 : : long recursionLevel;
34 : : rotating_node_t *calls;
35 : : } ProfilerEntry;
36 : :
37 : : typedef struct _ProfilerContext {
38 : : _PyTime_t t0;
39 : : _PyTime_t subt;
40 : : struct _ProfilerContext *previous;
41 : : ProfilerEntry *ctxEntry;
42 : : } ProfilerContext;
43 : :
44 : : typedef struct {
45 : : PyObject_HEAD
46 : : rotating_node_t *profilerEntries;
47 : : ProfilerContext *currentProfilerContext;
48 : : ProfilerContext *freelistProfilerContext;
49 : : int flags;
50 : : PyObject *externalTimer;
51 : : double externalTimerUnit;
52 : : } ProfilerObject;
53 : :
54 : : #define POF_ENABLED 0x001
55 : : #define POF_SUBCALLS 0x002
56 : : #define POF_BUILTINS 0x004
57 : : #define POF_NOMEMORY 0x100
58 : :
59 : : /*[clinic input]
60 : : module _lsprof
61 : : class _lsprof.Profiler "ProfilerObject *" "&ProfilerType"
62 : : [clinic start generated code]*/
63 : : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=e349ac952152f336]*/
64 : :
65 : : #include "clinic/_lsprof.c.h"
66 : :
67 : : typedef struct {
68 : : PyTypeObject *profiler_type;
69 : : PyTypeObject *stats_entry_type;
70 : : PyTypeObject *stats_subentry_type;
71 : : } _lsprof_state;
72 : :
73 : : static inline _lsprof_state*
74 : 132 : _lsprof_get_state(PyObject *module)
75 : : {
76 : 132 : void *state = PyModule_GetState(module);
77 : : assert(state != NULL);
78 : 132 : return (_lsprof_state *)state;
79 : : }
80 : :
81 : : /*** External Timers ***/
82 : :
83 : 288 : static _PyTime_t CallExternalTimer(ProfilerObject *pObj)
84 : : {
85 : 288 : PyObject *o = _PyObject_CallNoArgs(pObj->externalTimer);
86 [ - + ]: 288 : if (o == NULL) {
87 : 0 : PyErr_WriteUnraisable(pObj->externalTimer);
88 : 0 : return 0;
89 : : }
90 : :
91 : : _PyTime_t result;
92 : : int err;
93 [ + + ]: 288 : if (pObj->externalTimerUnit > 0.0) {
94 : : /* interpret the result as an integer that will be scaled
95 : : in profiler_getstats() */
96 : 286 : err = _PyTime_FromNanosecondsObject(&result, o);
97 : : }
98 : : else {
99 : : /* interpret the result as a double measured in seconds.
100 : : As the profiler works with _PyTime_t internally
101 : : we convert it to a large integer */
102 : 2 : err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
103 : : }
104 : 288 : Py_DECREF(o);
105 [ + + ]: 288 : if (err < 0) {
106 : 2 : PyErr_WriteUnraisable(pObj->externalTimer);
107 : 2 : return 0;
108 : : }
109 : 286 : return result;
110 : : }
111 : :
112 : : static inline _PyTime_t
113 : 1938 : call_timer(ProfilerObject *pObj)
114 : : {
115 [ + + ]: 1938 : if (pObj->externalTimer != NULL) {
116 : 288 : return CallExternalTimer(pObj);
117 : : }
118 : : else {
119 : 1650 : return _PyTime_GetPerfCounter();
120 : : }
121 : : }
122 : :
123 : :
124 : : /*** ProfilerObject ***/
125 : :
126 : : static PyObject *
127 : 255 : normalizeUserObj(PyObject *obj)
128 : : {
129 : : PyCFunctionObject *fn;
130 [ + + ]: 255 : if (!PyCFunction_Check(obj)) {
131 : 147 : Py_INCREF(obj);
132 : 147 : return obj;
133 : : }
134 : : /* Replace built-in function objects with a descriptive string
135 : : because of built-in methods -- keeping a reference to
136 : : __self__ is probably not a good idea. */
137 : 108 : fn = (PyCFunctionObject *)obj;
138 : :
139 [ - + ]: 108 : if (fn->m_self == NULL) {
140 : : /* built-in function: look up the module name */
141 : 0 : PyObject *mod = fn->m_module;
142 : 0 : PyObject *modname = NULL;
143 [ # # ]: 0 : if (mod != NULL) {
144 [ # # ]: 0 : if (PyUnicode_Check(mod)) {
145 : 0 : modname = mod;
146 : 0 : Py_INCREF(modname);
147 : : }
148 [ # # ]: 0 : else if (PyModule_Check(mod)) {
149 : 0 : modname = PyModule_GetNameObject(mod);
150 [ # # ]: 0 : if (modname == NULL)
151 : 0 : PyErr_Clear();
152 : : }
153 : : }
154 [ # # ]: 0 : if (modname != NULL) {
155 [ # # ]: 0 : if (!_PyUnicode_EqualToASCIIString(modname, "builtins")) {
156 : : PyObject *result;
157 : 0 : result = PyUnicode_FromFormat("<%U.%s>", modname,
158 : 0 : fn->m_ml->ml_name);
159 : 0 : Py_DECREF(modname);
160 : 0 : return result;
161 : : }
162 : 0 : Py_DECREF(modname);
163 : : }
164 : 0 : return PyUnicode_FromFormat("<%s>", fn->m_ml->ml_name);
165 : : }
166 : : else {
167 : : /* built-in method: try to return
168 : : repr(getattr(type(__self__), __name__))
169 : : */
170 : 108 : PyObject *self = fn->m_self;
171 : 108 : PyObject *name = PyUnicode_FromString(fn->m_ml->ml_name);
172 : 108 : PyObject *modname = fn->m_module;
173 : :
174 [ + - ]: 108 : if (name != NULL) {
175 : 108 : PyObject *mo = _PyType_Lookup(Py_TYPE(self), name);
176 : 108 : Py_XINCREF(mo);
177 : 108 : Py_DECREF(name);
178 [ + + ]: 108 : if (mo != NULL) {
179 : 43 : PyObject *res = PyObject_Repr(mo);
180 : 43 : Py_DECREF(mo);
181 [ + - ]: 43 : if (res != NULL)
182 : 43 : return res;
183 : : }
184 : : }
185 : : /* Otherwise, use __module__ */
186 : 65 : PyErr_Clear();
187 [ + + + - ]: 65 : if (modname != NULL && PyUnicode_Check(modname))
188 : 64 : return PyUnicode_FromFormat("<built-in method %S.%s>",
189 : 64 : modname, fn->m_ml->ml_name);
190 : : else
191 : 1 : return PyUnicode_FromFormat("<built-in method %s>",
192 : 1 : fn->m_ml->ml_name);
193 : : }
194 : : }
195 : :
196 : : static ProfilerEntry*
197 : 255 : newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj)
198 : : {
199 : : ProfilerEntry *self;
200 : 255 : self = (ProfilerEntry*) PyMem_Malloc(sizeof(ProfilerEntry));
201 [ - + ]: 255 : if (self == NULL) {
202 : 0 : pObj->flags |= POF_NOMEMORY;
203 : 0 : return NULL;
204 : : }
205 : 255 : userObj = normalizeUserObj(userObj);
206 [ - + ]: 255 : if (userObj == NULL) {
207 : 0 : PyErr_Clear();
208 : 0 : PyMem_Free(self);
209 : 0 : pObj->flags |= POF_NOMEMORY;
210 : 0 : return NULL;
211 : : }
212 : 255 : self->header.key = key;
213 : 255 : self->userObj = userObj;
214 : 255 : self->tt = 0;
215 : 255 : self->it = 0;
216 : 255 : self->callcount = 0;
217 : 255 : self->recursivecallcount = 0;
218 : 255 : self->recursionLevel = 0;
219 : 255 : self->calls = EMPTY_ROTATING_TREE;
220 : 255 : RotatingTree_Add(&pObj->profilerEntries, &self->header);
221 : 255 : return self;
222 : : }
223 : :
224 : : static ProfilerEntry*
225 : 1918 : getEntry(ProfilerObject *pObj, void *key)
226 : : {
227 : 1918 : return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
228 : : }
229 : :
230 : : static ProfilerSubEntry *
231 : 1858 : getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
232 : : {
233 : 1858 : return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
234 : : (void *)entry);
235 : : }
236 : :
237 : : static ProfilerSubEntry *
238 : 285 : newSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
239 : : {
240 : : ProfilerSubEntry *self;
241 : 285 : self = (ProfilerSubEntry*) PyMem_Malloc(sizeof(ProfilerSubEntry));
242 [ - + ]: 285 : if (self == NULL) {
243 : 0 : pObj->flags |= POF_NOMEMORY;
244 : 0 : return NULL;
245 : : }
246 : 285 : self->header.key = (void *)entry;
247 : 285 : self->tt = 0;
248 : 285 : self->it = 0;
249 : 285 : self->callcount = 0;
250 : 285 : self->recursivecallcount = 0;
251 : 285 : self->recursionLevel = 0;
252 : 285 : RotatingTree_Add(&caller->calls, &self->header);
253 : 285 : return self;
254 : : }
255 : :
256 : 285 : static int freeSubEntry(rotating_node_t *header, void *arg)
257 : : {
258 : 285 : ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
259 : 285 : PyMem_Free(subentry);
260 : 285 : return 0;
261 : : }
262 : :
263 : 255 : static int freeEntry(rotating_node_t *header, void *arg)
264 : : {
265 : 255 : ProfilerEntry *entry = (ProfilerEntry*) header;
266 : 255 : RotatingTree_Enum(entry->calls, freeSubEntry, NULL);
267 : 255 : Py_DECREF(entry->userObj);
268 : 255 : PyMem_Free(entry);
269 : 255 : return 0;
270 : : }
271 : :
272 : 20 : static void clearEntries(ProfilerObject *pObj)
273 : : {
274 : 20 : RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
275 : 20 : pObj->profilerEntries = EMPTY_ROTATING_TREE;
276 : : /* release the memory hold by the ProfilerContexts */
277 [ - + ]: 20 : if (pObj->currentProfilerContext) {
278 : 0 : PyMem_Free(pObj->currentProfilerContext);
279 : 0 : pObj->currentProfilerContext = NULL;
280 : : }
281 [ + + ]: 116 : while (pObj->freelistProfilerContext) {
282 : 96 : ProfilerContext *c = pObj->freelistProfilerContext;
283 : 96 : pObj->freelistProfilerContext = c->previous;
284 : 96 : PyMem_Free(c);
285 : : }
286 : 20 : pObj->freelistProfilerContext = NULL;
287 : 20 : }
288 : :
289 : : static void
290 : 969 : initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
291 : : {
292 : 969 : self->ctxEntry = entry;
293 : 969 : self->subt = 0;
294 : 969 : self->previous = pObj->currentProfilerContext;
295 : 969 : pObj->currentProfilerContext = self;
296 : 969 : ++entry->recursionLevel;
297 [ + - + + ]: 969 : if ((pObj->flags & POF_SUBCALLS) && self->previous) {
298 : : /* find or create an entry for me in my caller's entry */
299 : 929 : ProfilerEntry *caller = self->previous->ctxEntry;
300 : 929 : ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
301 [ + + ]: 929 : if (subentry == NULL)
302 : 285 : subentry = newSubEntry(pObj, caller, entry);
303 [ + - ]: 929 : if (subentry)
304 : 929 : ++subentry->recursionLevel;
305 : : }
306 : 969 : self->t0 = call_timer(pObj);
307 : 969 : }
308 : :
309 : : static void
310 : 969 : Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
311 : : {
312 : 969 : _PyTime_t tt = call_timer(pObj) - self->t0;
313 : 969 : _PyTime_t it = tt - self->subt;
314 [ + + ]: 969 : if (self->previous)
315 : 929 : self->previous->subt += tt;
316 : 969 : pObj->currentProfilerContext = self->previous;
317 [ + + ]: 969 : if (--entry->recursionLevel == 0)
318 : 906 : entry->tt += tt;
319 : : else
320 : 63 : ++entry->recursivecallcount;
321 : 969 : entry->it += it;
322 : 969 : entry->callcount++;
323 [ + - + + ]: 969 : if ((pObj->flags & POF_SUBCALLS) && self->previous) {
324 : : /* find or create an entry for me in my caller's entry */
325 : 929 : ProfilerEntry *caller = self->previous->ctxEntry;
326 : 929 : ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
327 [ + - ]: 929 : if (subentry) {
328 [ + + ]: 929 : if (--subentry->recursionLevel == 0)
329 : 878 : subentry->tt += tt;
330 : : else
331 : 51 : ++subentry->recursivecallcount;
332 : 929 : subentry->it += it;
333 : 929 : ++subentry->callcount;
334 : : }
335 : : }
336 : 969 : }
337 : :
338 : : static void
339 : 969 : ptrace_enter_call(PyObject *self, void *key, PyObject *userObj)
340 : : {
341 : : /* entering a call to the function identified by 'key'
342 : : (which can be a PyCodeObject or a PyMethodDef pointer) */
343 : 969 : ProfilerObject *pObj = (ProfilerObject*)self;
344 : : ProfilerEntry *profEntry;
345 : : ProfilerContext *pContext;
346 : :
347 : : /* In the case of entering a generator expression frame via a
348 : : * throw (gen_send_ex(.., 1)), we may already have an
349 : : * Exception set here. We must not mess around with this
350 : : * exception, and some of the code under here assumes that
351 : : * PyErr_* is its own to mess around with, so we have to
352 : : * save and restore any current exception. */
353 : : PyObject *last_type, *last_value, *last_tb;
354 : 969 : PyErr_Fetch(&last_type, &last_value, &last_tb);
355 : :
356 : 969 : profEntry = getEntry(pObj, key);
357 [ + + ]: 969 : if (profEntry == NULL) {
358 : 255 : profEntry = newProfilerEntry(pObj, key, userObj);
359 [ - + ]: 255 : if (profEntry == NULL)
360 : 0 : goto restorePyerr;
361 : : }
362 : : /* grab a ProfilerContext out of the free list */
363 : 969 : pContext = pObj->freelistProfilerContext;
364 [ + + ]: 969 : if (pContext) {
365 : 853 : pObj->freelistProfilerContext = pContext->previous;
366 : : }
367 : : else {
368 : : /* free list exhausted, allocate a new one */
369 : : pContext = (ProfilerContext*)
370 : 116 : PyMem_Malloc(sizeof(ProfilerContext));
371 [ - + ]: 116 : if (pContext == NULL) {
372 : 0 : pObj->flags |= POF_NOMEMORY;
373 : 0 : goto restorePyerr;
374 : : }
375 : : }
376 : 969 : initContext(pObj, pContext, profEntry);
377 : :
378 : 969 : restorePyerr:
379 : 969 : PyErr_Restore(last_type, last_value, last_tb);
380 : 969 : }
381 : :
382 : : static void
383 : 950 : ptrace_leave_call(PyObject *self, void *key)
384 : : {
385 : : /* leaving a call to the function identified by 'key' */
386 : 950 : ProfilerObject *pObj = (ProfilerObject*)self;
387 : : ProfilerEntry *profEntry;
388 : : ProfilerContext *pContext;
389 : :
390 : 950 : pContext = pObj->currentProfilerContext;
391 [ + + ]: 950 : if (pContext == NULL)
392 : 1 : return;
393 : 949 : profEntry = getEntry(pObj, key);
394 [ + - ]: 949 : if (profEntry) {
395 : 949 : Stop(pObj, pContext, profEntry);
396 : : }
397 : : else {
398 : 0 : pObj->currentProfilerContext = pContext->previous;
399 : : }
400 : : /* put pContext into the free list */
401 : 949 : pContext->previous = pObj->freelistProfilerContext;
402 : 949 : pObj->freelistProfilerContext = pContext;
403 : : }
404 : :
405 : : static int
406 : 1919 : profiler_callback(PyObject *self, PyFrameObject *frame, int what,
407 : : PyObject *arg)
408 : : {
409 [ + + + + : 1919 : switch (what) {
- ]
410 : :
411 : : /* the 'frame' of a called function is about to start its execution */
412 : 575 : case PyTrace_CALL:
413 : : {
414 : 575 : PyCodeObject *code = PyFrame_GetCode(frame);
415 : 575 : ptrace_enter_call(self, (void *)code, (PyObject *)code);
416 : 575 : Py_DECREF(code);
417 : 575 : break;
418 : : }
419 : :
420 : : /* the 'frame' of a called function is about to finish
421 : : (either normally or with an exception) */
422 : 574 : case PyTrace_RETURN:
423 : : {
424 : 574 : PyCodeObject *code = PyFrame_GetCode(frame);
425 : 574 : ptrace_leave_call(self, (void *)code);
426 : 574 : Py_DECREF(code);
427 : 574 : break;
428 : : }
429 : :
430 : : /* case PyTrace_EXCEPTION:
431 : : If the exception results in the function exiting, a
432 : : PyTrace_RETURN event will be generated, so we don't need to
433 : : handle it. */
434 : :
435 : : /* the Python function 'frame' is issuing a call to the built-in
436 : : function 'arg' */
437 : 394 : case PyTrace_C_CALL:
438 [ + - ]: 394 : if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
439 [ + - ]: 394 : && PyCFunction_Check(arg)) {
440 : 394 : ptrace_enter_call(self,
441 : 394 : ((PyCFunctionObject *)arg)->m_ml,
442 : : arg);
443 : : }
444 : 394 : break;
445 : :
446 : : /* the call to the built-in function 'arg' is returning into its
447 : : caller 'frame' */
448 : 376 : case PyTrace_C_RETURN: /* ...normally */
449 : : case PyTrace_C_EXCEPTION: /* ...with an exception set */
450 [ + - ]: 376 : if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
451 [ + - ]: 376 : && PyCFunction_Check(arg)) {
452 : 376 : ptrace_leave_call(self,
453 : 376 : ((PyCFunctionObject *)arg)->m_ml);
454 : : }
455 : 376 : break;
456 : :
457 : 0 : default:
458 : 0 : break;
459 : : }
460 : 1919 : return 0;
461 : : }
462 : :
463 : : static int
464 : 55 : pending_exception(ProfilerObject *pObj)
465 : : {
466 [ - + ]: 55 : if (pObj->flags & POF_NOMEMORY) {
467 : 0 : pObj->flags -= POF_NOMEMORY;
468 : 0 : PyErr_SetString(PyExc_MemoryError,
469 : : "memory was exhausted while profiling");
470 : 0 : return -1;
471 : : }
472 : 55 : return 0;
473 : : }
474 : :
475 : : /************************************************************/
476 : :
477 : : static PyStructSequence_Field profiler_entry_fields[] = {
478 : : {"code", "code object or built-in function name"},
479 : : {"callcount", "how many times this was called"},
480 : : {"reccallcount", "how many times called recursively"},
481 : : {"totaltime", "total time in this entry"},
482 : : {"inlinetime", "inline time in this entry (not in subcalls)"},
483 : : {"calls", "details of the calls"},
484 : : {0}
485 : : };
486 : :
487 : : static PyStructSequence_Field profiler_subentry_fields[] = {
488 : : {"code", "called code object or built-in function name"},
489 : : {"callcount", "how many times this is called"},
490 : : {"reccallcount", "how many times this is called recursively"},
491 : : {"totaltime", "total time spent in this call"},
492 : : {"inlinetime", "inline time (not in further subcalls)"},
493 : : {0}
494 : : };
495 : :
496 : : static PyStructSequence_Desc profiler_entry_desc = {
497 : : .name = "_lsprof.profiler_entry",
498 : : .fields = profiler_entry_fields,
499 : : .doc = NULL,
500 : : .n_in_sequence = 6
501 : : };
502 : :
503 : : static PyStructSequence_Desc profiler_subentry_desc = {
504 : : .name = "_lsprof.profiler_subentry",
505 : : .fields = profiler_subentry_fields,
506 : : .doc = NULL,
507 : : .n_in_sequence = 5
508 : : };
509 : :
510 : : typedef struct {
511 : : PyObject *list;
512 : : PyObject *sublist;
513 : : double factor;
514 : : _lsprof_state *state;
515 : : } statscollector_t;
516 : :
517 : 321 : static int statsForSubEntry(rotating_node_t *node, void *arg)
518 : : {
519 : 321 : ProfilerSubEntry *sentry = (ProfilerSubEntry*) node;
520 : 321 : statscollector_t *collect = (statscollector_t*) arg;
521 : 321 : ProfilerEntry *entry = (ProfilerEntry*) sentry->header.key;
522 : : int err;
523 : : PyObject *sinfo;
524 : 321 : sinfo = PyObject_CallFunction((PyObject*) collect->state->stats_subentry_type,
525 : : "((Olldd))",
526 : : entry->userObj,
527 : : sentry->callcount,
528 : : sentry->recursivecallcount,
529 : 321 : collect->factor * sentry->tt,
530 : 321 : collect->factor * sentry->it);
531 [ - + ]: 321 : if (sinfo == NULL)
532 : 0 : return -1;
533 : 321 : err = PyList_Append(collect->sublist, sinfo);
534 : 321 : Py_DECREF(sinfo);
535 : 321 : return err;
536 : : }
537 : :
538 : 282 : static int statsForEntry(rotating_node_t *node, void *arg)
539 : : {
540 : 282 : ProfilerEntry *entry = (ProfilerEntry*) node;
541 : 282 : statscollector_t *collect = (statscollector_t*) arg;
542 : : PyObject *info;
543 : : int err;
544 [ - + ]: 282 : if (entry->callcount == 0)
545 : 0 : return 0; /* skip */
546 : :
547 [ + + ]: 282 : if (entry->calls != EMPTY_ROTATING_TREE) {
548 : 153 : collect->sublist = PyList_New(0);
549 [ - + ]: 153 : if (collect->sublist == NULL)
550 : 0 : return -1;
551 [ - + ]: 153 : if (RotatingTree_Enum(entry->calls,
552 : : statsForSubEntry, collect) != 0) {
553 : 0 : Py_DECREF(collect->sublist);
554 : 0 : return -1;
555 : : }
556 : : }
557 : : else {
558 : 129 : Py_INCREF(Py_None);
559 : 129 : collect->sublist = Py_None;
560 : : }
561 : :
562 : 282 : info = PyObject_CallFunction((PyObject*) collect->state->stats_entry_type,
563 : : "((OllddO))",
564 : : entry->userObj,
565 : : entry->callcount,
566 : : entry->recursivecallcount,
567 : 282 : collect->factor * entry->tt,
568 : 282 : collect->factor * entry->it,
569 : : collect->sublist);
570 : 282 : Py_DECREF(collect->sublist);
571 [ - + ]: 282 : if (info == NULL)
572 : 0 : return -1;
573 : 282 : err = PyList_Append(collect->list, info);
574 : 282 : Py_DECREF(info);
575 : 282 : return err;
576 : : }
577 : :
578 : : /*[clinic input]
579 : : _lsprof.Profiler.getstats
580 : :
581 : : cls: defining_class
582 : :
583 : : list of profiler_entry objects.
584 : :
585 : : getstats() -> list of profiler_entry objects
586 : :
587 : : Return all information collected by the profiler.
588 : : Each profiler_entry is a tuple-like object with the
589 : : following attributes:
590 : :
591 : : code code object
592 : : callcount how many times this was called
593 : : reccallcount how many times called recursively
594 : : totaltime total time in this entry
595 : : inlinetime inline time in this entry (not in subcalls)
596 : : calls details of the calls
597 : :
598 : : The calls attribute is either None or a list of
599 : : profiler_subentry objects:
600 : :
601 : : code called code object
602 : : callcount how many times this is called
603 : : reccallcount how many times this is called recursively
604 : : totaltime total time spent in this call
605 : : inlinetime inline time (not in further subcalls)
606 : : [clinic start generated code]*/
607 : :
608 : : static PyObject *
609 : 18 : _lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls)
610 : : /*[clinic end generated code: output=1806ef720019ee03 input=445e193ef4522902]*/
611 : : {
612 : : statscollector_t collect;
613 : 18 : collect.state = PyType_GetModuleState(cls);
614 [ - + ]: 18 : if (pending_exception(self)) {
615 : 0 : return NULL;
616 : : }
617 [ + + - + ]: 18 : if (!self->externalTimer || self->externalTimerUnit == 0.0) {
618 : 9 : _PyTime_t onesec = _PyTime_FromSeconds(1);
619 : 9 : collect.factor = (double)1 / onesec;
620 : : }
621 : : else {
622 : 9 : collect.factor = self->externalTimerUnit;
623 : : }
624 : :
625 : 18 : collect.list = PyList_New(0);
626 [ - + ]: 18 : if (collect.list == NULL)
627 : 0 : return NULL;
628 [ - + ]: 18 : if (RotatingTree_Enum(self->profilerEntries, statsForEntry, &collect)
629 : : != 0) {
630 : 0 : Py_DECREF(collect.list);
631 : 0 : return NULL;
632 : : }
633 : 18 : return collect.list;
634 : : }
635 : :
636 : : static int
637 : 37 : setSubcalls(ProfilerObject *pObj, int nvalue)
638 : : {
639 [ - + ]: 37 : if (nvalue == 0)
640 : 0 : pObj->flags &= ~POF_SUBCALLS;
641 [ + + ]: 37 : else if (nvalue > 0)
642 : 19 : pObj->flags |= POF_SUBCALLS;
643 : 37 : return 0;
644 : : }
645 : :
646 : : static int
647 : 37 : setBuiltins(ProfilerObject *pObj, int nvalue)
648 : : {
649 [ - + ]: 37 : if (nvalue == 0)
650 : 0 : pObj->flags &= ~POF_BUILTINS;
651 [ + + ]: 37 : else if (nvalue > 0) {
652 : 19 : pObj->flags |= POF_BUILTINS;
653 : : }
654 : 37 : return 0;
655 : : }
656 : :
657 : : PyDoc_STRVAR(enable_doc, "\
658 : : enable(subcalls=True, builtins=True)\n\
659 : : \n\
660 : : Start collecting profiling information.\n\
661 : : If 'subcalls' is True, also records for each function\n\
662 : : statistics separated according to its current caller.\n\
663 : : If 'builtins' is True, records the time spent in\n\
664 : : built-in functions separately from their caller.\n\
665 : : ");
666 : :
667 : : static PyObject*
668 : 18 : profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
669 : : {
670 : 18 : int subcalls = -1;
671 : 18 : int builtins = -1;
672 : : static char *kwlist[] = {"subcalls", "builtins", 0};
673 [ - + ]: 18 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:enable",
674 : : kwlist, &subcalls, &builtins))
675 : 0 : return NULL;
676 [ + - - + ]: 18 : if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) {
677 : 0 : return NULL;
678 : : }
679 : :
680 : 18 : PyThreadState *tstate = _PyThreadState_GET();
681 [ - + ]: 18 : if (_PyEval_SetProfile(tstate, profiler_callback, (PyObject*)self) < 0) {
682 : 0 : return NULL;
683 : : }
684 : :
685 : 18 : self->flags |= POF_ENABLED;
686 : 18 : Py_RETURN_NONE;
687 : : }
688 : :
689 : : static void
690 : 56 : flush_unmatched(ProfilerObject *pObj)
691 : : {
692 [ + + ]: 132 : while (pObj->currentProfilerContext) {
693 : 20 : ProfilerContext *pContext = pObj->currentProfilerContext;
694 : 20 : ProfilerEntry *profEntry= pContext->ctxEntry;
695 [ + - ]: 20 : if (profEntry)
696 : 20 : Stop(pObj, pContext, profEntry);
697 : : else
698 : 0 : pObj->currentProfilerContext = pContext->previous;
699 [ - + ]: 20 : if (pContext)
700 : 20 : PyMem_Free(pContext);
701 : : }
702 : :
703 : 56 : }
704 : :
705 : : PyDoc_STRVAR(disable_doc, "\
706 : : disable()\n\
707 : : \n\
708 : : Stop collecting profiling information.\n\
709 : : ");
710 : :
711 : : static PyObject*
712 : 37 : profiler_disable(ProfilerObject *self, PyObject* noarg)
713 : : {
714 : 37 : PyThreadState *tstate = _PyThreadState_GET();
715 [ - + ]: 37 : if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
716 : 0 : return NULL;
717 : : }
718 : 37 : self->flags &= ~POF_ENABLED;
719 : :
720 : 37 : flush_unmatched(self);
721 [ - + ]: 37 : if (pending_exception(self)) {
722 : 0 : return NULL;
723 : : }
724 : 37 : Py_RETURN_NONE;
725 : : }
726 : :
727 : : PyDoc_STRVAR(clear_doc, "\
728 : : clear()\n\
729 : : \n\
730 : : Clear all profiling information collected so far.\n\
731 : : ");
732 : :
733 : : static PyObject*
734 : 1 : profiler_clear(ProfilerObject *pObj, PyObject* noarg)
735 : : {
736 : 1 : clearEntries(pObj);
737 : 1 : Py_RETURN_NONE;
738 : : }
739 : :
740 : : static int
741 : 4 : profiler_traverse(ProfilerObject *op, visitproc visit, void *arg)
742 : : {
743 [ + - - + ]: 4 : Py_VISIT(Py_TYPE(op));
744 : 4 : return 0;
745 : : }
746 : :
747 : : static void
748 : 19 : profiler_dealloc(ProfilerObject *op)
749 : : {
750 [ + + ]: 19 : if (op->flags & POF_ENABLED) {
751 : 1 : PyThreadState *tstate = _PyThreadState_GET();
752 [ + - ]: 1 : if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
753 : 1 : _PyErr_WriteUnraisableMsg("When destroying _lsprof profiler", NULL);
754 : : }
755 : : }
756 : :
757 : 19 : flush_unmatched(op);
758 : 19 : clearEntries(op);
759 : 19 : Py_XDECREF(op->externalTimer);
760 : 19 : PyTypeObject *tp = Py_TYPE(op);
761 : 19 : tp->tp_free(op);
762 : 19 : Py_DECREF(tp);
763 : 19 : }
764 : :
765 : : static int
766 : 19 : profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
767 : : {
768 : 19 : PyObject *timer = NULL;
769 : 19 : double timeunit = 0.0;
770 : 19 : int subcalls = 1;
771 : 19 : int builtins = 1;
772 : : static char *kwlist[] = {"timer", "timeunit",
773 : : "subcalls", "builtins", 0};
774 : :
775 [ - + ]: 19 : if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odii:Profiler", kwlist,
776 : : &timer, &timeunit,
777 : : &subcalls, &builtins))
778 : 0 : return -1;
779 : :
780 [ + - - + ]: 19 : if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
781 : 0 : return -1;
782 : 19 : pObj->externalTimerUnit = timeunit;
783 : 19 : Py_XINCREF(timer);
784 : 19 : Py_XSETREF(pObj->externalTimer, timer);
785 : 19 : return 0;
786 : : }
787 : :
788 : : static PyMethodDef profiler_methods[] = {
789 : : _LSPROF_PROFILER_GETSTATS_METHODDEF
790 : : {"enable", _PyCFunction_CAST(profiler_enable),
791 : : METH_VARARGS | METH_KEYWORDS, enable_doc},
792 : : {"disable", (PyCFunction)profiler_disable,
793 : : METH_NOARGS, disable_doc},
794 : : {"clear", (PyCFunction)profiler_clear,
795 : : METH_NOARGS, clear_doc},
796 : : {NULL, NULL}
797 : : };
798 : :
799 : : PyDoc_STRVAR(profiler_doc, "\
800 : : Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\
801 : : \n\
802 : : Builds a profiler object using the specified timer function.\n\
803 : : The default timer is a fast built-in one based on real time.\n\
804 : : For custom timer functions returning integers, timeunit can\n\
805 : : be a float specifying a scale (i.e. how long each integer unit\n\
806 : : is, in seconds).\n\
807 : : ");
808 : :
809 : : static PyType_Slot _lsprof_profiler_type_spec_slots[] = {
810 : : {Py_tp_doc, (void *)profiler_doc},
811 : : {Py_tp_methods, profiler_methods},
812 : : {Py_tp_dealloc, profiler_dealloc},
813 : : {Py_tp_init, profiler_init},
814 : : {Py_tp_traverse, profiler_traverse},
815 : : {0, 0}
816 : : };
817 : :
818 : : static PyType_Spec _lsprof_profiler_type_spec = {
819 : : .name = "_lsprof.Profiler",
820 : : .basicsize = sizeof(ProfilerObject),
821 : : .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
822 : : Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
823 : : .slots = _lsprof_profiler_type_spec_slots,
824 : : };
825 : :
826 : : static PyMethodDef moduleMethods[] = {
827 : : {NULL, NULL}
828 : : };
829 : :
830 : : static int
831 : 114 : _lsprof_traverse(PyObject *module, visitproc visit, void *arg)
832 : : {
833 : 114 : _lsprof_state *state = _lsprof_get_state(module);
834 [ + - - + ]: 114 : Py_VISIT(state->profiler_type);
835 [ + - - + ]: 114 : Py_VISIT(state->stats_entry_type);
836 [ + - - + ]: 114 : Py_VISIT(state->stats_subentry_type);
837 : 114 : return 0;
838 : : }
839 : :
840 : : static int
841 : 18 : _lsprof_clear(PyObject *module)
842 : : {
843 : 18 : _lsprof_state *state = _lsprof_get_state(module);
844 [ + + ]: 18 : Py_CLEAR(state->profiler_type);
845 [ + + ]: 18 : Py_CLEAR(state->stats_entry_type);
846 [ + + ]: 18 : Py_CLEAR(state->stats_subentry_type);
847 : 18 : return 0;
848 : : }
849 : :
850 : : static void
851 : 9 : _lsprof_free(void *module)
852 : : {
853 : 9 : _lsprof_clear((PyObject *)module);
854 : 9 : }
855 : :
856 : : static int
857 : 9 : _lsprof_exec(PyObject *module)
858 : : {
859 : 9 : _lsprof_state *state = PyModule_GetState(module);
860 : :
861 : 9 : state->profiler_type = (PyTypeObject *)PyType_FromModuleAndSpec(
862 : : module, &_lsprof_profiler_type_spec, NULL);
863 [ - + ]: 9 : if (state->profiler_type == NULL) {
864 : 0 : return -1;
865 : : }
866 : :
867 [ - + ]: 9 : if (PyModule_AddType(module, state->profiler_type) < 0) {
868 : 0 : return -1;
869 : : }
870 : :
871 : 9 : state->stats_entry_type = PyStructSequence_NewType(&profiler_entry_desc);
872 [ - + ]: 9 : if (state->stats_entry_type == NULL) {
873 : 0 : return -1;
874 : : }
875 [ - + ]: 9 : if (PyModule_AddType(module, state->stats_entry_type) < 0) {
876 : 0 : return -1;
877 : : }
878 : :
879 : 9 : state->stats_subentry_type = PyStructSequence_NewType(&profiler_subentry_desc);
880 [ - + ]: 9 : if (state->stats_subentry_type == NULL) {
881 : 0 : return -1;
882 : : }
883 [ - + ]: 9 : if (PyModule_AddType(module, state->stats_subentry_type) < 0) {
884 : 0 : return -1;
885 : : }
886 : :
887 : 9 : return 0;
888 : : }
889 : :
890 : : static PyModuleDef_Slot _lsprofslots[] = {
891 : : {Py_mod_exec, _lsprof_exec},
892 : : {0, NULL}
893 : : };
894 : :
895 : : static struct PyModuleDef _lsprofmodule = {
896 : : PyModuleDef_HEAD_INIT,
897 : : .m_name = "_lsprof",
898 : : .m_doc = "Fast profiler",
899 : : .m_size = sizeof(_lsprof_state),
900 : : .m_methods = moduleMethods,
901 : : .m_slots = _lsprofslots,
902 : : .m_traverse = _lsprof_traverse,
903 : : .m_clear = _lsprof_clear,
904 : : .m_free = _lsprof_free
905 : : };
906 : :
907 : : PyMODINIT_FUNC
908 : 9 : PyInit__lsprof(void)
909 : : {
910 : 9 : return PyModuleDef_Init(&_lsprofmodule);
911 : : }
|