#include "Python.h"#include "compile.h"#include "frameobject.h"#include "structseq.h"#include "rotatingtree.h"#if !defined(HAVE_LONG_LONG)#error "This module requires long longs!"#endif/*** Selection of a high-precision timer ***/#ifdef MS_WINDOWS#include <windows.h>staticPY_LONG_LONGhpTimer(void){LARGE_INTEGERli;QueryPerformanceCounter(&li);returnli.QuadPart;}staticdoublehpTimerUnit(void){LARGE_INTEGERli;if(QueryPerformanceFrequency(&li))return1.0/li.QuadPart;elsereturn0.000001;/* unlikely */}#else /* !MS_WINDOWS */#ifndef HAVE_GETTIMEOFDAY#error "This module requires gettimeofday() on non-Windows platforms!"#endif#if (defined(PYOS_OS2) && defined(PYCC_GCC))#include <sys/time.h>#else#include <sys/resource.h>#include <sys/times.h>#endifstaticPY_LONG_LONGhpTimer(void){structtimevaltv;PY_LONG_LONGret;#ifdef GETTIMEOFDAY_NO_TZgettimeofday(&tv);#elsegettimeofday(&tv,(structtimezone*)NULL);#endifret=tv.tv_sec;ret=ret*1000000+tv.tv_usec;returnret;}staticdoublehpTimerUnit(void){return0.000001;}#endif /* MS_WINDOWS *//************************************************************//* Written by Brett Rosen and Ted Czotter */struct_ProfilerEntry;/* represents a function called from another function */typedefstruct_ProfilerSubEntry{rotating_node_theader;PY_LONG_LONGtt;PY_LONG_LONGit;longcallcount;longrecursivecallcount;longrecursionLevel;}ProfilerSubEntry;/* represents a function or user defined block */typedefstruct_ProfilerEntry{rotating_node_theader;PyObject*userObj;/* PyCodeObject, or a descriptive str for builtins */PY_LONG_LONGtt;/* total time in this entry */PY_LONG_LONGit;/* inline time in this entry (not in subcalls) */longcallcount;/* how many times this was called */longrecursivecallcount;/* how many times called recursively */longrecursionLevel;rotating_node_t*calls;}ProfilerEntry;typedefstruct_ProfilerContext{PY_LONG_LONGt0;PY_LONG_LONGsubt;struct_ProfilerContext*previous;ProfilerEntry*ctxEntry;}ProfilerContext;typedefstruct{PyObject_HEADrotating_node_t*profilerEntries;ProfilerContext*currentProfilerContext;ProfilerContext*freelistProfilerContext;intflags;PyObject*externalTimer;doubleexternalTimerUnit;}ProfilerObject;#define POF_ENABLED 0x001#define POF_SUBCALLS 0x002#define POF_BUILTINS 0x004#define POF_NOMEMORY 0x100staticforwardPyTypeObjectPyProfiler_Type;#define PyProfiler_Check(op) PyObject_TypeCheck(op, &PyProfiler_Type)#define PyProfiler_CheckExact(op) (Py_TYPE(op) == &PyProfiler_Type)/*** External Timers ***/#define DOUBLE_TIMER_PRECISION 4294967296.0staticPyObject*empty_tuple;staticPY_LONG_LONGCallExternalTimer(ProfilerObject*pObj){PY_LONG_LONGresult;PyObject*o=PyObject_Call(pObj->externalTimer,empty_tuple,NULL);if(o==NULL){PyErr_WriteUnraisable(pObj->externalTimer);return0;}if(pObj->externalTimerUnit>0.0){/* interpret the result as an integer that will be scaled in profiler_getstats() */result=PyLong_AsLongLong(o);}else{/* interpret the result as a double measured in seconds. As the profiler works with PY_LONG_LONG internally we convert it to a large integer */doubleval=PyFloat_AsDouble(o);/* error handling delayed to the code below */result=(PY_LONG_LONG)(val*DOUBLE_TIMER_PRECISION);}Py_DECREF(o);if(PyErr_Occurred()){PyErr_WriteUnraisable((PyObject*)pObj);return0;}returnresult;}#define CALL_TIMER(pObj) ((pObj)->externalTimer ? \ CallExternalTimer(pObj) : \ hpTimer())/*** ProfilerObject ***/staticPyObject*normalizeUserObj(PyObject*obj){PyCFunctionObject*fn;if(!PyCFunction_Check(obj)){Py_INCREF(obj);returnobj;}/* 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;char*modname;if(mod&&PyString_Check(mod)){modname=PyString_AS_STRING(mod);}elseif(mod&&PyModule_Check(mod)){modname=PyModule_GetName(mod);if(modname==NULL){PyErr_Clear();modname="__builtin__";}}else{modname="__builtin__";}if(strcmp(modname,"__builtin__")!=0)returnPyString_FromFormat("<%s.%s>",modname,fn->m_ml->ml_name);elsereturnPyString_FromFormat("<%s>",fn->m_ml->ml_name);}else{/* built-in method: try to return repr(getattr(type(__self__), __name__)) */PyObject*self=fn->m_self;PyObject*name=PyString_FromString(fn->m_ml->ml_name);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)returnres;}}PyErr_Clear();returnPyString_FromFormat("<built-in method %s>",fn->m_ml->ml_name);}}staticProfilerEntry*newProfilerEntry(ProfilerObject*pObj,void*key,PyObject*userObj){ProfilerEntry*self;self=(ProfilerEntry*)malloc(sizeof(ProfilerEntry));if(self==NULL){pObj->flags|=POF_NOMEMORY;returnNULL;}userObj=normalizeUserObj(userObj);if(userObj==NULL){PyErr_Clear();free(self);pObj->flags|=POF_NOMEMORY;returnNULL;}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);returnself;}staticProfilerEntry*getEntry(ProfilerObject*pObj,void*key){return(ProfilerEntry*)RotatingTree_Get(&pObj->profilerEntries,key);}staticProfilerSubEntry*getSubEntry(ProfilerObject*pObj,ProfilerEntry*caller,ProfilerEntry*entry){return(ProfilerSubEntry*)RotatingTree_Get(&caller->calls,(void*)entry);}staticProfilerSubEntry*newSubEntry(ProfilerObject*pObj,ProfilerEntry*caller,ProfilerEntry*entry){ProfilerSubEntry*self;self=(ProfilerSubEntry*)malloc(sizeof(ProfilerSubEntry));if(self==NULL){pObj->flags|=POF_NOMEMORY;returnNULL;}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);returnself;}staticintfreeSubEntry(rotating_node_t*header,void*arg){ProfilerSubEntry*subentry=(ProfilerSubEntry*)header;free(subentry);return0;}staticintfreeEntry(rotating_node_t*header,void*arg){ProfilerEntry*entry=(ProfilerEntry*)header;RotatingTree_Enum(entry->calls,freeSubEntry,NULL);Py_DECREF(entry->userObj);free(entry);return0;}staticvoidclearEntries(ProfilerObject*pObj){RotatingTree_Enum(pObj->profilerEntries,freeEntry,NULL);pObj->profilerEntries=EMPTY_ROTATING_TREE;/* release the memory hold by the free list of ProfilerContexts */while(pObj->freelistProfilerContext){ProfilerContext*c=pObj->freelistProfilerContext;pObj->freelistProfilerContext=c->previous;free(c);}}staticvoidinitContext(ProfilerObject*pObj,ProfilerContext*self,ProfilerEntry*entry){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;}self->t0=CALL_TIMER(pObj);}staticvoidStop(ProfilerObject*pObj,ProfilerContext*self,ProfilerEntry*entry){PY_LONG_LONGtt=CALL_TIMER(pObj)-self->t0;PY_LONG_LONGit=tt-self->subt;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;}}}staticvoidptrace_enter_call(PyObject*self,void*key,PyObject*userObj){/* 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)gotorestorePyerr;}/* 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*)malloc(sizeof(ProfilerContext));if(pContext==NULL){pObj->flags|=POF_NOMEMORY;gotorestorePyerr;}}initContext(pObj,pContext,profEntry);restorePyerr:PyErr_Restore(last_type,last_value,last_tb);}staticvoidptrace_leave_call(PyObject*self,void*key){/* 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;}staticintprofiler_callback(PyObject*self,PyFrameObject*frame,intwhat,PyObject*arg){switch(what){/* the 'frame' of a called function is about to start its execution */casePyTrace_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) */casePyTrace_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. */#ifdef PyTrace_C_CALL /* not defined in Python <= 2.3 *//* the Python function 'frame' is issuing a call to the built-in function 'arg' */casePyTrace_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' */casePyTrace_C_RETURN:/* ...normally */casePyTrace_C_EXCEPTION:/* ...with an exception set */if((((ProfilerObject*)self)->flags&POF_BUILTINS)&&PyCFunction_Check(arg)){ptrace_leave_call(self,((PyCFunctionObject*)arg)->m_ml);}break;#endifdefault:break;}return0;}staticintpending_exception(ProfilerObject*pObj){if(pObj->flags&POF_NOMEMORY){pObj->flags-=POF_NOMEMORY;PyErr_SetString(PyExc_MemoryError,"memory was exhausted while profiling");return-1;}return0;}/************************************************************/staticPyStructSequence_Fieldprofiler_entry_fields[]={{"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}};staticPyStructSequence_Fieldprofiler_subentry_fields[]={{"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}};staticPyStructSequence_Descprofiler_entry_desc={"_lsprof.profiler_entry",/* name */NULL,/* doc */profiler_entry_fields,6};staticPyStructSequence_Descprofiler_subentry_desc={"_lsprof.profiler_subentry",/* name */NULL,/* doc */profiler_subentry_fields,5};staticintinitialized;staticPyTypeObjectStatsEntryType;staticPyTypeObjectStatsSubEntryType;typedefstruct{PyObject*list;PyObject*sublist;doublefactor;}statscollector_t;staticintstatsForSubEntry(rotating_node_t*node,void*arg){ProfilerSubEntry*sentry=(ProfilerSubEntry*)node;statscollector_t*collect=(statscollector_t*)arg;ProfilerEntry*entry=(ProfilerEntry*)sentry->header.key;interr;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);returnerr;}staticintstatsForEntry(rotating_node_t*node,void*arg){ProfilerEntry*entry=(ProfilerEntry*)node;statscollector_t*collect=(statscollector_t*)arg;PyObject*info;interr;if(entry->callcount==0)return0;/* 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);returnerr;}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\");staticPyObject*profiler_getstats(ProfilerObject*pObj,PyObject*noarg){statscollector_tcollect;if(pending_exception(pObj))returnNULL;if(!pObj->externalTimer)collect.factor=hpTimerUnit();elseif(pObj->externalTimerUnit>0.0)collect.factor=pObj->externalTimerUnit;elsecollect.factor=1.0/DOUBLE_TIMER_PRECISION;collect.list=PyList_New(0);if(collect.list==NULL)returnNULL;if(RotatingTree_Enum(pObj->profilerEntries,statsForEntry,&collect)!=0){Py_DECREF(collect.list);returnNULL;}returncollect.list;}staticintsetSubcalls(ProfilerObject*pObj,intnvalue){if(nvalue==0)pObj->flags&=~POF_SUBCALLS;elseif(nvalue>0)pObj->flags|=POF_SUBCALLS;return0;}staticintsetBuiltins(ProfilerObject*pObj,intnvalue){if(nvalue==0)pObj->flags&=~POF_BUILTINS;elseif(nvalue>0){#ifndef PyTrace_C_CALLPyErr_SetString(PyExc_ValueError,"builtins=True requires Python >= 2.4");return-1;#elsepObj->flags|=POF_BUILTINS;#endif}return0;}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\");staticPyObject*profiler_enable(ProfilerObject*self,PyObject*args,PyObject*kwds){intsubcalls=-1;intbuiltins=-1;staticchar*kwlist[]={"subcalls","builtins",0};if(!PyArg_ParseTupleAndKeywords(args,kwds,"|ii:enable",kwlist,&subcalls,&builtins))returnNULL;if(setSubcalls(self,subcalls)<0||setBuiltins(self,builtins)<0)returnNULL;PyEval_SetProfile(profiler_callback,(PyObject*)self);self->flags|=POF_ENABLED;Py_INCREF(Py_None);returnPy_None;}staticvoidflush_unmatched(ProfilerObject*pObj){while(pObj->currentProfilerContext){ProfilerContext*pContext=pObj->currentProfilerContext;ProfilerEntry*profEntry=pContext->ctxEntry;if(profEntry)Stop(pObj,pContext,profEntry);elsepObj->currentProfilerContext=pContext->previous;if(pContext)free(pContext);}}PyDoc_STRVAR(disable_doc,"\disable()\n\\n\Stop collecting profiling information.\n\");staticPyObject*profiler_disable(ProfilerObject*self,PyObject*noarg){self->flags&=~POF_ENABLED;PyEval_SetProfile(NULL,NULL);flush_unmatched(self);if(pending_exception(self))returnNULL;Py_INCREF(Py_None);returnPy_None;}PyDoc_STRVAR(clear_doc,"\clear()\n\\n\Clear all profiling information collected so far.\n\");staticPyObject*profiler_clear(ProfilerObject*pObj,PyObject*noarg){clearEntries(pObj);Py_INCREF(Py_None);returnPy_None;}staticvoidprofiler_dealloc(ProfilerObject*op){if(op->flags&POF_ENABLED)PyEval_SetProfile(NULL,NULL);flush_unmatched(op);clearEntries(op);Py_XDECREF(op->externalTimer);Py_TYPE(op)->tp_free(op);}staticintprofiler_init(ProfilerObject*pObj,PyObject*args,PyObject*kw){PyObject*o;PyObject*timer=NULL;doubletimeunit=0.0;intsubcalls=1;#ifdef PyTrace_C_CALLintbuiltins=1;#elseintbuiltins=0;#endifstaticchar*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;o=pObj->externalTimer;pObj->externalTimer=timer;Py_XINCREF(timer);Py_XDECREF(o);pObj->externalTimerUnit=timeunit;return0;}staticPyMethodDefprofiler_methods[]={{"getstats",(PyCFunction)profiler_getstats,METH_NOARGS,getstats_doc},{"enable",(PyCFunction)profiler_enable,METH_VARARGS|METH_KEYWORDS,enable_doc},{"disable",(PyCFunction)profiler_disable,METH_NOARGS,disable_doc},{"clear",(PyCFunction)profiler_clear,METH_NOARGS,clear_doc},{NULL,NULL}};PyDoc_STRVAR(profiler_doc,"\Profiler(custom_timer=None, time_unit=None, subcalls=True, builtins=True)\n\\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\ For custom timer functions returning integers, time_unit can\n\ be a float specifying a scale (i.e. how long each integer unit\n\ is, in seconds).\n\");staticherePyTypeObjectPyProfiler_Type={PyObject_HEAD_INIT(NULL)0,/* ob_size */"_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_compare */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 */};staticPyMethodDefmoduleMethods[]={{NULL,NULL}};PyMODINIT_FUNCinit_lsprof(void){PyObject*module,*d;module=Py_InitModule3("_lsprof",moduleMethods,"Fast profiler");if(module==NULL)return;d=PyModule_GetDict(module);if(PyType_Ready(&PyProfiler_Type)<0)return;PyDict_SetItemString(d,"Profiler",(PyObject*)&PyProfiler_Type);if(!initialized){PyStructSequence_InitType(&StatsEntryType,&profiler_entry_desc);PyStructSequence_InitType(&StatsSubEntryType,&profiler_subentry_desc);}Py_INCREF((PyObject*)&StatsEntryType);Py_INCREF((PyObject*)&StatsSubEntryType);PyModule_AddObject(module,"profiler_entry",(PyObject*)&StatsEntryType);PyModule_AddObject(module,"profiler_subentry",(PyObject*)&StatsSubEntryType);empty_tuple=PyTuple_New(0);initialized=1;}