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