-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
GH-91048: Add utils for capturing async call stack for asyncio programs and enable profiling #124640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GH-91048: Add utils for capturing async call stack for asyncio programs and enable profiling #124640
Changes from 1 commit
1b01a91
0fc5511
1d20a51
c8be18e
abf2cb9
20ceab7
72d9321
c9475f6
e1099e9
817f88b
54386ac
98434f0
485c166
8802be7
1ddc9cf
bc9beb8
fd141d4
2d72f24
391defa
d6357fd
bb3b6df
54c99ec
c1a4f09
027d522
c2d5ec6
08d09eb
fe3113b
18ec26d
e4cc462
d5cdc36
83606f2
5edac41
8dc6d34
30884ea
1317658
81b0a31
258ce3d
b9ecefb
b77dcb0
8867946
87d2524
b47bef1
230b7ec
b1d6158
ac51364
c7e59eb
9eba5e1
59121f6
f8f48f0
74c5ad1
067c043
9f04911
0774805
3048493
1f42873
7799391
03ed5c1
21f9ea9
8a43dfa
b3fae68
d0aedf0
df0032a
0ce241b
8f126f6
966d84e
f56468a
404b88a
911fed8
ab511a4
c3c685a
785adeb
a577328
064129a
ce332d9
d6d943f
703ff46
e867863
9cb5b29
61b2b7b
9533ab9
596191d
ad9152e
066bf21
4caeec4
38f061d
a8dd667
cf8f5e5
eda9c7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,7 +75,6 @@ typedef struct { | |
| PyObject *sw_arg; | ||
| } TaskStepMethWrapper; | ||
|
|
||
|
|
||
| #define Future_CheckExact(state, obj) Py_IS_TYPE(obj, state->FutureType) | ||
| #define Task_CheckExact(state, obj) Py_IS_TYPE(obj, state->TaskType) | ||
|
|
||
|
|
@@ -113,6 +112,11 @@ typedef struct _Py_AsyncioModuleDebugOffsets { | |
| uint64_t task_awaited_by_is_set; | ||
| uint64_t task_coro; | ||
| } asyncio_task_object; | ||
| struct _asyncio_thread_state { | ||
| uint64_t size; | ||
| uint64_t asyncio_running_loop; | ||
| uint64_t asyncio_running_task; | ||
| } asyncio_thread_state; | ||
| } Py_AsyncioModuleDebugOffsets; | ||
|
|
||
| GENERATE_DEBUG_SECTION(AsyncioDebug, Py_AsyncioModuleDebugOffsets AsyncioDebug) | ||
|
|
@@ -123,6 +127,11 @@ GENERATE_DEBUG_SECTION(AsyncioDebug, Py_AsyncioModuleDebugOffsets AsyncioDebug) | |
| .task_is_task = offsetof(TaskObj, task_is_task), | ||
| .task_awaited_by_is_set = offsetof(TaskObj, task_awaited_by_is_set), | ||
| .task_coro = offsetof(TaskObj, task_coro), | ||
| }, | ||
| .asyncio_thread_state = { | ||
| .size = sizeof(_PyThreadStateImpl), | ||
| .asyncio_running_loop = offsetof(_PyThreadStateImpl, asyncio_running_loop), | ||
| .asyncio_running_task = offsetof(_PyThreadStateImpl, asyncio_running_task), | ||
| }}; | ||
|
|
||
| /* State of the _asyncio module */ | ||
|
|
@@ -219,7 +228,6 @@ typedef struct { | |
| TaskObj tail; | ||
| TaskObj *head; | ||
| } asyncio_tasks; | ||
|
|
||
| } asyncio_state; | ||
|
|
||
| static inline asyncio_state * | ||
|
|
@@ -268,9 +276,6 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu | |
| static void | ||
| clear_task_coro(TaskObj *task) | ||
| { | ||
| if (task->task_coro != NULL && PyCoro_CheckExact(task->task_coro)) { | ||
| _PyCoro_SetTask(task->task_coro, NULL); | ||
| } | ||
| Py_CLEAR(task->task_coro); | ||
| } | ||
|
|
||
|
|
@@ -279,9 +284,6 @@ static void | |
| set_task_coro(TaskObj *task, PyObject *coro) | ||
| { | ||
| assert(coro != NULL); | ||
| if (PyCoro_CheckExact(coro)) { | ||
| _PyCoro_SetTask(coro, (PyObject *)task); | ||
| } | ||
| Py_INCREF(coro); | ||
| Py_XSETREF(task->task_coro, coro); | ||
| } | ||
|
|
@@ -2160,7 +2162,10 @@ enter_task(asyncio_state *state, PyObject *loop, PyObject *task) | |
| Py_DECREF(item); | ||
| return -1; | ||
| } | ||
| Py_DECREF(item); | ||
|
|
||
| _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); | ||
| assert(ts->asyncio_running_task == NULL); | ||
| ts->asyncio_running_task = item; // strong ref | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't safe. You can only do this when you can guarantee that running loop is the loop of this task. This function can be called from any thread.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea which would work on all cases:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because reading a dictionary is very complex externally. You cannot run Python code, you'd have to recreate the implementation on the other end, with all the pointer indirection.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kumar, this is a very good catch. I agree that additional checks are warranted here. But I disagree that the current approach overall isn't valid. While technically "enter_task" can be called from some unrelated thread, in practice, it will only be called from one place -- around the Task's stepping function. For our asyncio Task that will always be the case, for third-party tasks that don't inherit from asyncio.Task I have to comments:
Therefore I'd argue that the current approach is OK, but I'm adding an additional check to the logic as well as an extensive comment elaborating on what's going on.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This may happen if in case someone is running two separate event loops in two threads and tries to introspect the call stack of the other. I understand that this may seem far fetch but still better safe than sorry. Pushed a minor suggestion to your comment.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That will work, introspection doesn't rely on |
||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -2185,14 +2190,16 @@ leave_task_predicate(PyObject *item, void *task) | |
|
|
||
| static int | ||
| leave_task(asyncio_state *state, PyObject *loop, PyObject *task) | ||
| /*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/ | ||
| { | ||
| int res = _PyDict_DelItemIf(state->current_tasks, loop, | ||
| leave_task_predicate, task); | ||
| if (res == 0) { | ||
| // task was not found | ||
| return err_leave_task(Py_None, task); | ||
| } | ||
|
|
||
| _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); | ||
| Py_CLEAR(ts->asyncio_running_task); | ||
| return res; | ||
| } | ||
|
|
||
|
|
@@ -3960,7 +3967,9 @@ module_clear(PyObject *mod) | |
| Py_CLEAR(state->iscoroutine_typecache); | ||
|
|
||
| Py_CLEAR(state->context_kwname); | ||
|
|
||
| _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); | ||
| Py_CLEAR(ts->asyncio_running_loop); | ||
| Py_CLEAR(ts->asyncio_running_task); | ||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -3990,7 +3999,6 @@ module_init(asyncio_state *state) | |
| goto fail; | ||
| } | ||
|
|
||
|
|
||
| state->context_kwname = Py_BuildValue("(s)", "context"); | ||
| if (state->context_kwname == NULL) { | ||
| goto fail; | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.