_lsprof.c 25.8 KB
Newer Older
Armin Rigo's avatar
Armin Rigo committed
1 2 3 4 5 6 7 8 9 10 11
#include "Python.h"
#include "frameobject.h"
#include "rotatingtree.h"

/************************************************************/
/* Written by Brett Rosen and Ted Czotter */

struct _ProfilerEntry;

/* represents a function called from another function */
typedef struct _ProfilerSubEntry {
12
    rotating_node_t header;
13 14
    _PyTime_t tt;
    _PyTime_t it;
15 16 17
    long callcount;
    long recursivecallcount;
    long recursionLevel;
Armin Rigo's avatar
Armin Rigo committed
18 19 20 21
} ProfilerSubEntry;

/* represents a function or user defined block */
typedef struct _ProfilerEntry {
22 23
    rotating_node_t header;
    PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
24 25
    _PyTime_t tt; /* total time in this entry */
    _PyTime_t it; /* inline time in this entry (not in subcalls) */
26 27 28 29
    long callcount; /* how many times this was called */
    long recursivecallcount; /* how many times called recursively */
    long recursionLevel;
    rotating_node_t *calls;
Armin Rigo's avatar
Armin Rigo committed
30 31 32
} ProfilerEntry;

typedef struct _ProfilerContext {
33 34
    _PyTime_t t0;
    _PyTime_t subt;
35 36
    struct _ProfilerContext *previous;
    ProfilerEntry *ctxEntry;
Armin Rigo's avatar
Armin Rigo committed
37 38 39
} ProfilerContext;

typedef struct {
40 41 42 43 44 45 46
    PyObject_HEAD
    rotating_node_t *profilerEntries;
    ProfilerContext *currentProfilerContext;
    ProfilerContext *freelistProfilerContext;
    int flags;
    PyObject *externalTimer;
    double externalTimerUnit;
Armin Rigo's avatar
Armin Rigo committed
47 48 49 50 51 52 53
} ProfilerObject;

#define POF_ENABLED     0x001
#define POF_SUBCALLS    0x002
#define POF_BUILTINS    0x004
#define POF_NOMEMORY    0x100

54
static PyTypeObject PyProfiler_Type;
Armin Rigo's avatar
Armin Rigo committed
55 56

#define PyProfiler_Check(op) PyObject_TypeCheck(op, &PyProfiler_Type)
57
#define PyProfiler_CheckExact(op) (Py_TYPE(op) == &PyProfiler_Type)
Armin Rigo's avatar
Armin Rigo committed
58 59 60

/*** External Timers ***/

61
static _PyTime_t CallExternalTimer(ProfilerObject *pObj)
Armin Rigo's avatar
Armin Rigo committed
62
{
63
    PyObject *o = _PyObject_CallNoArg(pObj->externalTimer);
64 65 66 67
    if (o == NULL) {
        PyErr_WriteUnraisable(pObj->externalTimer);
        return 0;
    }
68 69 70

    _PyTime_t result;
    int err;
71 72 73
    if (pObj->externalTimerUnit > 0.0) {
        /* interpret the result as an integer that will be scaled
           in profiler_getstats() */
74
        err = _PyTime_FromNanosecondsObject(&result, o);
75 76 77
    }
    else {
        /* interpret the result as a double measured in seconds.
78
           As the profiler works with _PyTime_t internally
79
           we convert it to a large integer */
80
        err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
81 82
    }
    Py_DECREF(o);
83
    if (err < 0) {
84 85 86 87
        PyErr_WriteUnraisable(pObj->externalTimer);
        return 0;
    }
    return result;
Armin Rigo's avatar
Armin Rigo committed
88 89
}

90 91 92 93 94 95 96 97 98 99 100
static inline _PyTime_t
call_timer(ProfilerObject *pObj)
{
    if (pObj->externalTimer != NULL) {
        return CallExternalTimer(pObj);
    }
    else {
        return _PyTime_GetPerfCounter();
    }
}

