]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-122029: Log call events in sys.setprofile when it's a method with c functio...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 23 Jul 2024 22:49:36 +0000 (00:49 +0200)
committerGitHub <noreply@github.com>
Tue, 23 Jul 2024 22:49:36 +0000 (22:49 +0000)
gh-122029: Log call events in sys.setprofile when it's a method with c function (GH-122072)

Log call events in sys.setprofile when it is a method with a C function.
(cherry picked from commit e91ef13861e88c27aed51a24e58d1dcc855a01dc)

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
Lib/test/test_sys_setprofile.py
Misc/NEWS.d/next/Core and Builtins/2024-07-21-01-23-54.gh-issue-122029.gKv-e2.rst [new file with mode: 0644]
Python/legacy_tracing.c

index 32e03d7cd25dbef6c02ee1df2941e48446845abf..b2e8e8a15b67eae73750b5ea4e4ed4993cbb7707 100644 (file)
@@ -479,6 +479,20 @@ class TestEdgeCases(unittest.TestCase):
         sys.setprofile(lambda *args: None)
         f()
 
+    def test_method_with_c_function(self):
+        # gh-122029
+        # When we have a PyMethodObject whose im_func is a C function, we
+        # should record both the call and the return. f = classmethod(repr)
+        # is just a way to create a PyMethodObject with a C function.
+        class A:
+            f = classmethod(repr)
+        events = []
+        sys.setprofile(lambda frame, event, args: events.append(event))
+        A().f()
+        sys.setprofile(None)
+        # The last c_call is the call to sys.setprofile
+        self.assertEqual(events, ['c_call', 'c_return', 'c_call'])
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-21-01-23-54.gh-issue-122029.gKv-e2.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-21-01-23-54.gh-issue-122029.gKv-e2.rst
new file mode 100644 (file)
index 0000000..bddee3a
--- /dev/null
@@ -0,0 +1 @@
+Emit ``c_call`` events in :func:`sys.setprofile` when a ``PyMethodObject`` pointing to a ``PyCFunction`` is called.
index 1103d999dfaea5911d6c9ed5fe6bc8dbe1edba7a..9cc3af1f5e162c14f9fe9ae8281d59e57d03ccd3 100644 (file)
@@ -121,6 +121,19 @@ sys_profile_call_or_return(
         Py_DECREF(meth);
         return res;
     }
+    else if (Py_TYPE(callable) == &PyMethod_Type) {
+        // CALL instruction will grab the function from the method,
+        // so if the function is a C function, the return event will
+        // be emitted. However, CALL event happens before CALL
+        // instruction, so we need to handle this case here.
+        PyObject* func = PyMethod_GET_FUNCTION(callable);
+        if (func == NULL) {
+            return NULL;
+        }
+        if (PyCFunction_Check(func)) {
+            return call_profile_func(self, func);
+        }
+    }
     Py_RETURN_NONE;
 }