ยปCore Development>Code coverage>Modules/_lsprof.c

Python code coverage for Modules/_lsprof.c

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