Armin Rigo's avatar
Armin Rigo committed
101 102 103 104 105 106

/*** ProfilerObject ***/

static PyObject *
normalizeUserObj(PyObject *obj)
{
107 108 109 110 111 112 113 114 115 116 117 118 119
    PyCFunctionObject *fn;
    if (!PyCFunction_Check(obj)) {
        Py_INCREF(obj);
        return obj;
    }
    /* Replace built-in function objects with a descriptive string
       because of built-in methods -- keeping a reference to
       __self__ is probably not a good idea. */
    fn = (PyCFunctionObject *)obj;

    if (fn->m_self == NULL) {
        /* built-in function: look up the module name */
        PyObject *mod = fn->m_module;
120 121 122 123 124
        PyObject *modname = NULL;
        if (mod != NULL) {
            if (PyUnicode_Check(mod)) {
                modname = mod;
                Py_INCREF(modname);
125
            }
126 127 128 129
            else if (PyModule_Check(mod)) {
                modname = PyModule_GetNameObject(mod);
                if (modname == NULL)
                    PyErr_Clear();
130 131
            }
        }
132
        if (modname != NULL) {
133
            if (!_PyUnicode_EqualToASCIIString(modname, "builtins")) {
134 135 136 137 138 139 140
                PyObject *result;
                result = PyUnicode_FromFormat("<%U.%s>", modname,
                                              fn->m_ml->ml_name);
                Py_DECREF(modname);
                return result;
            }
            Py_DECREF(modname);
141
        }
142
        return PyUnicode_FromFormat("<%s>", fn->m_ml->ml_name);
143 144 145 146 147 148 149
    }
    else {
        /* built-in method: try to return
            repr(getattr(type(__self__), __name__))
        */
        PyObject *self = fn->m_self;
        PyObject *name = PyUnicode_FromString(fn->m_ml->ml_name);
150 151
        PyObject *modname = fn->m_module;

152 153 154 155 156 157 158 159 160 161 162
        if (name != NULL) {
            PyObject *mo = _PyType_Lookup(Py_TYPE(self), name);
            Py_XINCREF(mo);
            Py_DECREF(name);
            if (mo != NULL) {
                PyObject *res = PyObject_Repr(mo);
                Py_DECREF(mo);
                if (res != NULL)
                    return res;
            }
        }
163
        /* Otherwise, use __module__ */
164
        PyErr_Clear();
165 166 167 168 169 170
        if (modname != NULL && PyUnicode_Check(modname))
            return PyUnicode_FromFormat("<built-in method %S.%s>",
                                        modname,  fn->m_ml->ml_name);
        else
            return PyUnicode_FromFormat("<built-in method %s>",
                                        fn->m_ml->ml_name);
171
    }
Armin Rigo's avatar
Armin Rigo committed
172 173 174 175 176
}

static ProfilerEntry*
newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj)
{
177
    ProfilerEntry *self;
178
    self = (ProfilerEntry*) PyMem_Malloc(sizeof(ProfilerEntry));
179 180 181 182 183 184 185
    if (self == NULL) {
        pObj->flags |= POF_NOMEMORY;
        return NULL;
    }
    userObj = normalizeUserObj(userObj);
    if (userObj == NULL) {
        PyErr_Clear();
186
        PyMem_Free(self);
187 188 189 190 191 192 193 194 195 196 197 198 199
        pObj->flags |= POF_NOMEMORY;
        return NULL;
    }
    self->header.key = key;
    self->userObj = userObj;
    self->tt = 0;
    self->it = 0;
    self->callcount = 0;
    self->recursivecallcount = 0;
    self->recursionLevel = 0;
    self->calls = EMPTY_ROTATING_TREE;
    RotatingTree_Add(&pObj->profilerEntries, &self->header);
    return self;
Armin Rigo's avatar
Armin Rigo committed
200 201 202 203 204
}

static ProfilerEntry*
getEntry(ProfilerObject *pObj, void *key)
{
205
    return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
Armin Rigo's avatar
Armin Rigo committed
206 207
}

