blob: 39cf6e126d635f7a3e6caa63f9105b27dcffb750 [file] [log] [blame]
Armin Rigoa871ef22006-02-08 12:53:56 +00001#include "Python.h"
Armin Rigoa871ef22006-02-08 12:53:56 +00002#include "rotatingtree.h"
3
Armin Rigoa871ef22006-02-08 12:53:56 +00004/************************************************************/
5/* Written by Brett Rosen and Ted Czotter */
6
7struct _ProfilerEntry;
8
9/* represents a function called from another function */
10typedef struct _ProfilerSubEntry {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000011 rotating_node_t header;
Inada Naoki536a35b2019-04-11 19:11:46 +090012 _PyTime_t tt;
13 _PyTime_t it;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000014 long callcount;
15 long recursivecallcount;
16 long recursionLevel;
Armin Rigoa871ef22006-02-08 12:53:56 +000017} ProfilerSubEntry;
18
19/* represents a function or user defined block */
20typedef struct _ProfilerEntry {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000021 rotating_node_t header;
22 PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
Inada Naoki536a35b2019-04-11 19:11:46 +090023 _PyTime_t tt; /* total time in this entry */
24 _PyTime_t it; /* inline time in this entry (not in subcalls) */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000025 long callcount; /* how many times this was called */
26 long recursivecallcount; /* how many times called recursively */
27 long recursionLevel;
28 rotating_node_t *calls;
Armin Rigoa871ef22006-02-08 12:53:56 +000029} ProfilerEntry;
30
31typedef struct _ProfilerContext {
Inada Naoki536a35b2019-04-11 19:11:46 +090032 _PyTime_t t0;
33 _PyTime_t subt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000034 struct _ProfilerContext *previous;
35 ProfilerEntry *ctxEntry;
Armin Rigoa871ef22006-02-08 12:53:56 +000036} ProfilerContext;
37
38typedef struct {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000039 PyObject_HEAD
40 rotating_node_t *profilerEntries;
41 ProfilerContext *currentProfilerContext;
42 ProfilerContext *freelistProfilerContext;
43 int flags;
44 PyObject *externalTimer;
45 double externalTimerUnit;
Armin Rigoa871ef22006-02-08 12:53:56 +000046} ProfilerObject;
47
48#define POF_ENABLED 0x001
49#define POF_SUBCALLS 0x002
50#define POF_BUILTINS 0x004
51#define POF_NOMEMORY 0x100
52
Neal Norwitz227b5332006-03-22 09:28:35 +000053static PyTypeObject PyProfiler_Type;
Armin Rigoa871ef22006-02-08 12:53:56 +000054
55#define PyProfiler_Check(op) PyObject_TypeCheck(op, &PyProfiler_Type)
Dong-hee Na1b55b652020-02-17 19:09:15 +090056#define PyProfiler_CheckExact(op) Py_IS_TYPE(op, &PyProfiler_Type)
Armin Rigoa871ef22006-02-08 12:53:56 +000057
58/*** External Timers ***/
59
Inada Naoki536a35b2019-04-11 19:11:46 +090060static _PyTime_t CallExternalTimer(ProfilerObject *pObj)
Armin Rigoa871ef22006-02-08 12:53:56 +000061{
Inada Naoki536a35b2019-04-11 19:11:46 +090062 PyObject *o = _PyObject_CallNoArg(pObj->externalTimer);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000063 if (o == NULL) {
64 PyErr_WriteUnraisable(pObj->externalTimer);
65 return 0;
66 }
Inada Naoki536a35b2019-04-11 19:11:46 +090067
68 _PyTime_t result;
69 int err;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000070 if (pObj->externalTimerUnit > 0.0) {
71 /* interpret the result as an integer that will be scaled
72 in profiler_getstats() */
Inada Naoki536a35b2019-04-11 19:11:46 +090073 err = _PyTime_FromNanosecondsObject(&result, o);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000074 }
75 else {
76 /* interpret the result as a double measured in seconds.
Inada Naoki536a35b2019-04-11 19:11:46 +090077 As the profiler works with _PyTime_t internally
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000078 we convert it to a large integer */
Inada Naoki536a35b2019-04-11 19:11:46 +090079 err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000080 }
81 Py_DECREF(o);
Inada Naoki536a35b2019-04-11 19:11:46 +090082 if (err < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000083 PyErr_WriteUnraisable(pObj->externalTimer);
84 return 0;
85 }
86 return result;
Armin Rigoa871ef22006-02-08 12:53:56 +000087}
88
Inada Naoki536a35b2019-04-11 19:11:46 +090089static inline _PyTime_t
90call_timer(ProfilerObject *pObj)
91{
92 if (pObj->externalTimer != NULL) {
93 return CallExternalTimer(pObj);
94 }
95 else {
96 return _PyTime_GetPerfCounter();
97 }
98}
99
Armin Rigoa871ef22006-02-08 12:53:56 +0000100
101/*** ProfilerObject ***/
102
103static PyObject *
104normalizeUserObj(PyObject *obj)
105{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000106 PyCFunctionObject *fn;
107 if (!PyCFunction_Check(obj)) {
108 Py_INCREF(obj);
109 return obj;
110 }
111 /* Replace built-in function objects with a descriptive string
112 because of built-in methods -- keeping a reference to
113 __self__ is probably not a good idea. */
114 fn = (PyCFunctionObject *)obj;
Armin Rigoa871ef22006-02-08 12:53:56 +0000115
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000116 if (fn->m_self == NULL) {
117 /* built-in function: look up the module name */
118 PyObject *mod = fn->m_module;
Victor Stinner7edb5df2011-06-20 14:59:53 +0200119 PyObject *modname = NULL;
120 if (mod != NULL) {
121 if (PyUnicode_Check(mod)) {
122 modname = mod;
123 Py_INCREF(modname);
124 }
125 else if (PyModule_Check(mod)) {
126 modname = PyModule_GetNameObject(mod);
127 if (modname == NULL)
128 PyErr_Clear();
Alexander Belopolskye239d232010-12-08 23:31:48 +0000129 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000130 }
Victor Stinner7edb5df2011-06-20 14:59:53 +0200131 if (modname != NULL) {
Serhiy Storchakaf4934ea2016-11-16 10:17:58 +0200132 if (!_PyUnicode_EqualToASCIIString(modname, "builtins")) {
Victor Stinner7edb5df2011-06-20 14:59:53 +0200133 PyObject *result;
134 result = PyUnicode_FromFormat("<%U.%s>", modname,
135 fn->m_ml->ml_name);
136 Py_DECREF(modname);
137 return result;
Alexander Belopolsky532d0912010-12-10 18:14:16 +0000138 }
Victor Stinner7edb5df2011-06-20 14:59:53 +0200139 Py_DECREF(modname);
Alexander Belopolsky532d0912010-12-10 18:14:16 +0000140 }
Victor Stinner7edb5df2011-06-20 14:59:53 +0200141 return PyUnicode_FromFormat("<%s>", fn->m_ml->ml_name);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000142 }
143 else {
144 /* built-in method: try to return
145 repr(getattr(type(__self__), __name__))
146 */
147 PyObject *self = fn->m_self;
148 PyObject *name = PyUnicode_FromString(fn->m_ml->ml_name);
Antoine Pitrou8477f7a2014-06-27 23:49:29 -0400149 PyObject *modname = fn->m_module;
150
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000151 if (name != NULL) {
152 PyObject *mo = _PyType_Lookup(Py_TYPE(self), name);
153 Py_XINCREF(mo);
154 Py_DECREF(name);
155 if (mo != NULL) {
156 PyObject *res = PyObject_Repr(mo);
157 Py_DECREF(mo);
158 if (res != NULL)
159 return res;
160 }
161 }
Antoine Pitrou8477f7a2014-06-27 23:49:29 -0400162 /* Otherwise, use __module__ */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000163 PyErr_Clear();
Antoine Pitrou8477f7a2014-06-27 23:49:29 -0400164 if (modname != NULL && PyUnicode_Check(modname))
165 return PyUnicode_FromFormat("<built-in method %S.%s>",
166 modname, fn->m_ml->ml_name);
167 else
168 return PyUnicode_FromFormat("<built-in method %s>",
169 fn->m_ml->ml_name);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000170 }
Armin Rigoa871ef22006-02-08 12:53:56 +0000171}
172
173static ProfilerEntry*
174newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj)
175{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000176 ProfilerEntry *self;
Victor Stinnerb6404912013-07-07 16:21:41 +0200177 self = (ProfilerEntry*) PyMem_Malloc(sizeof(ProfilerEntry));
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000178 if (self == NULL) {
179 pObj->flags |= POF_NOMEMORY;
180 return NULL;
181 }
182 userObj = normalizeUserObj(userObj);
183 if (userObj == NULL) {
184 PyErr_Clear();
Victor Stinnerb6404912013-07-07 16:21:41 +0200185 PyMem_Free(self);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000186 pObj->flags |= POF_NOMEMORY;
187 return NULL;
188 }
189 self->header.key = key;
190 self->userObj = userObj;
191 self->tt = 0;
192 self->it = 0;
193 self->callcount = 0;
194 self->recursivecallcount = 0;
195 self->recursionLevel = 0;
196 self->calls = EMPTY_ROTATING_TREE;
197 RotatingTree_Add(&pObj->profilerEntries, &self->header);
198 return self;
Armin Rigoa871ef22006-02-08 12:53:56 +0000199}
200
201static ProfilerEntry*
202getEntry(ProfilerObject *pObj, void *key)
203{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000204 return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
Armin Rigoa871ef22006-02-08 12:53:56 +0000205}
206
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000207static ProfilerSubEntry *
Armin Rigoa871ef22006-02-08 12:53:56 +0000208getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
209{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000210 return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
211 (void *)entry);
Armin Rigoa871ef22006-02-08 12:53:56 +0000212}
213
214static ProfilerSubEntry *
215newSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
216{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000217 ProfilerSubEntry *self;
Victor Stinnerb6404912013-07-07 16:21:41 +0200218 self = (ProfilerSubEntry*) PyMem_Malloc(sizeof(ProfilerSubEntry));
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000219 if (self == NULL) {
220 pObj->flags |= POF_NOMEMORY;
221 return NULL;
222 }
223 self->header.key = (void *)entry;
224 self->tt = 0;
225 self->it = 0;
226 self->callcount = 0;
227 self->recursivecallcount = 0;
228 self->recursionLevel = 0;
229 RotatingTree_Add(&caller->calls, &self->header);
230 return self;
Armin Rigoa871ef22006-02-08 12:53:56 +0000231}
232
233static int freeSubEntry(rotating_node_t *header, void *arg)
234{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000235 ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
Victor Stinnerb6404912013-07-07 16:21:41 +0200236 PyMem_Free(subentry);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000237 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000238}
239
240static int freeEntry(rotating_node_t *header, void *arg)
241{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000242 ProfilerEntry *entry = (ProfilerEntry*) header;
243 RotatingTree_Enum(entry->calls, freeSubEntry, NULL);
244 Py_DECREF(entry->userObj);
Victor Stinnerb6404912013-07-07 16:21:41 +0200245 PyMem_Free(entry);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000246 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000247}
248
249static void clearEntries(ProfilerObject *pObj)
250{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000251 RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
252 pObj->profilerEntries = EMPTY_ROTATING_TREE;
253 /* release the memory hold by the ProfilerContexts */
254 if (pObj->currentProfilerContext) {
Victor Stinnerb6404912013-07-07 16:21:41 +0200255 PyMem_Free(pObj->currentProfilerContext);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000256 pObj->currentProfilerContext = NULL;
257 }
258 while (pObj->freelistProfilerContext) {
259 ProfilerContext *c = pObj->freelistProfilerContext;
260 pObj->freelistProfilerContext = c->previous;
Victor Stinnerb6404912013-07-07 16:21:41 +0200261 PyMem_Free(c);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000262 }
263 pObj->freelistProfilerContext = NULL;
Armin Rigoa871ef22006-02-08 12:53:56 +0000264}
265
266static void
267initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
268{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000269 self->ctxEntry = entry;
270 self->subt = 0;
271 self->previous = pObj->currentProfilerContext;
272 pObj->currentProfilerContext = self;
273 ++entry->recursionLevel;
274 if ((pObj->flags & POF_SUBCALLS) && self->previous) {
275 /* find or create an entry for me in my caller's entry */
276 ProfilerEntry *caller = self->previous->ctxEntry;
277 ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
278 if (subentry == NULL)
279 subentry = newSubEntry(pObj, caller, entry);
280 if (subentry)
281 ++subentry->recursionLevel;
282 }
Inada Naoki536a35b2019-04-11 19:11:46 +0900283 self->t0 = call_timer(pObj);
Armin Rigoa871ef22006-02-08 12:53:56 +0000284}
285
286static void
287Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
288{
Inada Naoki536a35b2019-04-11 19:11:46 +0900289 _PyTime_t tt = call_timer(pObj) - self->t0;
290 _PyTime_t it = tt - self->subt;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000291 if (self->previous)
292 self->previous->subt += tt;
293 pObj->currentProfilerContext = self->previous;
294 if (--entry->recursionLevel == 0)
295 entry->tt += tt;
296 else
297 ++entry->recursivecallcount;
298 entry->it += it;
299 entry->callcount++;
300 if ((pObj->flags & POF_SUBCALLS) && self->previous) {
301 /* find or create an entry for me in my caller's entry */
302 ProfilerEntry *caller = self->previous->ctxEntry;
303 ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
304 if (subentry) {
305 if (--subentry->recursionLevel == 0)
306 subentry->tt += tt;
307 else
308 ++subentry->recursivecallcount;
309 subentry->it += it;
310 ++subentry->callcount;
311 }
312 }
Armin Rigoa871ef22006-02-08 12:53:56 +0000313}
314
315static void
316ptrace_enter_call(PyObject *self, void *key, PyObject *userObj)
317{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000318 /* entering a call to the function identified by 'key'
319 (which can be a PyCodeObject or a PyMethodDef pointer) */
320 ProfilerObject *pObj = (ProfilerObject*)self;
321 ProfilerEntry *profEntry;
322 ProfilerContext *pContext;
Armin Rigoa871ef22006-02-08 12:53:56 +0000323
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000324 /* In the case of entering a generator expression frame via a
325 * throw (gen_send_ex(.., 1)), we may already have an
326 * Exception set here. We must not mess around with this
327 * exception, and some of the code under here assumes that
328 * PyErr_* is its own to mess around with, so we have to
329 * save and restore any current exception. */
330 PyObject *last_type, *last_value, *last_tb;
331 PyErr_Fetch(&last_type, &last_value, &last_tb);
Thomas Wouters89d996e2007-09-08 17:39:28 +0000332
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000333 profEntry = getEntry(pObj, key);
334 if (profEntry == NULL) {
335 profEntry = newProfilerEntry(pObj, key, userObj);
336 if (profEntry == NULL)
337 goto restorePyerr;
338 }
339 /* grab a ProfilerContext out of the free list */
340 pContext = pObj->freelistProfilerContext;
341 if (pContext) {
342 pObj->freelistProfilerContext = pContext->previous;
343 }
344 else {
345 /* free list exhausted, allocate a new one */
346 pContext = (ProfilerContext*)
Victor Stinnerb6404912013-07-07 16:21:41 +0200347 PyMem_Malloc(sizeof(ProfilerContext));
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000348 if (pContext == NULL) {
349 pObj->flags |= POF_NOMEMORY;
350 goto restorePyerr;
351 }
352 }
353 initContext(pObj, pContext, profEntry);
Thomas Wouters89d996e2007-09-08 17:39:28 +0000354
355restorePyerr:
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000356 PyErr_Restore(last_type, last_value, last_tb);
Armin Rigoa871ef22006-02-08 12:53:56 +0000357}
358
359static void
360ptrace_leave_call(PyObject *self, void *key)
361{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000362 /* leaving a call to the function identified by 'key' */
363 ProfilerObject *pObj = (ProfilerObject*)self;
364 ProfilerEntry *profEntry;
365 ProfilerContext *pContext;
Armin Rigoa871ef22006-02-08 12:53:56 +0000366
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000367 pContext = pObj->currentProfilerContext;
368 if (pContext == NULL)
369 return;
370 profEntry = getEntry(pObj, key);
371 if (profEntry) {
372 Stop(pObj, pContext, profEntry);
373 }
374 else {
375 pObj->currentProfilerContext = pContext->previous;
376 }
377 /* put pContext into the free list */
378 pContext->previous = pObj->freelistProfilerContext;
379 pObj->freelistProfilerContext = pContext;
Armin Rigoa871ef22006-02-08 12:53:56 +0000380}
381
382static int
383profiler_callback(PyObject *self, PyFrameObject *frame, int what,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000384 PyObject *arg)
Armin Rigoa871ef22006-02-08 12:53:56 +0000385{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000386 switch (what) {
Armin Rigoa871ef22006-02-08 12:53:56 +0000387
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000388 /* the 'frame' of a called function is about to start its execution */
389 case PyTrace_CALL:
Victor Stinnera42ca742020-04-28 19:01:31 +0200390 {
391 PyCodeObject *code = PyFrame_GetCode(frame);
392 ptrace_enter_call(self, (void *)code, (PyObject *)code);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000393 break;
Victor Stinnera42ca742020-04-28 19:01:31 +0200394 }
Armin Rigoa871ef22006-02-08 12:53:56 +0000395
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000396 /* the 'frame' of a called function is about to finish
397 (either normally or with an exception) */
398 case PyTrace_RETURN:
Victor Stinnera42ca742020-04-28 19:01:31 +0200399 ptrace_leave_call(self, (void *)PyFrame_GetCode(frame));
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000400 break;
Armin Rigoa871ef22006-02-08 12:53:56 +0000401
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000402 /* case PyTrace_EXCEPTION:
403 If the exception results in the function exiting, a
404 PyTrace_RETURN event will be generated, so we don't need to
405 handle it. */
Armin Rigoa871ef22006-02-08 12:53:56 +0000406
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000407 /* the Python function 'frame' is issuing a call to the built-in
408 function 'arg' */
409 case PyTrace_C_CALL:
410 if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
411 && PyCFunction_Check(arg)) {
412 ptrace_enter_call(self,
413 ((PyCFunctionObject *)arg)->m_ml,
414 arg);
415 }
416 break;
Armin Rigoa871ef22006-02-08 12:53:56 +0000417
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000418 /* the call to the built-in function 'arg' is returning into its
419 caller 'frame' */
420 case PyTrace_C_RETURN: /* ...normally */
421 case PyTrace_C_EXCEPTION: /* ...with an exception set */
422 if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
423 && PyCFunction_Check(arg)) {
424 ptrace_leave_call(self,
425 ((PyCFunctionObject *)arg)->m_ml);
426 }
427 break;
Armin Rigoa871ef22006-02-08 12:53:56 +0000428
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000429 default:
430 break;
431 }
432 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000433}
434
435static int
436pending_exception(ProfilerObject *pObj)
437{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000438 if (pObj->flags & POF_NOMEMORY) {
439 pObj->flags -= POF_NOMEMORY;
440 PyErr_SetString(PyExc_MemoryError,
441 "memory was exhausted while profiling");
442 return -1;
443 }
444 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000445}
446
447/************************************************************/
448
449static PyStructSequence_Field profiler_entry_fields[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000450 {"code", "code object or built-in function name"},
451 {"callcount", "how many times this was called"},
452 {"reccallcount", "how many times called recursively"},
453 {"totaltime", "total time in this entry"},
454 {"inlinetime", "inline time in this entry (not in subcalls)"},
455 {"calls", "details of the calls"},
456 {0}
Armin Rigoa871ef22006-02-08 12:53:56 +0000457};
458
459static PyStructSequence_Field profiler_subentry_fields[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000460 {"code", "called code object or built-in function name"},
461 {"callcount", "how many times this is called"},
462 {"reccallcount", "how many times this is called recursively"},
463 {"totaltime", "total time spent in this call"},
464 {"inlinetime", "inline time (not in further subcalls)"},
465 {0}
Armin Rigoa871ef22006-02-08 12:53:56 +0000466};
467
468static PyStructSequence_Desc profiler_entry_desc = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000469 "_lsprof.profiler_entry", /* name */
470 NULL, /* doc */
471 profiler_entry_fields,
472 6
Armin Rigoa871ef22006-02-08 12:53:56 +0000473};
474
475static PyStructSequence_Desc profiler_subentry_desc = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000476 "_lsprof.profiler_subentry", /* name */
477 NULL, /* doc */
478 profiler_subentry_fields,
479 5
Armin Rigoa871ef22006-02-08 12:53:56 +0000480};
481
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000482static int initialized;
Armin Rigoa871ef22006-02-08 12:53:56 +0000483static PyTypeObject StatsEntryType;
484static PyTypeObject StatsSubEntryType;
485
486
487typedef struct {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000488 PyObject *list;
489 PyObject *sublist;
490 double factor;
Armin Rigoa871ef22006-02-08 12:53:56 +0000491} statscollector_t;
492
493static int statsForSubEntry(rotating_node_t *node, void *arg)
494{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000495 ProfilerSubEntry *sentry = (ProfilerSubEntry*) node;
496 statscollector_t *collect = (statscollector_t*) arg;
497 ProfilerEntry *entry = (ProfilerEntry*) sentry->header.key;
498 int err;
499 PyObject *sinfo;
500 sinfo = PyObject_CallFunction((PyObject*) &StatsSubEntryType,
501 "((Olldd))",
502 entry->userObj,
503 sentry->callcount,
504 sentry->recursivecallcount,
505 collect->factor * sentry->tt,
506 collect->factor * sentry->it);
507 if (sinfo == NULL)
508 return -1;
509 err = PyList_Append(collect->sublist, sinfo);
510 Py_DECREF(sinfo);
511 return err;
Armin Rigoa871ef22006-02-08 12:53:56 +0000512}
513
514static int statsForEntry(rotating_node_t *node, void *arg)
515{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000516 ProfilerEntry *entry = (ProfilerEntry*) node;
517 statscollector_t *collect = (statscollector_t*) arg;
518 PyObject *info;
519 int err;
520 if (entry->callcount == 0)
521 return 0; /* skip */
Armin Rigoa871ef22006-02-08 12:53:56 +0000522
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000523 if (entry->calls != EMPTY_ROTATING_TREE) {
524 collect->sublist = PyList_New(0);
525 if (collect->sublist == NULL)
526 return -1;
527 if (RotatingTree_Enum(entry->calls,
528 statsForSubEntry, collect) != 0) {
529 Py_DECREF(collect->sublist);
530 return -1;
531 }
532 }
533 else {
534 Py_INCREF(Py_None);
535 collect->sublist = Py_None;
536 }
Armin Rigoa871ef22006-02-08 12:53:56 +0000537
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000538 info = PyObject_CallFunction((PyObject*) &StatsEntryType,
539 "((OllddO))",
540 entry->userObj,
541 entry->callcount,
542 entry->recursivecallcount,
543 collect->factor * entry->tt,
544 collect->factor * entry->it,
545 collect->sublist);
546 Py_DECREF(collect->sublist);
547 if (info == NULL)
548 return -1;
549 err = PyList_Append(collect->list, info);
550 Py_DECREF(info);
551 return err;
Armin Rigoa871ef22006-02-08 12:53:56 +0000552}
553
554PyDoc_STRVAR(getstats_doc, "\
555getstats() -> list of profiler_entry objects\n\
556\n\
557Return all information collected by the profiler.\n\
558Each profiler_entry is a tuple-like object with the\n\
559following attributes:\n\
560\n\
561 code code object\n\
562 callcount how many times this was called\n\
563 reccallcount how many times called recursively\n\
564 totaltime total time in this entry\n\
565 inlinetime inline time in this entry (not in subcalls)\n\
566 calls details of the calls\n\
567\n\
568The calls attribute is either None or a list of\n\
569profiler_subentry objects:\n\
570\n\
571 code called code object\n\
572 callcount how many times this is called\n\
573 reccallcount how many times this is called recursively\n\
574 totaltime total time spent in this call\n\
575 inlinetime inline time (not in further subcalls)\n\
576");
577
578static PyObject*
579profiler_getstats(ProfilerObject *pObj, PyObject* noarg)
580{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000581 statscollector_t collect;
Victor Stinner309d7cc2020-03-13 16:39:12 +0100582 if (pending_exception(pObj)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000583 return NULL;
Victor Stinner309d7cc2020-03-13 16:39:12 +0100584 }
Inada Naoki536a35b2019-04-11 19:11:46 +0900585 if (!pObj->externalTimer || pObj->externalTimerUnit == 0.0) {
586 _PyTime_t onesec = _PyTime_FromSeconds(1);
587 collect.factor = (double)1 / onesec;
588 }
589 else {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000590 collect.factor = pObj->externalTimerUnit;
Inada Naoki536a35b2019-04-11 19:11:46 +0900591 }
592
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000593 collect.list = PyList_New(0);
594 if (collect.list == NULL)
595 return NULL;
596 if (RotatingTree_Enum(pObj->profilerEntries, statsForEntry, &collect)
597 != 0) {
598 Py_DECREF(collect.list);
599 return NULL;
600 }
601 return collect.list;
Armin Rigoa871ef22006-02-08 12:53:56 +0000602}
603
604static int
605setSubcalls(ProfilerObject *pObj, int nvalue)
606{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000607 if (nvalue == 0)
608 pObj->flags &= ~POF_SUBCALLS;
609 else if (nvalue > 0)
610 pObj->flags |= POF_SUBCALLS;
611 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000612}
613
614static int
615setBuiltins(ProfilerObject *pObj, int nvalue)
616{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000617 if (nvalue == 0)
618 pObj->flags &= ~POF_BUILTINS;
619 else if (nvalue > 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000620 pObj->flags |= POF_BUILTINS;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000621 }
622 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000623}
624
625PyDoc_STRVAR(enable_doc, "\
626enable(subcalls=True, builtins=True)\n\
627\n\
628Start collecting profiling information.\n\
629If 'subcalls' is True, also records for each function\n\
630statistics separated according to its current caller.\n\
631If 'builtins' is True, records the time spent in\n\
632built-in functions separately from their caller.\n\
633");
634
635static PyObject*
636profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
637{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000638 int subcalls = -1;
639 int builtins = -1;
640 static char *kwlist[] = {"subcalls", "builtins", 0};
641 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:enable",
642 kwlist, &subcalls, &builtins))
643 return NULL;
Victor Stinner309d7cc2020-03-13 16:39:12 +0100644 if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000645 return NULL;
Victor Stinner309d7cc2020-03-13 16:39:12 +0100646 }
647
648 PyThreadState *tstate = PyThreadState_GET();
649 if (_PyEval_SetProfile(tstate, profiler_callback, (PyObject*)self) < 0) {
650 return NULL;
651 }
652
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000653 self->flags |= POF_ENABLED;
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200654 Py_RETURN_NONE;
Armin Rigoa871ef22006-02-08 12:53:56 +0000655}
656
657static void
658flush_unmatched(ProfilerObject *pObj)
659{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000660 while (pObj->currentProfilerContext) {
661 ProfilerContext *pContext = pObj->currentProfilerContext;
662 ProfilerEntry *profEntry= pContext->ctxEntry;
663 if (profEntry)
664 Stop(pObj, pContext, profEntry);
665 else
666 pObj->currentProfilerContext = pContext->previous;
667 if (pContext)
Victor Stinnerb6404912013-07-07 16:21:41 +0200668 PyMem_Free(pContext);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000669 }
Armin Rigoa871ef22006-02-08 12:53:56 +0000670
671}
672
673PyDoc_STRVAR(disable_doc, "\
674disable()\n\
675\n\
676Stop collecting profiling information.\n\
677");
678
679static PyObject*
680profiler_disable(ProfilerObject *self, PyObject* noarg)
681{
Victor Stinner309d7cc2020-03-13 16:39:12 +0100682 PyThreadState *tstate = PyThreadState_GET();
683 if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000684 return NULL;
Victor Stinner309d7cc2020-03-13 16:39:12 +0100685 }
686 self->flags &= ~POF_ENABLED;
687
688 flush_unmatched(self);
689 if (pending_exception(self)) {
690 return NULL;
691 }
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200692 Py_RETURN_NONE;
Armin Rigoa871ef22006-02-08 12:53:56 +0000693}
694
695PyDoc_STRVAR(clear_doc, "\
696clear()\n\
697\n\
698Clear all profiling information collected so far.\n\
699");
700
701static PyObject*
702profiler_clear(ProfilerObject *pObj, PyObject* noarg)
703{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000704 clearEntries(pObj);
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200705 Py_RETURN_NONE;
Armin Rigoa871ef22006-02-08 12:53:56 +0000706}
707
708static void
709profiler_dealloc(ProfilerObject *op)
710{
Victor Stinner309d7cc2020-03-13 16:39:12 +0100711 if (op->flags & POF_ENABLED) {
712 PyThreadState *tstate = PyThreadState_GET();
713 if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
714 PyErr_WriteUnraisable((PyObject *)op);
715 }
716 }
717
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000718 flush_unmatched(op);
719 clearEntries(op);
720 Py_XDECREF(op->externalTimer);
721 Py_TYPE(op)->tp_free(op);
Armin Rigoa871ef22006-02-08 12:53:56 +0000722}
723
724static int
725profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
726{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000727 PyObject *timer = NULL;
728 double timeunit = 0.0;
729 int subcalls = 1;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000730 int builtins = 1;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000731 static char *kwlist[] = {"timer", "timeunit",
732 "subcalls", "builtins", 0};
Armin Rigoa871ef22006-02-08 12:53:56 +0000733
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000734 if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odii:Profiler", kwlist,
735 &timer, &timeunit,
736 &subcalls, &builtins))
737 return -1;
Armin Rigoa871ef22006-02-08 12:53:56 +0000738
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000739 if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
740 return -1;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000741 pObj->externalTimerUnit = timeunit;
Serhiy Storchaka576f1322016-01-05 21:27:54 +0200742 Py_XINCREF(timer);
Serhiy Storchakaec397562016-04-06 09:50:03 +0300743 Py_XSETREF(pObj->externalTimer, timer);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000744 return 0;
Armin Rigoa871ef22006-02-08 12:53:56 +0000745}
746
747static PyMethodDef profiler_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000748 {"getstats", (PyCFunction)profiler_getstats,
749 METH_NOARGS, getstats_doc},
Serhiy Storchaka62be7422018-11-27 13:27:31 +0200750 {"enable", (PyCFunction)(void(*)(void))profiler_enable,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000751 METH_VARARGS | METH_KEYWORDS, enable_doc},
752 {"disable", (PyCFunction)profiler_disable,
753 METH_NOARGS, disable_doc},
754 {"clear", (PyCFunction)profiler_clear,
755 METH_NOARGS, clear_doc},
756 {NULL, NULL}
Armin Rigoa871ef22006-02-08 12:53:56 +0000757};
758
759PyDoc_STRVAR(profiler_doc, "\
INADA Naoki2ebd3812018-08-03 18:09:57 +0900760Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\
Armin Rigoa871ef22006-02-08 12:53:56 +0000761\n\
762 Builds a profiler object using the specified timer function.\n\
763 The default timer is a fast built-in one based on real time.\n\
INADA Naoki2ebd3812018-08-03 18:09:57 +0900764 For custom timer functions returning integers, timeunit can\n\
Armin Rigoa871ef22006-02-08 12:53:56 +0000765 be a float specifying a scale (i.e. how long each integer unit\n\
766 is, in seconds).\n\
767");
768
Neal Norwitz227b5332006-03-22 09:28:35 +0000769static PyTypeObject PyProfiler_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000770 PyVarObject_HEAD_INIT(NULL, 0)
771 "_lsprof.Profiler", /* tp_name */
772 sizeof(ProfilerObject), /* tp_basicsize */
773 0, /* tp_itemsize */
774 (destructor)profiler_dealloc, /* tp_dealloc */
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200775 0, /* tp_vectorcall_offset */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000776 0, /* tp_getattr */
777 0, /* tp_setattr */
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200778 0, /* tp_as_async */
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000779 0, /* tp_repr */
780 0, /* tp_as_number */
781 0, /* tp_as_sequence */
782 0, /* tp_as_mapping */
783 0, /* tp_hash */
784 0, /* tp_call */
785 0, /* tp_str */
786 0, /* tp_getattro */
787 0, /* tp_setattro */
788 0, /* tp_as_buffer */
789 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
790 profiler_doc, /* tp_doc */
791 0, /* tp_traverse */
792 0, /* tp_clear */
793 0, /* tp_richcompare */
794 0, /* tp_weaklistoffset */
795 0, /* tp_iter */
796 0, /* tp_iternext */
797 profiler_methods, /* tp_methods */
798 0, /* tp_members */
799 0, /* tp_getset */
800 0, /* tp_base */
801 0, /* tp_dict */
802 0, /* tp_descr_get */
803 0, /* tp_descr_set */
804 0, /* tp_dictoffset */
805 (initproc)profiler_init, /* tp_init */
806 PyType_GenericAlloc, /* tp_alloc */
807 PyType_GenericNew, /* tp_new */
808 PyObject_Del, /* tp_free */
Armin Rigoa871ef22006-02-08 12:53:56 +0000809};
810
811static PyMethodDef moduleMethods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000812 {NULL, NULL}
Armin Rigoa871ef22006-02-08 12:53:56 +0000813};
814
Martin v. Löwis1a214512008-06-11 05:26:20 +0000815
816static struct PyModuleDef _lsprofmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000817 PyModuleDef_HEAD_INIT,
818 "_lsprof",
819 "Fast profiler",
820 -1,
821 moduleMethods,
822 NULL,
823 NULL,
824 NULL,
825 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000826};
827
Armin Rigoa871ef22006-02-08 12:53:56 +0000828PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +0000829PyInit__lsprof(void)
Armin Rigoa871ef22006-02-08 12:53:56 +0000830{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000831 PyObject *module, *d;
832 module = PyModule_Create(&_lsprofmodule);
833 if (module == NULL)
834 return NULL;
835 d = PyModule_GetDict(module);
836 if (PyType_Ready(&PyProfiler_Type) < 0)
837 return NULL;
838 PyDict_SetItemString(d, "Profiler", (PyObject *)&PyProfiler_Type);
Armin Rigoa871ef22006-02-08 12:53:56 +0000839
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000840 if (!initialized) {
Victor Stinner1c8f0592013-07-22 22:24:54 +0200841 if (PyStructSequence_InitType2(&StatsEntryType,
842 &profiler_entry_desc) < 0)
843 return NULL;
844 if (PyStructSequence_InitType2(&StatsSubEntryType,
845 &profiler_subentry_desc) < 0)
846 return NULL;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000847 }
848 Py_INCREF((PyObject*) &StatsEntryType);
849 Py_INCREF((PyObject*) &StatsSubEntryType);
850 PyModule_AddObject(module, "profiler_entry",
851 (PyObject*) &StatsEntryType);
852 PyModule_AddObject(module, "profiler_subentry",
853 (PyObject*) &StatsSubEntryType);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000854 initialized = 1;
855 return module;
Armin Rigoa871ef22006-02-08 12:53:56 +0000856}