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