208
static ProfilerSubEntry *
Armin Rigo's avatar
Armin Rigo committed
209 210
getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
{
211 212
    return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
                                                (void *)entry);
Armin Rigo's avatar
Armin Rigo committed
213 214 215 216 217
}

static ProfilerSubEntry *
newSubEntry(ProfilerObject *pObj,  ProfilerEntry *caller, ProfilerEntry* entry)
{
218
    ProfilerSubEntry *self;
219
    self = (ProfilerSubEntry*) PyMem_Malloc(sizeof(ProfilerSubEntry));
220 221 222 223 224 225 226 227 228 229 230 231
    if (self == NULL) {
        pObj->flags |= POF_NOMEMORY;
        return NULL;
    }
    self->header.key = (void *)entry;
    self->tt = 0;
    self->it = 0;
    self->callcount = 0;
    self->recursivecallcount = 0;
    self->recursionLevel = 0;
    RotatingTree_Add(&caller->calls, &self->header);
    return self;
Armin Rigo's avatar
Armin Rigo committed
232 233 234 235
}

static int freeSubEntry(rotating_node_t *header, void *arg)
{
236
    ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
237
    PyMem_Free(subentry);
238
    return 0;
Armin Rigo's avatar
Armin Rigo committed
239 240 241 242
}

static int freeEntry(rotating_node_t *header, void *arg)
{
243 244 245
    ProfilerEntry *entry = (ProfilerEntry*) header;
    RotatingTree_Enum(entry->calls, freeSubEntry, NULL);
    Py_DECREF(entry->userObj);
246
    PyMem_Free(entry);
247
    return 0;
Armin Rigo's avatar
Armin Rigo committed
248 249 250 251
}

static void clearEntries(ProfilerObject *pObj)
{
252 253 254 255
    RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
    pObj->profilerEntries = EMPTY_ROTATING_TREE;
    /* release the memory hold by the ProfilerContexts */
    if (pObj->currentProfilerContext) {
256
        PyMem_Free(pObj->currentProfilerContext);
257 258 259 260 261
        pObj->currentProfilerContext = NULL;
    }
    while (pObj->freelistProfilerContext) {
        ProfilerContext *c = pObj->freelistProfilerContext;
        pObj->freelistProfilerContext = c->previous;
262
        PyMem_Free(c);
263 264
    }
    pObj->freelistProfilerContext = NULL;
Armin Rigo's avatar
Armin Rigo committed
265 266 267 268 269
}

static void
initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
{
270 271 272 273 274 275 276 277 278 279 280 281 282 283
    self->ctxEntry = entry;
    self->subt = 0;
    self->previous = pObj->currentProfilerContext;
    pObj->currentProfilerContext = self;
    ++entry->recursionLevel;
    if ((pObj->flags & POF_SUBCALLS) && self->previous) {
        /* find or create an entry for me in my caller's entry */
        ProfilerEntry *caller = self->previous->ctxEntry;
        ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
        if (subentry == NULL)
            subentry = newSubEntry(pObj, caller, entry);
        if (subentry)
            ++subentry->recursionLevel;
    }
284
    self->t0 = call_timer(pObj);
Armin Rigo's avatar
Armin Rigo committed
285 286 287 288 289
}

static void
Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
{
290 291
    _PyTime_t tt = call_timer(pObj) - self->t0;
    _PyTime_t it = tt - self->subt;
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
    if (self->previous)
        self->previous->subt += tt;
    pObj->currentProfilerContext = self->previous;
    if (--entry->recursionLevel == 0)
        entry->tt += tt;
    else
        ++entry->recursivecallcount;
    entry->it += it;
    entry->callcount++;
    if ((pObj->flags & POF_SUBCALLS) && self->previous) {
        /* find or create an entry for me in my caller's entry */
        ProfilerEntry *caller = self->previous->ctxEntry;
        ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
        if (subentry) {
            if (--subentry->recursionLevel == 0)
                subentry->tt += tt;
            else
                ++subentry->recursivecallcount;
            subentry->it += it;
            ++subentry->callcount;
        }
    }
Armin Rigo's avatar
Armin Rigo committed
314 315 316 317 318
}

