如何在调试 cpython 时遍历 Python 操作码?

har*_*k24 5 c python gdb cpython

我想了解Python解释器的功能。我了解操作码的生成过程,并希望更好地理解解释器部分。为此,我在互联网上阅读了很多内容,并了解了python 解释器(Cpython)中的文件for (;;)循环ceval.c

现在我想解释以下Python代码a.py

a = 4
b = 5
c = a + b
Run Code Online (Sandbox Code Playgroud)

当我做python -m dis a.py

  1           0 LOAD_CONST               0 (4)
              2 STORE_NAME               0 (a)

  2           4 LOAD_CONST               1 (5)
              6 STORE_NAME               1 (b)

  3           8 LOAD_NAME                0 (a)
             10 LOAD_NAME                1 (b)
             12 BINARY_ADD
             14 STORE_NAME               2 (c)
             16 LOAD_CONST               2 (None)
             18 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

现在我已将调试点switch(opcode)放入ceval.c. 现在,当我启动调试器时,它会出现这个位置 2000 多次。我认为这是因为在开始之前,Python 还必须做一些其他的解释工作。所以,我的问题是如何仅调试相关的操作码指令?

基本上,我如何知道我正在调试的指令实际上来自我创建的程序?

请帮我解决同样的问题。提前致谢。

Min*_*Max 3

我进行了大量的CPython调试,以便更好地理解它的工作方式。我通过编写C解决了无法在Python源文件中设置gdb断点的问题断点的问题。

\n\n

想法CPython是一个用C语言编写的大程序。我们可以像任何C程序一样轻松地调试它- 这里没有问题。如果我们想在_PyType_Lookup函数启动时停止执行,我们只需运行一个break _PyType_Lookup命令即可。因此,如果我们将自己的C函数添加到CPython程序中,例如cbreakpoint,我们可以在每次cbreakpoint调用时停止执行。cbreakpoint如果我们找到将该函数插入到 中的方法source.py,我们将获得所需的功能 - 每次解释器看到 时cbreakpoint,它都会停止(如果我们break cbreakpoint之前设置过)。我们可以通过编写C来做到这一点扩展来做到这一点”。

\n\n

我是如何做到的(我可能会错过一些东西,因为我是从记忆中复制的):

\n\n
    \n
  1. CPython源下载到~/learning_python/cpython-master目录中并编译它。有一些错综复杂的问题 -无法摆脱 \xe2\x80\x9c 值已在 GDB 中优化出 \xe2\x80\x9d
  2. \n
  3. 本身创建了一个模块 -my_breakpoint.c .
  4. \n
  5. 创建了一个安装文件 -my_breakpoint_setup.py .
  6. \n
  7. 运行一个

    \n\n
    ~/learning_python/cpython-master/python my_breakpoint_setup.py build\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    命令。它创建了一个my_breakpoint.cpython-38dm-x86_64-linux-gnu.so文件。

  8. \n
  9. 将上一步中的共享对象文件复制到CPython中 Lib目录中:

    \n\n
    cp -iv my_breakpoint.cpython-38dm-x86_64-linux-gnu.so ~/learning_python/cpython-master/Lib/\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    为了方便起见,需要进行复制,否则我们应该将此.so文件放在我们想要使用(导入)此模块的任何目录中。

  10. \n
  11. 现在,我们可以做如下source.py

    \n\n
    #!/usr/bin/python3\n\nfrom my_breakpoint import cbreakpoint\n\ncbreakpoint(1)\na = 4\n\ncbreakpoint(2)\nb = 5\n\ncbreakpoint(3)\nc = a + b\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    要执行这个文件,我们必须使用我们的~/learning_python/cpython-master解释器,而不是系统的解释器python3,因为系统的 python 没有my_breakpoint模块:

    \n\n
    ~/learning_python/cpython-master/python source.py\n
    Run Code Online (Sandbox Code Playgroud)
  12. \n
  13. 要调试此文件,请执行以下操作:

    \n\n
    gdb --args ~/learning_python/cpython-master/python -B source.py\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    然后,里面gdb

    \n\n
    (gdb) start\n\n(gdb) break cbreakpoint\nFunction "cbreakpoint" not defined.\nMake breakpoint pending on future shared library load? (y or [n]) y\nBreakpoint 2 (cbreakpoint) pending.\n\n(gdb) cont\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    有一个问题。当您按下, 时contgdb会停止在函数的开头cbreakpoint,并且您需要执行许多next命令来跳过此函数以及CPython函数调用代码以实现所需的 Python 代码执行的开始。或者你可以在之后设置一个新的断点cbreakpoint或者你可以在被击中

    \n\n
    (gdb) break ceval.c:1080 ### The LOAD_CONST case beginning\n(gdb) cont\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    但是,在多次执行此操作后,我将这些操作自动化,因此您只需将这些行添加到~/.gdbinit中即可中即可:

    \n\n
    set breakpoint pending on\nbreak cbreakpoint\n    command $bpnum\n    tbreak ceval.c:1098\n        command $bpnum\n        n\n        end\n    cont\n    end\nset breakpoint pending off\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    现在,您只需像第 7 步中那样启动gdb并执行以下操作:

    \n\n
    (gdb) start\n(gdb) cont\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    然后您将跳转到代码执行的开头source.py

  14. \n
\n\n

my_breakpoint.c

\n\n
#include <Python.h>\n\nstatic PyObject* cbreakpoint(PyObject *self, PyObject *args){\n    int breakpoint_id;\n\n    if(!PyArg_ParseTuple(args, "i", &breakpoint_id))\n        return NULL;\n\n    return Py_BuildValue("i", breakpoint_id);\n}\n\nstatic PyMethodDef my_methods[] = { \n    {"cbreakpoint", cbreakpoint, METH_VARARGS, "breakpoint function"},  \n    {NULL, NULL, 0, NULL}\n};\n\nstatic struct PyModuleDef my_breakpoint = { \n    PyModuleDef_HEAD_INIT,  \n    "my_breakpoint",\n    "the module for setting C breakpoint in the Python source",\n    -1, \n    my_methods\n};\n\nPyMODINIT_FUNC PyInit_my_breakpoint(void){\n    return PyModule_Create(&my_breakpoint);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

my_breakpoint_setup.py

\n\n
from distutils.core import setup, Extension\n\nmodule = Extension(\'my_breakpoint\', sources = [\'my_breakpoint.c\'])\n\nsetup (name = \'PackageName\',\n       version = \'1.0\',\n       description = \'This is a package for my_breakpoint module\',\n       ext_modules = [module])\n
Run Code Online (Sandbox Code Playgroud)\n\n

聚苯乙烯

\n\n

我过去问过同样的问题,它对你可能有用:The best way to set a Breakpoint in the Python source code while debug CPython by GDB

\n