@@ -49,6 +49,8 @@ typedef struct {
49
49
int flags ;
50
50
PyObject * externalTimer ;
51
51
double externalTimerUnit ;
52
+ int tool_id ;
53
+ PyObject * missing ;
52
54
} ProfilerObject ;
53
55
54
56
#define POF_ENABLED 0x001
@@ -399,64 +401,6 @@ ptrace_leave_call(PyObject *self, void *key)
399
401
pObj -> freelistProfilerContext = pContext ;
400
402
}
401
403
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
-
460
404
static int
461
405
pending_exception (ProfilerObject * pObj )
462
406
{
@@ -650,6 +594,99 @@ setBuiltins(ProfilerObject *pObj, int nvalue)
650
594
return 0 ;
651
595
}
652
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_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
+
653
690
PyDoc_STRVAR (enable_doc , "\
654
691
enable(subcalls=True, builtins=True)\n\
655
692
\n\
@@ -666,18 +703,46 @@ profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
666
703
int subcalls = -1 ;
667
704
int builtins = -1 ;
668
705
static char * kwlist [] = {"subcalls" , "builtins" , 0 };
706
+ int all_events = 0 ;
707
+
669
708
if (!PyArg_ParseTupleAndKeywords (args , kwds , "|pp:enable" ,
670
709
kwlist , & subcalls , & builtins ))
671
710
return NULL ;
672
711
if (setSubcalls (self , subcalls ) < 0 || setBuiltins (self , builtins ) < 0 ) {
673
712
return NULL ;
674
713
}
675
714
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 );
678
741
return NULL ;
679
742
}
680
743
744
+ Py_DECREF (monitoring );
745
+
681
746
self -> flags |= POF_ENABLED ;
682
747
Py_RETURN_NONE ;
683
748
}
@@ -707,13 +772,44 @@ Stop collecting profiling information.\n\
707
772
static PyObject *
708
773
profiler_disable (ProfilerObject * self , PyObject * noarg )
709
774
{
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 );
713
811
}
714
- self -> flags &= ~POF_ENABLED ;
715
812
716
- flush_unmatched (self );
717
813
if (pending_exception (self )) {
718
814
return NULL ;
719
815
}
@@ -778,17 +874,37 @@ profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
778
874
return -1 ;
779
875
pObj -> externalTimerUnit = timeunit ;
780
876
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 );
781
889
return 0 ;
782
890
}
783
891
784
892
static PyMethodDef profiler_methods [] = {
785
893
_LSPROF_PROFILER_GETSTATS_METHODDEF
786
- {"enable" , _PyCFunction_CAST (profiler_enable ),
894
+ {"enable" , _PyCFunction_CAST (profiler_enable ),
787
895
METH_VARARGS | METH_KEYWORDS , enable_doc },
788
- {"disable" , (PyCFunction )profiler_disable ,
896
+ {"disable" , (PyCFunction )profiler_disable ,
789
897
METH_NOARGS , disable_doc },
790
- {"clear" , (PyCFunction )profiler_clear ,
898
+ {"clear" , (PyCFunction )profiler_clear ,
791
899
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 },
792
908
{NULL , NULL }
793
909
};
794
910
0 commit comments