static void
ptrace_enter_call(PyObject *self, void *key, PyObject *userObj)
{
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    /* entering a call to the function identified by 'key'
       (which can be a PyCodeObject or a PyMethodDef pointer) */
    ProfilerObject *pObj = (ProfilerObject*)self;
    ProfilerEntry *profEntry;
    ProfilerContext *pContext;

    /* In the case of entering a generator expression frame via a
     * throw (gen_send_ex(.., 1)), we may already have an
     * Exception set here. We must not mess around with this
     * exception, and some of the code under here assumes that
     * PyErr_* is its own to mess around with, so we have to
     * save and restore any current exception. */
    PyObject *last_type, *last_value, *last_tb;
    PyErr_Fetch(&last_type, &last_value, &last_tb);

    profEntry = getEntry(pObj, key);
    if (profEntry == NULL) {
        profEntry = newProfilerEntry(pObj, key, userObj);
        if (profEntry == NULL)
            goto restorePyerr;
    }
    /* grab a ProfilerContext out of the free list */
    pContext = pObj->freelistProfilerContext;
    if (pContext) {
        pObj->freelistProfilerContext = pContext->previous;
    }
    else {
        /* free list exhausted, allocate a new one */
        pContext = (ProfilerContext*)
348
            PyMem_Malloc(sizeof(ProfilerContext));
349 350 351 352 353 354
        if (pContext == NULL) {
            pObj->flags |= POF_NOMEMORY;
            goto restorePyerr;
        }
    }
    initContext(pObj, pContext, profEntry);
355 356

restorePyerr:
357
    PyErr_Restore(last_type, last_value, last_tb);
Armin Rigo's avatar
Armin Rigo committed
358 359 360 361 362
}

static void
ptrace_leave_call(PyObject *self, void *key)
{
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    /* leaving a call to the function identified by 'key' */
    ProfilerObject *pObj = (ProfilerObject*)self;
    ProfilerEntry *profEntry;
    ProfilerContext *pContext;

    pContext = pObj->currentProfilerContext;
    if (pContext == NULL)
        return;
    profEntry = getEntry(pObj, key);
    if (profEntry) {
        Stop(pObj, pContext, profEntry);
    }
    else {
        pObj->currentProfilerContext = pContext->previous;
    }
    /* put pContext into the free list */
    pContext->previous = pObj->freelistProfilerContext;
    pObj->freelistProfilerContext = pContext;
Armin Rigo's avatar
Armin Rigo committed
381 382 383 384
}

static int
profiler_callback(PyObject *self, PyFrameObject *frame, int what,
385
                  PyObject *arg)
Armin Rigo's avatar
Armin Rigo committed
386
{
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
    switch (what) {

    /* the 'frame' of a called function is about to start its execution */
    case PyTrace_CALL:
        ptrace_enter_call(self, (void *)frame->f_code,
                                (PyObject *)frame->f_code);
        break;

    /* the 'frame' of a called function is about to finish
       (either normally or with an exception) */
    case PyTrace_RETURN:
        ptrace_leave_call(self, (void *)frame->f_code);
        break;

    /* case PyTrace_EXCEPTION:
        If the exception results in the function exiting, a
        PyTrace_RETURN event will be generated, so we don't need to
        handle it. */

    /* the Python function 'frame' is issuing a call to the built-in
       function 'arg' */
    case PyTrace_C_CALL:
        if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
            && PyCFunction_Check(arg)) {
            ptrace_enter_call(self,
                              ((PyCFunctionObject *)arg)->m_ml,
                              arg);
        }
        break;

    /* the call to the built-in function 'arg' is returning into its
       caller 'frame' */
    case PyTrace_C_RETURN:              /* ...normally */
    case PyTrace_C_EXCEPTION:           /* ...with an exception set */
        if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
            && PyCFunction_Check(arg)) {
            ptrace_leave_call(self,
                              ((PyCFunctionObject *)arg)->m_ml);
        }
        break;
