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