@@ -49,6 +49,8 @@ typedef struct {
4949 int flags ;
5050 PyObject * externalTimer ;
5151 double externalTimerUnit ;
52+ int tool_id ;
53+ PyObject * missing ;
5254} ProfilerObject ;
5355
5456#define POF_ENABLED 0x001
@@ -399,64 +401,6 @@ ptrace_leave_call(PyObject *self, void *key)
399401 pObj -> freelistProfilerContext = pContext ;
400402}
401403
402- static int
403- profiler_callback (PyObject * self , PyFrameObject * frame , int what ,
404- PyObject * arg )
405- {
406- switch (what ) {
407-
408- /* the 'frame' of a called function is about to start its execution */
409- case PyTrace_CALL :
410- {
411- PyCodeObject * code = PyFrame_GetCode (frame );
412- ptrace_enter_call (self , (void * )code , (PyObject * )code );
413- Py_DECREF (code );
414- break ;
415- }
416-
417- /* the 'frame' of a called function is about to finish
418- (either normally or with an exception) */
419- case PyTrace_RETURN :
420- {
421- PyCodeObject * code = PyFrame_GetCode (frame );
422- ptrace_leave_call (self , (void * )code );
423- Py_DECREF (code );
424- break ;
425- }
426-
427- /* case PyTrace_EXCEPTION:
428- If the exception results in the function exiting, a
429- PyTrace_RETURN event will be generated, so we don't need to
430- handle it. */
431-
432- /* the Python function 'frame' is issuing a call to the built-in
433- function 'arg' */
434- case PyTrace_C_CALL :
435- if ((((ProfilerObject * )self )-> flags & POF_BUILTINS )
436- && PyCFunction_Check (arg )) {
437- ptrace_enter_call (self ,
438- ((PyCFunctionObject * )arg )-> m_ml ,
439- arg );
440- }
441- break ;
442-
443- /* the call to the built-in function 'arg' is returning into its
444- caller 'frame' */
445- case PyTrace_C_RETURN : /* ...normally */
446- case PyTrace_C_EXCEPTION : /* ...with an exception set */
447- if ((((ProfilerObject * )self )-> flags & POF_BUILTINS )
448- && PyCFunction_Check (arg )) {
449- ptrace_leave_call (self ,
450- ((PyCFunctionObject * )arg )-> m_ml );
451- }
452- break ;
453-
454- default :
455- break ;
456- }
457- return 0 ;
458- }
459-
460404static int
461405pending_exception (ProfilerObject * pObj )
462406{
@@ -650,6 +594,99 @@ setBuiltins(ProfilerObject *pObj, int nvalue)
650594 return 0 ;
651595}
652596
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_RETURN , "_pyreturn_callback" },
682+ {PY_MONITORING_EVENT_PY_YIELD , "_pyreturn_callback" },
683+ {PY_MONITORING_EVENT_PY_UNWIND , "_pyreturn_callback" },
684+ {PY_MONITORING_EVENT_CALL , "_ccall_callback" },
685+ {PY_MONITORING_EVENT_C_RETURN , "_creturn_callback" },
686+ {PY_MONITORING_EVENT_C_RAISE , "_creturn_callback" },
687+ {0 , NULL }
688+ };
689+
653690PyDoc_STRVAR (enable_doc , "\
654691enable(subcalls=True, builtins=True)\n\
655692\n\
@@ -666,18 +703,46 @@ profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
666703 int subcalls = -1 ;
667704 int builtins = -1 ;
668705 static char * kwlist [] = {"subcalls" , "builtins" , 0 };
706+ int all_events = 0 ;
707+
669708 if (!PyArg_ParseTupleAndKeywords (args , kwds , "|pp:enable" ,
670709 kwlist , & subcalls , & builtins ))
671710 return NULL ;
672711 if (setSubcalls (self , subcalls ) < 0 || setBuiltins (self , builtins ) < 0 ) {
673712 return NULL ;
674713 }
675714
676- PyThreadState * tstate = _PyThreadState_GET ();
677- if (_PyEval_SetProfile (tstate , profiler_callback , (PyObject * )self ) < 0 ) {
715+ PyObject * monitoring = _PyImport_GetModuleAttrString ("sys" , "monitoring" );
716+ if (!monitoring ) {
717+ return NULL ;
718+ }
719+
720+ if (PyObject_CallMethod (monitoring , "use_tool_id" , "is" , self -> tool_id , "cProfile" ) == NULL ) {
721+ PyErr_Format (PyExc_ValueError , "Another profiling tool is already active" );
722+ Py_DECREF (monitoring );
723+ return NULL ;
724+ }
725+
726+ for (int i = 0 ; callback_table [i ].callback_method ; i ++ ) {
727+ PyObject * callback = PyObject_GetAttrString ((PyObject * )self , callback_table [i ].callback_method );
728+ if (!callback ) {
729+ Py_DECREF (monitoring );
730+ return NULL ;
731+ }
732+ Py_XDECREF (PyObject_CallMethod (monitoring , "register_callback" , "iiO" , self -> tool_id ,
733+ (1 << callback_table [i ].event ),
734+ callback ));
735+ Py_DECREF (callback );
736+ all_events |= (1 << callback_table [i ].event );
737+ }
738+
739+ if (!PyObject_CallMethod (monitoring , "set_events" , "ii" , self -> tool_id , all_events )) {
740+ Py_DECREF (monitoring );
678741 return NULL ;
679742 }
680743
744+ Py_DECREF (monitoring );
745+
681746 self -> flags |= POF_ENABLED ;
682747 Py_RETURN_NONE ;
683748}
@@ -707,13 +772,44 @@ Stop collecting profiling information.\n\
707772static PyObject *
708773profiler_disable (ProfilerObject * self , PyObject * noarg )
709774{
710- PyThreadState * tstate = _PyThreadState_GET ();
711- if (_PyEval_SetProfile (tstate , NULL , NULL ) < 0 ) {
712- return NULL ;
775+ if (self -> flags & POF_ENABLED ) {
776+ PyObject * result = NULL ;
777+ PyObject * monitoring = _PyImport_GetModuleAttrString ("sys" , "monitoring" );
778+
779+ if (!monitoring ) {
780+ return NULL ;
781+ }
782+
783+ for (int i = 0 ; callback_table [i ].callback_method ; i ++ ) {
784+ result = PyObject_CallMethod (monitoring , "register_callback" , "iiO" , self -> tool_id ,
785+ (1 << callback_table [i ].event ), Py_None );
786+ if (!result ) {
787+ Py_DECREF (monitoring );
788+ return NULL ;
789+ }
790+ Py_DECREF (result );
791+ }
792+
793+ result = PyObject_CallMethod (monitoring , "set_events" , "ii" , self -> tool_id , 0 );
794+ if (!result ) {
795+ Py_DECREF (monitoring );
796+ return NULL ;
797+ }
798+ Py_DECREF (result );
799+
800+ result = PyObject_CallMethod (monitoring , "free_tool_id" , "i" , self -> tool_id );
801+ if (!result ) {
802+ Py_DECREF (monitoring );
803+ return NULL ;
804+ }
805+ Py_DECREF (result );
806+
807+ Py_DECREF (monitoring );
808+
809+ self -> flags &= ~POF_ENABLED ;
810+ flush_unmatched (self );
713811 }
714- self -> flags &= ~POF_ENABLED ;
715812
716- flush_unmatched (self );
717813 if (pending_exception (self )) {
718814 return NULL ;
719815 }
@@ -778,17 +874,37 @@ profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
778874 return -1 ;
779875 pObj -> externalTimerUnit = timeunit ;
780876 Py_XSETREF (pObj -> externalTimer , Py_XNewRef (timer ));
877+ pObj -> tool_id = PY_MONITORING_PROFILER_ID ;
878+
879+ PyObject * monitoring = _PyImport_GetModuleAttrString ("sys" , "monitoring" );
880+ if (!monitoring ) {
881+ return -1 ;
882+ }
883+ pObj -> missing = PyObject_GetAttrString (monitoring , "MISSING" );
884+ if (!pObj -> missing ) {
885+ Py_DECREF (monitoring );
886+ return -1 ;
887+ }
888+ Py_DECREF (monitoring );
781889 return 0 ;
782890}
783891
784892static PyMethodDef profiler_methods [] = {
785893 _LSPROF_PROFILER_GETSTATS_METHODDEF
786- {"enable" , _PyCFunction_CAST (profiler_enable ),
894+ {"enable" , _PyCFunction_CAST (profiler_enable ),
787895 METH_VARARGS | METH_KEYWORDS , enable_doc },
788- {"disable" , (PyCFunction )profiler_disable ,
896+ {"disable" , (PyCFunction )profiler_disable ,
789897 METH_NOARGS , disable_doc },
790- {"clear" , (PyCFunction )profiler_clear ,
898+ {"clear" , (PyCFunction )profiler_clear ,
791899 METH_NOARGS , clear_doc },
900+ {"_pystart_callback" , _PyCFunction_CAST (pystart_callback ),
901+ METH_FASTCALL , NULL },
902+ {"_pyreturn_callback" , _PyCFunction_CAST (pyreturn_callback ),
903+ METH_FASTCALL , NULL },
904+ {"_ccall_callback" , _PyCFunction_CAST (ccall_callback ),
905+ METH_FASTCALL , NULL },
906+ {"_creturn_callback" , _PyCFunction_CAST (creturn_callback ),
907+ METH_FASTCALL , NULL },
792908 {NULL , NULL }
793909};
794910
0 commit comments