Armin Rigo's avatar
Armin Rigo committed
427

428 429 430 431
    default:
        break;
    }
    return 0;
Armin Rigo's avatar
Armin Rigo committed
432 433 434 435 436
}

static int
pending_exception(ProfilerObject *pObj)
{
437 438 439 440 441 442 443
    if (pObj->flags & POF_NOMEMORY) {
        pObj->flags -= POF_NOMEMORY;
        PyErr_SetString(PyExc_MemoryError,
                        "memory was exhausted while profiling");
        return -1;
    }
    return 0;
Armin Rigo's avatar
Armin Rigo committed
444 445 446 447 448
}

/************************************************************/

static PyStructSequence_Field profiler_entry_fields[] = {
449 450 451 452 453 454 455
    {"code",         "code object or built-in function name"},
    {"callcount",    "how many times this was called"},
    {"reccallcount", "how many times called recursively"},
    {"totaltime",    "total time in this entry"},
    {"inlinetime",   "inline time in this entry (not in subcalls)"},
    {"calls",        "details of the calls"},
    {0}
Armin Rigo's avatar
Armin Rigo committed
456 457 458
};

static PyStructSequence_Field profiler_subentry_fields[] = {
459 460 461 462 463 464
    {"code",         "called code object or built-in function name"},
    {"callcount",    "how many times this is called"},
    {"reccallcount", "how many times this is called recursively"},
    {"totaltime",    "total time spent in this call"},
    {"inlinetime",   "inline time (not in further subcalls)"},
    {0}
Armin Rigo's avatar
Armin Rigo committed
465 466 467
};

static PyStructSequence_Desc profiler_entry_desc = {
468 469 470 471
    "_lsprof.profiler_entry", /* name */
    NULL, /* doc */
    profiler_entry_fields,
    6
Armin Rigo's avatar
Armin Rigo committed
472 473 474
};

static PyStructSequence_Desc profiler_subentry_desc = {
475 476 477 478
    "_lsprof.profiler_subentry", /* name */
    NULL, /* doc */
    profiler_subentry_fields,
    5
Armin Rigo's avatar
Armin Rigo committed
479 480
};

481
static int initialized;
Armin Rigo's avatar
Armin Rigo committed
482 483 484 485 486
static PyTypeObject StatsEntryType;
static PyTypeObject StatsSubEntryType;


typedef struct {
487 488 489
    PyObject *list;
    PyObject *sublist;
    double factor;
Armin Rigo's avatar
Armin Rigo committed
490 491 492 493
} statscollector_t;

static int statsForSubEntry(rotating_node_t *node, void *arg)
{
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
    ProfilerSubEntry *sentry = (ProfilerSubEntry*) node;
    statscollector_t *collect = (statscollector_t*) arg;
    ProfilerEntry *entry = (ProfilerEntry*) sentry->header.key;
    int err;
    PyObject *sinfo;
    sinfo = PyObject_CallFunction((PyObject*) &StatsSubEntryType,
                                  "((Olldd))",
                                  entry->userObj,
                                  sentry->callcount,
                                  sentry->recursivecallcount,
                                  collect->factor * sentry->tt,
                                  collect->factor * sentry->it);
    if (sinfo == NULL)
        return -1;
    err = PyList_Append(collect->sublist, sinfo);
    Py_DECREF(sinfo);
    return err;
Armin Rigo's avatar
Armin Rigo committed
511 512 513 514
}

