Обновление: Если это поможет сузить вопрос для кого-либо, этот вопрос действительно больше касается API CPython и того, отсутствует ли у меня какой-либо способ получения необходимой информации.Я не спрашиваю решения более широкой проблемы, а скорее работаю над более широкой проблемой, я задал конкретный вопрос о CPython и о том, предоставил ли он способ, который не был очевиден для меня, чтобы получить некоторую конкретную информацию.Я только пометил вопрос c , потому что по своей природе это требует некоторого опыта в C, но это , а не общий вопрос о C или конкретных архитектурах / платформах.
См.также примечание ниже об одном возможном подходе, использующем PyEval_SetTrace
, хотя я надеялся, что они могли бы быть лучшим способом.В качестве другого примера, существует PyMain_GetArgcArgv
, который бы сработал, но только , если интерпретатор Python был запущен из исполняемого файла python
, а не встроен (что может быть приемлемым ограничением).Также PyMain_GetArgcArgv
не задокументировано как часть API.
Я хотел бы иметь возможность найти адрес фрейма стека C (т. Е. __builtin_frame_address(0)
, как определено соответствующим образом для этой платформы)это наиболее тесно связано с фреймом стека Python.В частности, я хотел бы найти самый внешний фрейм - или близкий к нему - связанный с вызовом функции Python, который будет определен лучше ниже.
Вкратце, контекст заключается в том, что я 'm оборачивает библиотеку C, которая использует неясный сборщик мусора специального назначения, которому нужен указатель на дно стека - по крайней мере, так далеко назад, поскольку существуют локальные переменные, указывающие на объекты, которые должны отслеживаться GC.В идеале я мог бы разметить дно стека один раз;в этом случае, поскольку он упакован в модуль Python, достаточно перейти к самому внешнему фрейму стека Python.Наилучшей доступной альтернативой было бы вручную отмечать дно стека при каждом входе в библиотеку, но это не идеально, а также требовало бы исправления библиотеки (что может понадобиться в любом случае), поскольку в настоящее время это позволяет только устанавливать стекнижний адрес один раз, во время функции инициализации.
То, как именно фрейм стека Python связан с фреймом стека C, плохо определено, поскольку технически нет жесткой и быстрой связи между двумя,Тем не менее, для практической цели он будет на уровне или близко (в зависимости от оптимизации компилятора и т. Д.) К вызову PyEval_EvalFrameEx
для выполняемого кадра (меня не интересуют кадры, которыев настоящее время не в стеке вызовов, поскольку в данном случае это, очевидно, бессмысленный вопрос.)
Это все явно зависит от CPython, и это нормально для моих целей.В этом случае технически нет причин, по которым реализация структуры CPython PyFrameObject
не могла бы переносить такую информацию на одного из своих членов, но, насколько я могу судить, на PyFrameObject
s не хранится ничего, что позволило бы мнесвязать его с фреймом стека C.Например, моя задача была бы «решена» достаточно хорошо, для целей данного приложения, если бы было в PyFrameObject
как f_cstack
что-то вроде:
PyObject* _Py_HOT_FUNCTION
_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
{
...
f->f_executing = 1;
f->f_cstack = &f;
...
}
Это сработало быAFAICT - даже несмотря на то, что f
обычно передается в регистр, мой gcc будет обрабатывать такой код, помещая f
в стек и сохраняя его адрес в стеке.К сожалению, в настоящее время я не могу найти ничего подобного.
Лучшая идея, которую я смог придумать, - это зарегистрировать обработчик PyEval_SetTrace
, который будет вызыватьсявходя во фреймы стека Python и, таким образом, я получаю возможность рутяться вокруг стека оттуда.Но на самом деле для рассматриваемого приложения мне нужно только найти «самый внешний» вызов PyEval_EvalFrameEx
, который будет один для любого работающего кода Python.Так что установка обратного вызова трассировки не обязательно даст мне это, и это дополнительные издержки, которые мне не нужны для каждого вызова функции.
Боюсь, в настоящее время нет хорошего решения для этого, хотя было бы удобно, если бы оно было.
(PS Меня также волнует только основной стек, а не потоки, хотя какое-либо решениеэто будет работать в основном потоке, вероятно, будет иметь аналогичное решение для вспомогательных потоков).