diff --git a/.python-yappi.metadata b/.python-yappi.metadata new file mode 100644 index 0000000..9aa7e02 --- /dev/null +++ b/.python-yappi.metadata @@ -0,0 +1 @@ +7035778a818e3fa72d53a0c7e05d3577ad641baa SOURCES/yappi-1.3.6.tar.gz diff --git a/SOURCES/0001-Change-frame-object-update-asyncio-tests.patch b/SOURCES/0001-Change-frame-object-update-asyncio-tests.patch new file mode 100644 index 0000000..2f937cf --- /dev/null +++ b/SOURCES/0001-Change-frame-object-update-asyncio-tests.patch @@ -0,0 +1,200 @@ +From e939a82ae8a1617a821547060e7a581492adda01 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Tue, 2 Aug 2022 11:27:29 +0300 +Subject: [PATCH 01/11] Change frame object + update asyncio tests + +--- + tests/test_asyncio.py | 53 ++++++++++++++++++------------------------- + yappi/_yappi.c | 21 +++++++++++++---- + yappi/timing.c | 3 +-- + 3 files changed, 39 insertions(+), 38 deletions(-) + +diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py +index b547674..8e9e631 100644 +--- a/tests/test_asyncio.py ++++ b/tests/test_asyncio.py +@@ -5,20 +5,18 @@ import threading + from utils import YappiUnitTestCase, find_stat_by_name, burn_cpu, burn_io + + +-@asyncio.coroutine +-def async_sleep(sec): +- yield from asyncio.sleep(sec) ++async def async_sleep(sec): ++ await asyncio.sleep(sec) + + + class SingleThreadTests(YappiUnitTestCase): + + def test_issue58(self): + +- @asyncio.coroutine +- def mt(d): ++ async def mt(d): + t = asyncio.Task(async_sleep(3 + d)) +- yield from async_sleep(3) +- yield from t ++ await async_sleep(3) ++ await t + + yappi.set_clock_type('wall') + +@@ -41,14 +39,13 @@ class SingleThreadTests(YappiUnitTestCase): + + def test_recursive_coroutine(self): + +- @asyncio.coroutine +- def a(n): ++ async def a(n): + if n <= 0: + return +- yield from async_sleep(0.1) ++ await async_sleep(0.1) + burn_cpu(0.1) +- yield from a(n - 1) +- yield from a(n - 2) ++ await a(n - 1) ++ await a(n - 2) + + yappi.set_clock_type("cpu") + yappi.start() +@@ -65,13 +62,12 @@ class SingleThreadTests(YappiUnitTestCase): + + def test_basic_old_style(self): + +- @asyncio.coroutine +- def a(): +- yield from async_sleep(0.1) ++ async def a(): ++ await async_sleep(0.1) + burn_io(0.1) +- yield from async_sleep(0.1) ++ await async_sleep(0.1) + burn_io(0.1) +- yield from async_sleep(0.1) ++ await async_sleep(0.1) + burn_cpu(0.3) + + yappi.set_clock_type("wall") +@@ -111,22 +107,19 @@ class MultiThreadTests(YappiUnitTestCase): + + def test_basic(self): + +- @asyncio.coroutine +- def a(): +- yield from async_sleep(0.3) ++ async def a(): ++ await async_sleep(0.3) + burn_cpu(0.4) + +- @asyncio.coroutine +- def b(): +- yield from a() ++ async def b(): ++ await a() + +- @asyncio.coroutine +- def recursive_a(n): ++ async def recursive_a(n): + if not n: + return + burn_io(0.3) +- yield from async_sleep(0.3) +- yield from recursive_a(n - 1) ++ await async_sleep(0.3) ++ await recursive_a(n - 1) + + tlocal = threading.local() + +@@ -158,12 +151,10 @@ class MultiThreadTests(YappiUnitTestCase): + ts.append(t) + _ctag += 1 + +- @asyncio.coroutine +- def stop_loop(): ++ async def stop_loop(): + asyncio.get_event_loop().stop() + +- @asyncio.coroutine +- def driver(): ++ async def driver(): + futs = [] + fut = asyncio.run_coroutine_threadsafe(a(), ts[0]._loop) + futs.append(fut) +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index fa3884e..0b10935 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -200,6 +200,15 @@ static int _pitenumdel(_hitem *item, void *arg); + + // funcs + ++static PyCodeObject * ++FRAME2CODE(PyFrameObject *frame) { ++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 10 ++ return PyFrame_GetCode(frame); ++#else ++ return frame->f_code; ++#endif ++} ++ + static void _DebugPrintObjects(unsigned int arg_count, ...) + { + unsigned int i; +@@ -216,7 +225,9 @@ static void _DebugPrintObjects(unsigned int arg_count, ...) + int + IS_SUSPENDED(PyFrameObject *frame) + { +-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 10 ++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 ++ return 1; ++#elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 10 + return (frame->f_state == FRAME_SUSPENDED); + #else + return (frame->f_stacktop != NULL); +@@ -229,11 +240,11 @@ int IS_ASYNC(PyFrameObject *frame) + + #if defined(IS_PY3K) + #if PY_MINOR_VERSION >= 4 +- result = frame->f_code->co_flags & CO_COROUTINE || +- frame->f_code->co_flags & CO_ITERABLE_COROUTINE; ++ result = FRAME2CODE(frame)->co_flags & CO_COROUTINE || ++ FRAME2CODE(frame)->co_flags & CO_ITERABLE_COROUTINE; + #endif + #if PY_MINOR_VERSION >= 6 +- result = result || frame->f_code->co_flags & CO_ASYNC_GENERATOR; ++ result = result || FRAME2CODE(frame)->co_flags & CO_ASYNC_GENERATOR; + #endif + #endif + +@@ -650,7 +661,7 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) + return NULL; + } + +- cobj = fobj->f_code; ++ cobj = FRAME2CODE(fobj); + it = hfind(pits, (uintptr_t)cobj); + if (it) { + return ((_pit *)it->val); +diff --git a/yappi/timing.c b/yappi/timing.c +index fa2ad24..b5bf083 100644 +--- a/yappi/timing.c ++++ b/yappi/timing.c +@@ -81,13 +81,12 @@ tickcount(void) + + rc = 0; + if (g_clock_type == CPU_CLOCK) { +- kern_return_t kr; + thread_basic_info_t tinfo_b; + thread_info_data_t tinfo_d; + mach_msg_type_number_t tinfo_cnt; + + tinfo_cnt = THREAD_INFO_MAX; +- kr = thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)tinfo_d, &tinfo_cnt); ++ thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)tinfo_d, &tinfo_cnt); + tinfo_b = (thread_basic_info_t)tinfo_d; + + if (!(tinfo_b->flags & TH_FLAGS_IDLE)) +-- +2.34.1 + diff --git a/SOURCES/0002-fix-few-seg.-faults.patch b/SOURCES/0002-fix-few-seg.-faults.patch new file mode 100644 index 0000000..8426d01 --- /dev/null +++ b/SOURCES/0002-fix-few-seg.-faults.patch @@ -0,0 +1,81 @@ +From 12152051e6af47f898c0f791d113a53bb3bbe478 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 4 Aug 2022 13:16:28 +0300 +Subject: [PATCH 02/11] fix few seg. faults + +--- + yappi/_yappi.c | 37 ++++++++++++++++++++++++------------- + 1 file changed, 24 insertions(+), 13 deletions(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 0b10935..369e88c 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -226,6 +226,8 @@ int + IS_SUSPENDED(PyFrameObject *frame) + { + #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 ++ // See https://discuss.python.org/t/python-3-11-frame-structure-and-various-changes/17895 ++ // TODO: _PyFrame_GetGenerator(frame)->gi_frame_state ??? + return 1; + #elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 10 + return (frame->f_state == FRAME_SUSPENDED); +@@ -681,7 +683,10 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) + Py_INCREF(cobj); + + if (cobj->co_argcount) { +- const char *firstarg = PyStr_AS_CSTRING(PyTuple_GET_ITEM(cobj->co_varnames, 0)); ++ // todo: this is said to be slower. Maybe there is a better alternative ++ // like _PyCode_GetVarnames(..). See https://discuss.python.org/t/python-3-11-frame-structure-and-various-changes/17895 ++ PyObject *co_varnames = PyObject_GetAttrString((PyObject *)cobj, "co_varnames"); ++ const char *firstarg = PyStr_AS_CSTRING(PyTuple_GET_ITEM(co_varnames, 0)); + + if (!strcmp(firstarg, "self")) { + PyObject* locals = _get_locals(fobj); +@@ -1325,12 +1330,15 @@ _profile_thread(PyThreadState *ts) + ctx = (_ctx *)it->val; + } + +-#if PY_VERSION_HEX < 0x030a00b1 +- ts->use_tracing = 1; +-#else +- ts->cframe->use_tracing = 1; +-#endif +- ts->c_profilefunc = _yapp_callback; ++// #if PY_VERSION_HEX < 0x030a00b1 ++// ts->use_tracing = 1; ++// #else ++// ts->cframe->use_tracing = 1; ++// #endif ++// ts->c_profilefunc = _yapp_callback; ++ // TODO: How to do this for all threads? ++ PyEval_SetProfile(_yapp_callback, NULL); ++ + ctx->id = ctx_id; + ctx->tid = ts->thread_id; + ctx->ts_ptr = ts; +@@ -1346,12 +1354,15 @@ _profile_thread(PyThreadState *ts) + static _ctx* + _unprofile_thread(PyThreadState *ts) + { +-#if PY_VERSION_HEX < 0x030a00b1 +- ts->use_tracing = 0; +-#else +- ts->cframe->use_tracing = 0; +-#endif +- ts->c_profilefunc = NULL; ++// #if PY_VERSION_HEX < 0x030a00b1 ++// ts->use_tracing = 0; ++// #else ++// ts->cframe->use_tracing = 0; ++// #endif ++// ts->c_profilefunc = NULL; ++ ++ // TODO: How to do this for all threads? ++ PyEval_SetProfile(NULL, NULL); + + return NULL; //dummy return for enum_threads() func. prototype + } +-- +2.34.1 + diff --git a/SOURCES/0003-fix-ensure_thread_profiled.patch b/SOURCES/0003-fix-ensure_thread_profiled.patch new file mode 100644 index 0000000..272c645 --- /dev/null +++ b/SOURCES/0003-fix-ensure_thread_profiled.patch @@ -0,0 +1,46 @@ +From 432c13b3d1247efd59610e502181091b33baada5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 4 Aug 2022 13:31:20 +0300 +Subject: [PATCH 03/11] fix ensure_thread_profiled + +--- + yappi/_yappi.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 369e88c..00fdbd4 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -1295,12 +1295,14 @@ _resume_greenlet_ctx(_ctx *ctx) + static _ctx * + _bootstrap_thread(PyThreadState *ts) + { +-#if PY_VERSION_HEX < 0x030a00b1 +- ts->use_tracing = 1; +-#else +- ts->cframe->use_tracing = 1; +-#endif +- ts->c_profilefunc = _yapp_callback; ++// #if PY_VERSION_HEX < 0x030a00b1 ++// ts->use_tracing = 1; ++// #else ++// ts->cframe->use_tracing = 1; ++// #endif ++// ts->c_profilefunc = _yapp_callback; ++ PyEval_SetProfile(_yapp_callback, NULL); ++ + return NULL; + } + +@@ -1491,6 +1493,8 @@ _start(void) + return 0; + } + ++ //flags.multicontext = 0; ++ + if (flags.multicontext) { + _enum_threads(&_bootstrap_thread); + } else { +-- +2.34.1 + diff --git a/SOURCES/0004-6-failures-in-TCs.patch b/SOURCES/0004-6-failures-in-TCs.patch new file mode 100644 index 0000000..f5d70c5 --- /dev/null +++ b/SOURCES/0004-6-failures-in-TCs.patch @@ -0,0 +1,97 @@ +From 09f738ced164b3b347cd4865c11a4e7c45de25e3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 4 Aug 2022 14:04:47 +0300 +Subject: [PATCH 04/11] 6 failures in TCs + +--- + yappi/_yappi.c | 57 ++++++++++++++++++++++++++------------------------ + 1 file changed, 30 insertions(+), 27 deletions(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 00fdbd4..d2f0af4 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -1292,17 +1292,37 @@ _resume_greenlet_ctx(_ctx *ctx) + ydprintf("resuming context: %ld, shift: %lld", ctx->id, shift); + } + ++static void ++_eval_setprofile(PyThreadState *ts) ++{ ++#if PY_VERSION_HEX < 0x030a00b1 ++ ts->use_tracing = 1; ++#else ++ ts->cframe->use_tracing = 1; ++#endif ++ ts->c_profilefunc = _yapp_callback; ++ ++ //_update_tracing_state() ++ ++// todo: do this only for 3.11 ++ ts->cframe->use_tracing = 255; ++} ++ ++static void ++_eval_unsetprofile(PyThreadState *ts) ++{ ++#if PY_VERSION_HEX < 0x030a00b1 ++ ts->use_tracing = 0; ++#else ++ ts->cframe->use_tracing = 0; ++#endif ++ ts->c_profilefunc = NULL; ++} ++ + static _ctx * + _bootstrap_thread(PyThreadState *ts) + { +-// #if PY_VERSION_HEX < 0x030a00b1 +-// ts->use_tracing = 1; +-// #else +-// ts->cframe->use_tracing = 1; +-// #endif +-// ts->c_profilefunc = _yapp_callback; +- PyEval_SetProfile(_yapp_callback, NULL); +- ++ _eval_setprofile(ts); + return NULL; + } + +@@ -1331,16 +1351,7 @@ _profile_thread(PyThreadState *ts) + } else { + ctx = (_ctx *)it->val; + } +- +-// #if PY_VERSION_HEX < 0x030a00b1 +-// ts->use_tracing = 1; +-// #else +-// ts->cframe->use_tracing = 1; +-// #endif +-// ts->c_profilefunc = _yapp_callback; +- // TODO: How to do this for all threads? +- PyEval_SetProfile(_yapp_callback, NULL); +- ++ _eval_setprofile(ts); + ctx->id = ctx_id; + ctx->tid = ts->thread_id; + ctx->ts_ptr = ts; +@@ -1356,15 +1367,7 @@ _profile_thread(PyThreadState *ts) + static _ctx* + _unprofile_thread(PyThreadState *ts) + { +-// #if PY_VERSION_HEX < 0x030a00b1 +-// ts->use_tracing = 0; +-// #else +-// ts->cframe->use_tracing = 0; +-// #endif +-// ts->c_profilefunc = NULL; +- +- // TODO: How to do this for all threads? +- PyEval_SetProfile(NULL, NULL); ++ _eval_unsetprofile(ts); + + return NULL; //dummy return for enum_threads() func. prototype + } +-- +2.34.1 + diff --git a/SOURCES/0005-Use-_PyEval_SetProfile-instead-of-manually-updating-.patch b/SOURCES/0005-Use-_PyEval_SetProfile-instead-of-manually-updating-.patch new file mode 100644 index 0000000..4c1e347 --- /dev/null +++ b/SOURCES/0005-Use-_PyEval_SetProfile-instead-of-manually-updating-.patch @@ -0,0 +1,80 @@ +From 4d9c0667a24ead349333c47752747e51baa02d42 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 18 Aug 2022 12:15:59 +0300 +Subject: [PATCH 05/11] Use _PyEval_SetProfile instead of manually updating + internal structures to enable per profiler per threadstate + +--- + yappi/_yappi.c | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index d2f0af4..5a3f78a 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -25,6 +25,10 @@ + #include "mem.h" + #include "tls.h" + ++#if PY_VERSION_HEX > 0x030b0000 ++#include "internal/pycore_code.h" ++#endif ++ + #define SUPPRESS_WARNING(a) (void)a + + #ifdef IS_PY3K +@@ -683,9 +687,8 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) + Py_INCREF(cobj); + + if (cobj->co_argcount) { +- // todo: this is said to be slower. Maybe there is a better alternative +- // like _PyCode_GetVarnames(..). See https://discuss.python.org/t/python-3-11-frame-structure-and-various-changes/17895 +- PyObject *co_varnames = PyObject_GetAttrString((PyObject *)cobj, "co_varnames"); ++ // todo: 3.12 this might be a public API ++ PyObject *co_varnames = _PyCode_GetVarnames(cobj); + const char *firstarg = PyStr_AS_CSTRING(PyTuple_GET_ITEM(co_varnames, 0)); + + if (!strcmp(firstarg, "self")) { +@@ -1295,28 +1298,30 @@ _resume_greenlet_ctx(_ctx *ctx) + static void + _eval_setprofile(PyThreadState *ts) + { +-#if PY_VERSION_HEX < 0x030a00b1 ++#if PY_VERSION_HEX > 0x030b0000 ++ _PyEval_SetProfile(ts, _yapp_callback, NULL); ++ // ts->cframe->use_tracing = 255; ++#elif PY_VERSION_HEX < 0x030a00b1 + ts->use_tracing = 1; ++ ts->c_profilefunc = _yapp_callback; + #else + ts->cframe->use_tracing = 1; +-#endif + ts->c_profilefunc = _yapp_callback; +- +- //_update_tracing_state() +- +-// todo: do this only for 3.11 +- ts->cframe->use_tracing = 255; ++#endif + } + + static void + _eval_unsetprofile(PyThreadState *ts) + { +-#if PY_VERSION_HEX < 0x030a00b1 ++#if PY_VERSION_HEX > 0x030b0000 ++ _PyEval_SetProfile(ts, NULL, NULL); ++#elif PY_VERSION_HEX < 0x030a00b1 + ts->use_tracing = 0; ++ ts->c_profilefunc = NULL; + #else + ts->cframe->use_tracing = 0; +-#endif + ts->c_profilefunc = NULL; ++#endif + } + + static _ctx * +-- +2.34.1 + diff --git a/SOURCES/0006-Use-PyCode_GetVarnames-added-in-3.11.0rc1.patch b/SOURCES/0006-Use-PyCode_GetVarnames-added-in-3.11.0rc1.patch new file mode 100644 index 0000000..1b87c8a --- /dev/null +++ b/SOURCES/0006-Use-PyCode_GetVarnames-added-in-3.11.0rc1.patch @@ -0,0 +1,28 @@ +From 7b3b6f70e6daeabb14c2b79f23367bda8a2772ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 18 Aug 2022 12:31:03 +0300 +Subject: [PATCH 06/11] Use PyCode_GetVarnames -- added in 3.11.0rc1 + +--- + yappi/_yappi.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 5a3f78a..641cc84 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -687,8 +687,9 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) + Py_INCREF(cobj); + + if (cobj->co_argcount) { +- // todo: 3.12 this might be a public API +- PyObject *co_varnames = _PyCode_GetVarnames(cobj); ++ // There has been a lot going on with `co_varnames`, but finally in ++ // 3.11.0rc1, it is added as a public API ++ PyObject *co_varnames = PyCode_GetVarnames(cobj); + const char *firstarg = PyStr_AS_CSTRING(PyTuple_GET_ITEM(co_varnames, 0)); + + if (!strcmp(firstarg, "self")) { +-- +2.34.1 + diff --git a/SOURCES/0007-Fix-IS_SUSPENDED-call-to-use-PyFrame_GetGenerator-gi.patch b/SOURCES/0007-Fix-IS_SUSPENDED-call-to-use-PyFrame_GetGenerator-gi.patch new file mode 100644 index 0000000..451cce2 --- /dev/null +++ b/SOURCES/0007-Fix-IS_SUSPENDED-call-to-use-PyFrame_GetGenerator-gi.patch @@ -0,0 +1,51 @@ +From caadf3fbbdc89528c1f4833c6cbb153a9de4fd0c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 18 Aug 2022 15:14:08 +0300 +Subject: [PATCH 07/11] Fix IS_SUSPENDED() call to use + PyFrame_GetGenerator()->gi_frame_state + +--- + yappi/_yappi.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 641cc84..88236ee 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -25,10 +25,6 @@ + #include "mem.h" + #include "tls.h" + +-#if PY_VERSION_HEX > 0x030b0000 +-#include "internal/pycore_code.h" +-#endif +- + #define SUPPRESS_WARNING(a) (void)a + + #ifdef IS_PY3K +@@ -227,12 +223,17 @@ static void _DebugPrintObjects(unsigned int arg_count, ...) + } + + int +-IS_SUSPENDED(PyFrameObject *frame) +-{ +-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 ++IS_SUSPENDED(PyFrameObject *frame) { ++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 11 ++ PyGenObject *gen = (PyGenObject *)PyFrame_GetGenerator(frame); ++ if (gen == NULL) { ++ return 0; ++ } ++ ++ // -1 is FRAME_SUSPENDED. See internal/pycore_frame.h ++ // TODO: Remove these after 3.12 make necessary public APIs. + // See https://discuss.python.org/t/python-3-11-frame-structure-and-various-changes/17895 +- // TODO: _PyFrame_GetGenerator(frame)->gi_frame_state ??? +- return 1; ++ return gen->gi_frame_state == -1; + #elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 10 + return (frame->f_state == FRAME_SUSPENDED); + #else +-- +2.34.1 + diff --git a/SOURCES/0009-Use-codeobject-co_varnames-for-Py-versions-11.patch b/SOURCES/0009-Use-codeobject-co_varnames-for-Py-versions-11.patch new file mode 100644 index 0000000..78d8cd6 --- /dev/null +++ b/SOURCES/0009-Use-codeobject-co_varnames-for-Py-versions-11.patch @@ -0,0 +1,327 @@ +From bf5cb748ffc3d228593a388b2286e35529f059d7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Thu, 18 Aug 2022 15:23:47 +0300 +Subject: [PATCH 09/11] Use codeobject->co_varnames for Py versions < 11 + +--- + run_tests.py | 4 +-- + setup.py | 9 +++-- + tests/test_gevent.py | 71 ++++++++++++++++++++++++-------------- + yappi/_yappi.c | 9 +++-- + 5 files changed, 62 insertions(+), 33 deletions(-) + +diff --git a/run_tests.py b/run_tests.py +index e632cbf..df36f74 100644 +--- a/run_tests.py ++++ b/run_tests.py +@@ -19,9 +19,9 @@ if __name__ == '__main__': + 'test_functionality', + 'test_hooks', + 'test_tags', ++ 'test_gevent', + ] +- if sys.version_info < (3, 10): +- tests += ['test_gevent'] ++ # TODO: make these auto-skip if cannot be imported + if sys.version_info >= (3, 4): + tests += ['test_asyncio'] + if sys.version_info >= (3, 7): +diff --git a/setup.py b/setup.py +index 5e4fe66..f241810 100644 +--- a/setup.py ++++ b/setup.py +@@ -62,6 +62,11 @@ CLASSIFIERS = [ + 'Topic :: Software Development :: Libraries :: Python Modules', + ] + ++test_deps = [] ++if sys.version_info <= (3, 10): ++ # TODO: fix this when gevent supports 3.11 ++ test_deps += ['gevent>=20.6.2'] ++ + setup( + name=NAME, + version=VERSION, +@@ -91,11 +96,11 @@ setup( + description="Yet Another Python Profiler", + long_description=long_description, + long_description_content_type='text/markdown', +- keywords="python thread multithread profiler", ++ keywords="python thread multithread asyncio gevent profiler", + classifiers=CLASSIFIERS, + license="MIT", + url=HOMEPAGE, + extras_require={ +- 'test': ['gevent>=20.6.2'], ++ 'test': test_deps, + } + ) +diff --git a/tests/test_gevent.py b/tests/test_gevent.py +index 8569712..0b105a2 100644 +--- a/tests/test_gevent.py ++++ b/tests/test_gevent.py +@@ -1,15 +1,21 @@ + import unittest + import _yappi + import yappi +-import gevent +-from gevent.event import Event + import threading + from utils import ( +- YappiUnitTestCase, find_stat_by_name, burn_cpu, burn_io, +- burn_io_gevent ++ YappiUnitTestCase, find_stat_by_name, burn_cpu, burn_io, burn_io_gevent + ) + ++GEVENT_AVAIL = True ++try: ++ import gevent ++ from gevent.event import Event ++except ImportError: ++ GEVENT_AVAIL = False ++ ++ + class GeventTestThread(threading.Thread): ++ + def __init__(self, name, *args, **kwargs): + super(GeventTestThread, self).__init__(*args, **kwargs) + self.name = name +@@ -19,6 +25,10 @@ class GeventTestThread(threading.Thread): + gevent.get_hub().name = "Hub" + super(GeventTestThread, self).run() + ++ ++@unittest.skipIf( ++ not GEVENT_AVAIL, "Gevent is not installed on the target system" ++) + class GeventTest(YappiUnitTestCase): + + def setUp(self): +@@ -51,6 +61,7 @@ class GeventTest(YappiUnitTestCase): + t.start() + return t + ++ + class TestAPI(GeventTest): + + def test_start_flags(self): +@@ -68,7 +79,9 @@ class TestAPI(GeventTest): + yappi.stop() + yappi.clear_stats() + +- yappi.start(builtins=True, profile_greenlets=True, profile_threads=False) ++ yappi.start( ++ builtins=True, profile_greenlets=True, profile_threads=False ++ ) + self.assertEqual(_yappi._get_start_flags()["profile_builtins"], 1) + self.assertEqual(_yappi._get_start_flags()["profile_multicontext"], 1) + self.assertEqual(len(yappi.get_greenlet_stats()), 1) +@@ -76,16 +89,21 @@ class TestAPI(GeventTest): + + def test_context_change_exception(self): + yappi.start() ++ + def a(): + pass + + a() + # Setting to same backend should succeed + # Changing backend should fail +- self.assertRaises(_yappi.error, yappi.set_context_backend, "native_thread") ++ self.assertRaises( ++ _yappi.error, yappi.set_context_backend, "native_thread" ++ ) + yappi.stop() + # Still fail, stats need to be cleared +- self.assertRaises(_yappi.error, yappi.set_context_backend, "native_thread") ++ self.assertRaises( ++ _yappi.error, yappi.set_context_backend, "native_thread" ++ ) + yappi.clear_stats() + # Should succeed now + yappi.set_context_backend("native_thread") +@@ -93,6 +111,7 @@ class TestAPI(GeventTest): + + def test_get_context_stat_exception(self): + yappi.start() ++ + def a(): + pass + +@@ -106,6 +125,7 @@ class TestAPI(GeventTest): + yappi.set_context_backend("native_thread") + + class ThreadA(threading.Thread): ++ + def run(self): + burn_cpu(0.05) + +@@ -127,7 +147,9 @@ class TestAPI(GeventTest): + + tstats = yappi.get_thread_stats() + +- self.assertEqual(len(tstats), 2, "Incorrect number of contexts captured") ++ self.assertEqual( ++ len(tstats), 2, "Incorrect number of contexts captured" ++ ) + + # First stat should be of threadA since it is sorted by ttot + statsA = tstats[0] +@@ -139,6 +161,7 @@ class TestAPI(GeventTest): + self.assertEqual(statsMain.tid, main_thread.ident) + self.assertEqual(statsMain.name, main_thread.__class__.__name__) + ++ + class SingleThreadTests(GeventTest): + + def test_recursive_greenlet(self): +@@ -148,14 +171,14 @@ class SingleThreadTests(GeventTest): + return + burn_io_gevent(0.1) + burn_cpu(0.1) +- g1 = self.spawn_greenlet("a_%d" % (n-1), a, n - 1) ++ g1 = self.spawn_greenlet("a_%d" % (n - 1), a, n - 1) + g1.get() +- g2 = self.spawn_greenlet("a_%d" % (n-2), a, n - 2) ++ g2 = self.spawn_greenlet("a_%d" % (n - 2), a, n - 2) + g2.get() + + yappi.start() + g = self.spawn_greenlet("a", a, 3) +- g.get() # run until complete, report exception (if any) ++ g.get() # run until complete, report exception (if any) + yappi.stop() + + r1 = ''' +@@ -239,6 +262,7 @@ class SingleThreadTests(GeventTest): + self.assert_ctx_stats_almost_equal(r2, gstats) + + def test_recursive_function(self): ++ + def a(n): + if (n <= 0): + return +@@ -280,6 +304,7 @@ class SingleThreadTests(GeventTest): + self.assert_ctx_stats_almost_equal(r2, gstats) + + def test_exception_raised(self): ++ + def a(n): + burn_cpu(0.1) + burn_io_gevent(0.1) +@@ -287,7 +312,7 @@ class SingleThreadTests(GeventTest): + if (n == 0): + raise Exception + +- a(n-1) ++ a(n - 1) + + yappi.set_clock_type("cpu") + yappi.start() +@@ -325,7 +350,6 @@ class SingleThreadTests(GeventTest): + burn_cpu(0.1) + burn_io_gevent(0.1) + +- + ev1 = Event() + ev2 = Event() + gl = self.spawn_greenlet("a", a, ev1, ev2) +@@ -353,7 +377,6 @@ class SingleThreadTests(GeventTest): + ''' + self.assert_ctx_stats_almost_equal(r2, gstats) + +- + def test_many_context_switches(self): + + def common(): +@@ -455,15 +478,14 @@ class MultiThreadTests(GeventTest): + + def driver(): + to_run = [ +- (a, ()), +- (b, ()), +- (recursive_a, (5,)), +- (recursive_a, (5,)) ++ (a, ()), (b, ()), (recursive_a, (5, )), (recursive_a, (5, )) + ] + + ts = [] + for idx, (func, args) in enumerate(to_run): +- t = self.spawn_thread("%s-%d" % (func.__name__, idx), func, *args) ++ t = self.spawn_thread( ++ "%s-%d" % (func.__name__, idx), func, *args ++ ) + ts.append(t) + + for t in ts: +@@ -520,14 +542,13 @@ class MultiThreadTests(GeventTest): + + def driver(): + +- to_run = [ +- (recursive_a, (5,)), +- (recursive_a, (5,)) +- ] ++ to_run = [(recursive_a, (5, )), (recursive_a, (5, ))] + + ts = [] + for idx, (func, args) in enumerate(to_run): +- t = self.spawn_thread("%s_%d" % (func.__name__, idx), func, *args) ++ t = self.spawn_thread( ++ "%s_%d" % (func.__name__, idx), func, *args ++ ) + ts.append(t) + + recursive_a(6) +@@ -558,7 +579,6 @@ class MultiThreadTests(GeventTest): + # Set context backend to confgiure default callbacks + yappi.set_context_backend("greenlet") + +- + class GreenletA(gevent.Greenlet): + pass + +@@ -598,5 +618,6 @@ class MultiThreadTests(GeventTest): + ''' + self.assert_ctx_stats_almost_equal(r2, gstats) + ++ + if __name__ == '__main__': + unittest.main() +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 88236ee..41ff517 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -662,6 +662,7 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) + PyCodeObject *cobj; + _pit *pit; + _htab *pits; ++ PyObject *co_varnames; + + pits = _get_pits_tbl(current_tag); + if (!pits) { +@@ -690,7 +691,11 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) + if (cobj->co_argcount) { + // There has been a lot going on with `co_varnames`, but finally in + // 3.11.0rc1, it is added as a public API +- PyObject *co_varnames = PyCode_GetVarnames(cobj); ++#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 11 ++ co_varnames = PyCode_GetVarnames(cobj); ++#else ++ co_varnames = cobj->co_varnames; ++#endif + const char *firstarg = PyStr_AS_CSTRING(PyTuple_GET_ITEM(co_varnames, 0)); + + if (!strcmp(firstarg, "self")) { +@@ -1503,8 +1508,6 @@ _start(void) + return 0; + } + +- //flags.multicontext = 0; +- + if (flags.multicontext) { + _enum_threads(&_bootstrap_thread); + } else { +-- +2.34.1 + diff --git a/SOURCES/0011-Cosmetic.patch b/SOURCES/0011-Cosmetic.patch new file mode 100644 index 0000000..e60ebf8 --- /dev/null +++ b/SOURCES/0011-Cosmetic.patch @@ -0,0 +1,24 @@ +From 67b1cfcb6e0395236fee84583d4fbabd652c4480 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=BCmer=20Cip?= +Date: Fri, 19 Aug 2022 18:28:11 +0300 +Subject: [PATCH 11/11] Cosmetic + +--- + yappi/_yappi.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/yappi/_yappi.c b/yappi/_yappi.c +index 41ff517..320ddc6 100644 +--- a/yappi/_yappi.c ++++ b/yappi/_yappi.c +@@ -1307,7 +1307,6 @@ _eval_setprofile(PyThreadState *ts) + { + #if PY_VERSION_HEX > 0x030b0000 + _PyEval_SetProfile(ts, _yapp_callback, NULL); +- // ts->cframe->use_tracing = 255; + #elif PY_VERSION_HEX < 0x030a00b1 + ts->use_tracing = 1; + ts->c_profilefunc = _yapp_callback; +-- +2.34.1 + diff --git a/SPECS/python-yappi.spec b/SPECS/python-yappi.spec new file mode 100644 index 0000000..04cc0fd --- /dev/null +++ b/SPECS/python-yappi.spec @@ -0,0 +1,128 @@ +%global srcname yappi + +Name: python-%{srcname} +Version: 1.3.6 +Release: 1%{?dist} +Summary: Yet Another Python Profiler, supports Multithread/CPU time profiling + +License: MIT +URL: https://github.com/sumerc/yappi +Source0: https://files.pythonhosted.org/packages/source/y/%{srcname}/%{srcname}-%{version}.tar.gz +# https://github.com/sumerc/yappi/commit/b07534e005d5ad36f06db81a3232998392fe55d8 unreleased +Patch01: 0001-Change-frame-object-update-asyncio-tests.patch +Patch02: 0002-fix-few-seg.-faults.patch +Patch03: 0003-fix-ensure_thread_profiled.patch +Patch04: 0004-6-failures-in-TCs.patch +Patch05: 0005-Use-_PyEval_SetProfile-instead-of-manually-updating-.patch +Patch06: 0006-Use-PyCode_GetVarnames-added-in-3.11.0rc1.patch +Patch07: 0007-Fix-IS_SUSPENDED-call-to-use-PyFrame_GetGenerator-gi.patch +Patch09: 0009-Use-codeobject-co_varnames-for-Py-versions-11.patch +Patch11: 0011-Cosmetic.patch + +BuildRequires: git +BuildRequires: gcc + +%description +Yappi, Yet Another Python Profiler, provides multithreading and cpu-time +support to profile python programs. + +%package -n python3-%{srcname} +Summary: Yet Another Python Profiler, supports Multithread/CPU time profiling. + +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-gevent + +%description -n python3-%{srcname} +Yappi, Yet Another Python Profiler, provides multithreading and cpu-time +support to profile python programs. + +%prep +%autosetup -n %{srcname}-%{version} -S git + +%build +%py3_build + +%install +%py3_install +mv %{buildroot}%{_bindir}/%{srcname} %{buildroot}%{_bindir}/%{srcname}-%{python3_version} +ln -s %{srcname}-%{python3_version} %{buildroot}%{_bindir}/%{srcname}-3 +ln -s %{srcname}-3 %{buildroot}%{_bindir}/%{srcname} + +%check +export PATH=$PATH:%{buildroot}/usr/bin +export PYTHONPATH=%{buildroot}/%{python3_sitearch} +%{__python3} tests/test_functionality.py +%{__python3} tests/test_hooks.py + +%files -n python3-%{srcname} +%license LICENSE +%doc README.md +%{python3_sitearch}/%{srcname}.py* +%{python3_sitearch}/_%{srcname}*.so +%{python3_sitearch}/__pycache__/%{srcname}* +%{python3_sitearch}/%{srcname}-*.egg-info +%{_bindir}/%{srcname} +%{_bindir}/%{srcname}-3* + +%changelog +* Wed Sep 14 2022 Alfredo Moralejo - 1.3.6-1 +- Update to 1.3.6 +- Backport upstream patches required to support python 3.11 + +* Fri Jul 22 2022 Fedora Release Engineering - 1.3.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Tue Jun 14 2022 Python Maint - 1.3.2-4 +- Rebuilt for Python 3.11 + +* Fri Jan 21 2022 Fedora Release Engineering - 1.3.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Fri Jul 23 2021 Fedora Release Engineering - 1.3.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Wed Jun 16 2021 Joel Capitao - 1.3.2-1 +- Update to 1.3.2 version (#1902302) +- Included patch to support python 3.10 (#1958896) + +* Fri Jun 04 2021 Python Maint - 1.3.1-3 +- Rebuilt for Python 3.10 + +* Wed Jan 27 2021 Fedora Release Engineering - 1.3.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Nov 10 2020 Alfredo Moralejo - 1.3.1-1 +- Update to 1.3.1 version + +* Tue Nov 10 2020 Alfredo Moralejo - 1.3.0-1 +- Update to 1.3.0 version + +* Fri Aug 28 2020 Alfredo Moralejo - 1.2.5-1 +- Update to 1.2.5 version +- Removed python2 subpackage + +* Sat Aug 01 2020 Fedora Release Engineering - 1.0-8 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Wed Jul 29 2020 Fedora Release Engineering - 1.0-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue May 26 2020 Miro Hrončok - 1.0-6 +- Rebuilt for Python 3.9 + +* Thu Jan 30 2020 Fedora Release Engineering - 1.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Thu Oct 03 2019 Miro Hrončok - 1.0-4 +- Rebuilt for Python 3.8.0rc1 (#1748018) + +* Mon Aug 19 2019 Miro Hrončok - 1.0-3 +- Rebuilt for Python 3.8 + +* Fri Jul 26 2019 Fedora Release Engineering - 1.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Feb 21 2019 Alfredo Moralejo - 1.0-1 +- Initial version