static int statsForEntry(rotating_node_t *node, void *arg)
{
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    ProfilerEntry *entry = (ProfilerEntry*) node;
    statscollector_t *collect = (statscollector_t*) arg;
    PyObject *info;
    int err;
    if (entry->callcount == 0)
        return 0;   /* skip */

    if (entry->calls != EMPTY_ROTATING_TREE) {
        collect->sublist = PyList_New(0);
        if (collect->sublist == NULL)
            return -1;
        if (RotatingTree_Enum(entry->calls,
                              statsForSubEntry, collect) != 0) {
            Py_DECREF(collect->sublist);
            return -1;
        }
    }
    else {
        Py_INCREF(Py_None);
        collect->sublist = Py_None;
    }

    info = PyObject_CallFunction((PyObject*) &StatsEntryType,
                                 "((OllddO))",
                                 entry->userObj,
                                 entry->callcount,
                                 entry->recursivecallcount,
                                 collect->factor * entry->tt,
                                 collect->factor * entry->it,
                                 collect->sublist);
    Py_DECREF(collect->sublist);
    if (info == NULL)
        return -1;
    err = PyList_Append(collect->list, info);
    Py_DECREF(info);
    return err;
Armin Rigo's avatar
Armin Rigo committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
}

PyDoc_STRVAR(getstats_doc, "\
getstats() -> list of profiler_entry objects\n\
\n\
Return all information collected by the profiler.\n\
Each profiler_entry is a tuple-like object with the\n\
following attributes:\n\
\n\
    code          code object\n\
    callcount     how many times this was called\n\
    reccallcount  how many times called recursively\n\
    totaltime     total time in this entry\n\
    inlinetime    inline time in this entry (not in subcalls)\n\
    calls         details of the calls\n\
\n\
The calls attribute is either None or a list of\n\
profiler_subentry objects:\n\
\n\
    code          called code object\n\
    callcount     how many times this is called\n\
    reccallcount  how many times this is called recursively\n\
    totaltime     total time spent in this call\n\
    inlinetime    inline time (not in further subcalls)\n\
");

static PyObject*
profiler_getstats(ProfilerObject *pObj, PyObject* noarg)
{
580 581 582
    statscollector_t collect;
    if (pending_exception(pObj))
        return NULL;
583 584 585 586 587
    if (!pObj->externalTimer || pObj->externalTimerUnit == 0.0) {
        _PyTime_t onesec = _PyTime_FromSeconds(1);
        collect.factor = (double)1 / onesec;
    }
    else {
588
        collect.factor = pObj->externalTimerUnit;
589 590
    }

591 592 593 594 595 596 597 598 599
    collect.list = PyList_New(0);
    if (collect.list == NULL)
        return NULL;
    if (RotatingTree_Enum(pObj->profilerEntries, statsForEntry, &collect)
        != 0) {
        Py_DECREF(collect.list);
        return NULL;
    }
    return collect.list;
Armin Rigo's avatar
Armin Rigo committed
600 601 602 603 604
}

static int
setSubcalls(ProfilerObject *pObj, int nvalue)
{
605 606 607 608 609
    if (nvalue == 0)
        pObj->flags &= ~POF_SUBCALLS;
    else if (nvalue > 0)
        pObj->flags |=  POF_SUBCALLS;
    return 0;
Armin Rigo's avatar
Armin Rigo committed
610 611 612 613 614
}

static int
setBuiltins(ProfilerObject *pObj, int nvalue)
{
615 616 617 618 619 620
    if (nvalue == 0)
        pObj->flags &= ~POF_BUILTINS;
    else if (nvalue > 0) {
        pObj->flags |=  POF_BUILTINS;
    }
    return 0;
Armin Rigo's avatar
Armin Rigo committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
}

PyDoc_STRVAR(enable_doc, "\
enable(subcalls=True, builtins=True)\n\
\n\
Start collecting profiling information.\n\
If 'subcalls' is True, also records for each function\n\
statistics separated according to its current caller.\n\
If 'builtins' is True, records the time spent in\n\
built-in functions separately from their caller.\n\
");

