如何记录Python程序执行的CPU指令?

3

我理解Python源代码被编译成字节码,然后由Python虚拟机(比如CPython)解释。如果我理解正确,这意味着虚拟机解析字节码指令,并在运行时决定应按照什么方式执行CPU指令。

我的问题:

  • 是否可以记录在你的计算机上执行特定Python文件(.py)解释而产生的实际CPU指令?我明白可能不容易(甚至不可行)得到.py文件和CPU指令之间的一对一的对应关系,但最接近的是什么?
  • 更进一步:是否有可能记录与特定进程相对应的已执行指令?

我可能不理解你想要看到的内容,但如果碰巧是这方面的,并且你不知道这个模块:dis 是否能让你更接近你想要的?显然它不能实时记录。 - roganjosh
免责声明:我的问题主要是出于好奇。我怀疑查看解释代码的CPU实际指令可能是不切实际的。我只是想知道是否有可能。 - rect0x51
1
请随意在https://reverseengineering.stackexchange.com/上提出反向工程问题。 - julian
2个回答

4
在Linux上使用strace,它会显示由任何程序(包括Python)发出的每个系统调用。在Windows上,您需要使用类似于wtLogger.exe这样的东西来追踪所有库调用(而不仅仅是系统调用)。
您可以使用像gdb这样的调试器实时查看机器代码,由于您有CPython源代码,更好的选择是只需使用调试符号编译它,然后在C调试器中运行它,这可以给您提供高级别的调用堆栈,这将更容易理解。

仅仅获取系统调用就是你所能做的最好的吗?难道不能更详细地观察CPU指令吗? - rect0x51
哇,直接在Python上使用gdb是可能的,真没想到!这回答了我的第一个问题,谢谢。那么一般情况下如何观察进程呢?我的意思是……调试器通过暂停执行并观察变量(如寄存器)来工作,对吧?如果没有像反汇编程序(比如C语言)中硬编码的CPU指令,那么就不可能知道实际运行了哪些CPU指令了吗? - rect0x51
3
gdb 是一款非常出色的软件。它可以生成指令日志,甚至还能记录并回放代码执行过程,同时支持倒带操作! - nosklo
1
这个旧的维基页面 https://wiki.python.org/moin/DebuggingWithGdb 专门介绍了使用gdb调试Python。也许可以给你一些正确方向上的指针? - nosklo
1
@81rect:你不是在对Python代码使用gdb,而是在对Python解释器的x86机器代码使用它,这本身就是一个程序。解释Python的+运算符涉及运行大量指令,而不仅仅是add rax,rcx。你只能从JIT编译器中获得这个。但是,是的,你可以调试任何用户空间进程,因为在最低级别上,一切都只是机器代码。 - Peter Cordes
显示剩余6条评论

0

我是如何使用lldb(类似于gdb)通过VS Code在我的ARM CPU(运行在Apple silicon上的macOS 13)上查看CPython的CPU指令的:

  1. 安装CodeLLDB VS Code扩展

  2. git clone https://github.com/python/cpython.git

  3. cd cpython

  4. git checkout 20cf32e761(因为最新的代码可能有问题)

  5. 编译CPython(详见README文件如何进行测试构建),这将生成一个名为“python.exe”的可执行文件:

brew install pkg-config openssl@1.1 xz gdbm tcl-tk

CFLAGS="-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" \
LDFLAGS="-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" \
PKG_CONFIG_PATH="$(brew --prefix tcl-tk)/lib/pkgconfig" \
./configure \
  --with-pydebug \
  --with-openssl="$(brew --prefix openssl@1.1)"

make
  1. 在 VS Code 中打开 CPython 源代码目录(code .),并添加一个包含以下内容的 .vscode/launch.json 文件(将 args 设置为要传递给 python 可执行文件的参数,在此示例中,我运行的是 python -c '30858 + 7',这只是让 Python 计算 30858 + 7 并退出而不打印任何内容):
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/python.exe",
            // Add two integers
            "args": ["-c", "30858 + 7"],
            "cwd": "${workspaceFolder}"
        }
    ]
}

添加断点。可以尝试在main()函数或者评估循环(一个巨大的switch语句,检查操作码),该语句将会在generated_cases.c.h文件中(不在源代码中)。对于我的30858 + 7示例,可以选择_PyLong_Add()函数,因为它实现了加法。
按F5键运行命令。

此时,您应该已经触发了断点,并能够看到执行C代码的位置。要查看当前的汇编指令,请按Cmd+Shift+P并执行“LLDB:显示反汇编…”命令,然后选择“始终”,您可以通过再次运行命令并选择“自动”来返回C视图。您可以在左上角的“寄存器”下拉菜单中查看寄存器值,在当前变量值下方。

这是机器指令,将存储在寄存器x20中的30858(十六进制中的788A)加上存储在寄存器x0中的7,并将结果存储在寄存器x0中(我稍微修改了代码,将加法放在单独的一行,以便更容易地在add指令处设置断点):

screenshot of VS Code


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接