static PyObject*
profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
{
636 637 638 639 640 641 642 643 644 645
    int subcalls = -1;
    int builtins = -1;
    static char *kwlist[] = {"subcalls", "builtins", 0};
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:enable",
                                     kwlist, &subcalls, &builtins))
        return NULL;
    if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0)
        return NULL;
    PyEval_SetProfile(profiler_callback, (PyObject*)self);
    self->flags |= POF_ENABLED;
646
    Py_RETURN_NONE;
Armin Rigo's avatar
Armin Rigo committed
647 648 649 650 651
}

static void
flush_unmatched(ProfilerObject *pObj)
{
652 653 654 655 656 657 658 659
    while (pObj->currentProfilerContext) {
        ProfilerContext *pContext = pObj->currentProfilerContext;
        ProfilerEntry *profEntry= pContext->ctxEntry;
        if (profEntry)
            Stop(pObj, pContext, profEntry);
        else
            pObj->currentProfilerContext = pContext->previous;
        if (pContext)
660
            PyMem_Free(pContext);
661
    }
Armin Rigo's avatar
Armin Rigo committed
662 663 664 665 666 667 668 669 670 671 672 673

}

PyDoc_STRVAR(disable_doc, "\
disable()\n\
\n\
Stop collecting profiling information.\n\
");

static PyObject*
profiler_disable(ProfilerObject *self, PyObject* noarg)
{
674 675 676 677 678
    self->flags &= ~POF_ENABLED;
    PyEval_SetProfile(NULL, NULL);
    flush_unmatched(self);
    if (pending_exception(self))
        return NULL;
679
    Py_RETURN_NONE;
Armin Rigo's avatar
Armin Rigo committed
680 681 682 683 684 685 686 687 688 689 690
}

PyDoc_STRVAR(clear_doc, "\
clear()\n\
\n\
Clear all profiling information collected so far.\n\
");

static PyObject*
profiler_clear(ProfilerObject *pObj, PyObject* noarg)
{
691
    clearEntries(pObj);
692
    Py_RETURN_NONE;
Armin Rigo's avatar
Armin Rigo committed
693 694 695 696 697
}

static void
profiler_dealloc(ProfilerObject *op)
{
698 699 700 701 702 703
    if (op->flags & POF_ENABLED)
        PyEval_SetProfile(NULL, NULL);
    flush_unmatched(op);
    clearEntries(op);
    Py_XDECREF(op->externalTimer);
    Py_TYPE(op)->tp_free(op);
Armin Rigo's avatar
Armin Rigo committed
704 705 706 707 708
}

static int
profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
{
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
    PyObject *timer = NULL;
    double timeunit = 0.0;
    int subcalls = 1;
    int builtins = 1;
    static char *kwlist[] = {"timer", "timeunit",
                                   "subcalls", "builtins", 0};

    if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odii:Profiler", kwlist,
                                     &timer, &timeunit,
                                     &subcalls, &builtins))
        return -1;

    if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
        return -1;
    pObj->externalTimerUnit = timeunit;
724
    Py_XINCREF(timer);
725
    Py_XSETREF(pObj->externalTimer, timer);
726
    return 0;
Armin Rigo's avatar
Armin Rigo committed
727 728 729
}

static PyMethodDef profiler_methods[] = {
730 731
    {"getstats",    (PyCFunction)profiler_getstats,
                    METH_NOARGS,                        getstats_doc},
732
    {"enable",          (PyCFunction)(void(*)(void))profiler_enable,
733 734 735 736 737 738
                    METH_VARARGS | METH_KEYWORDS,       enable_doc},
    {"disable",         (PyCFunction)profiler_disable,
                    METH_NOARGS,                        disable_doc},
    {"clear",           (PyCFunction)profiler_clear,
                    METH_NOARGS,                        clear_doc},
    {NULL, NULL}
Armin Rigo's avatar
Armin Rigo committed
739 740 741
};

PyDoc_STRVAR(profiler_doc, "\
742
Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\
Armin Rigo's avatar
Armin Rigo committed
743 744 745
\n\
    Builds a profiler object using the specified timer function.\n\
    The default timer is a fast built-in one based on real time.\n\
746
    For custom timer functions returning integers, timeunit can\n\
Armin Rigo's avatar
Armin Rigo committed
747 748 749 750
    be a float specifying a scale (i.e. how long each integer unit\n\
    is, in seconds).\n\
");

751
static PyTypeObject PyProfiler_Type = {
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
    PyVarObject_HEAD_INIT(NULL, 0)
    "_lsprof.Profiler",                     /* tp_name */
    sizeof(ProfilerObject),                 /* tp_basicsize */
    0,                                      /* tp_itemsize */
    (destructor)profiler_dealloc,           /* tp_dealloc */
    0,                                      /* tp_print */
    0,                                      /* tp_getattr */
    0,                                      /* tp_setattr */
    0,                                      /* tp_reserved */
    0,                                      /* tp_repr */
    0,                                      /* tp_as_number */
    0,                                      /* tp_as_sequence */
    0,                                      /* tp_as_mapping */
    0,                                      /* tp_hash */
    0,                                      /* tp_call */
    0,                                      /* tp_str */
    0,                                      /* tp_getattro */
    0,                                      /* tp_setattro */
    0,                                      /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    profiler_doc,                           /* tp_doc */
    0,                                      /* tp_traverse */
    0,                                      /* tp_clear */
    0,                                      /* tp_richcompare */
    0,                                      /* tp_weaklistoffset */
    0,                                      /* tp_iter */
    0,                                      /* tp_iternext */
    profiler_methods,                       /* tp_methods */
    0,                                      /* tp_members */
    0,                                      /* tp_getset */
    0,                                      /* tp_base */
    0,                                      /* tp_dict */
    0,                                      /* tp_descr_get */
    0,                                      /* tp_descr_set */
    0,                                      /* tp_dictoffset */
    (initproc)profiler_init,                /* tp_init */
    PyType_GenericAlloc,                    /* tp_alloc */
    PyType_GenericNew,                      /* tp_new */
    PyObject_Del,                           /* tp_free */
Armin Rigo's avatar
Armin Rigo committed
791 792 793
};

static PyMethodDef moduleMethods[] = {
794
    {NULL, NULL}
Armin Rigo's avatar
Armin Rigo committed
795 796
};

797 798

static struct PyModuleDef _lsprofmodule = {
799 800 801 802 803 804 805 806 807
    PyModuleDef_HEAD_INIT,
    "_lsprof",
    "Fast profiler",
    -1,
    moduleMethods,
    NULL,
    NULL,
    NULL,
    NULL
808 809
};

Armin Rigo's avatar
Armin Rigo committed
810
PyMODINIT_FUNC
811
PyInit__lsprof(void)
Armin Rigo's avatar
Armin Rigo committed
812
{
813 814 815 816 817 818 819 820 821 822
    PyObject *module, *d;
    module = PyModule_Create(&_lsprofmodule);
    if (module == NULL)
        return NULL;
    d = PyModule_GetDict(module);
    if (PyType_Ready(&PyProfiler_Type) < 0)
        return NULL;
    PyDict_SetItemString(d, "Profiler", (PyObject *)&PyProfiler_Type);

    if (!initialized) {
823 824 825 826 827 828
        if (PyStructSequence_InitType2(&StatsEntryType,
                                       &profiler_entry_desc) < 0)
            return NULL;
        if (PyStructSequence_InitType2(&StatsSubEntryType,
                                       &profiler_subentry_desc) < 0)
            return NULL;
829 830 831 832 833 834 835 836 837
    }
    Py_INCREF((PyObject*) &StatsEntryType);
    Py_INCREF((PyObject*) &StatsSubEntryType);
    PyModule_AddObject(module, "profiler_entry",
                       (PyObject*) &StatsEntryType);
    PyModule_AddObject(module, "profiler_subentry",
                       (PyObject*) &StatsSubEntryType);
    initialized = 1;
    return module;
Armin Rigo's avatar
Armin Rigo committed
838
}