我理解Python源代码被编译成字节码,然后由Python虚拟机(比如CPython)解释。如果我理解正确,这意味着虚拟机解析字节码指令,并在运行时决定应按照什么方式执行CPU指令。
我的问题:
- 是否可以记录在你的计算机上执行特定Python文件(.py)解释而产生的实际CPU指令?我明白可能不容易(甚至不可行)得到.py文件和CPU指令之间的一对一的对应关系,但最接近的是什么?
- 更进一步:是否有可能记录与特定进程相对应的已执行指令?
我理解Python源代码被编译成字节码,然后由Python虚拟机(比如CPython)解释。如果我理解正确,这意味着虚拟机解析字节码指令,并在运行时决定应按照什么方式执行CPU指令。
我的问题:
strace
,它会显示由任何程序(包括Python)发出的每个系统调用。在Windows上,您需要使用类似于wt或Logger.exe这样的东西来追踪所有库调用(而不仅仅是系统调用)。gdb
这样的调试器实时查看机器代码,由于您有CPython源代码,更好的选择是只需使用调试符号编译它,然后在C调试器中运行它,这可以给您提供高级别的调用堆栈,这将更容易理解。gdb
是可能的,真没想到!这回答了我的第一个问题,谢谢。那么一般情况下如何观察进程呢?我的意思是……调试器通过暂停执行并观察变量(如寄存器)来工作,对吧?如果没有像反汇编程序(比如C语言)中硬编码的CPU指令,那么就不可能知道实际运行了哪些CPU指令了吗? - rect0x51gdb
是一款非常出色的软件。它可以生成指令日志,甚至还能记录并回放代码执行过程,同时支持倒带操作! - nosklogdb
,而是在对Python解释器的x86机器代码使用它,这本身就是一个程序。解释Python的+
运算符涉及运行大量指令,而不仅仅是add rax,rcx
。你只能从JIT编译器中获得这个。但是,是的,你可以调试任何用户空间进程,因为在最低级别上,一切都只是机器代码。 - Peter Cordes我是如何使用lldb
(类似于gdb
)通过VS Code在我的ARM CPU(运行在Apple silicon上的macOS 13)上查看CPython的CPU指令的:
安装CodeLLDB VS Code扩展
git clone https://github.com/python/cpython.git
cd cpython
git checkout 20cf32e761
(因为最新的代码可能有问题)
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
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()
函数,因为它实现了加法。此时,您应该已经触发了断点,并能够看到执行C代码的位置。要查看当前的汇编指令,请按Cmd+Shift+P并执行“LLDB:显示反汇编…”命令,然后选择“始终”,您可以通过再次运行命令并选择“自动”来返回C视图。您可以在左上角的“寄存器”下拉菜单中查看寄存器值,在当前变量值下方。
这是机器指令,将存储在寄存器x20中的30858
(十六进制中的788A
)加上存储在寄存器x0中的7
,并将结果存储在寄存器x0中(我稍微修改了代码,将加法放在单独的一行,以便更容易地在add
指令处设置断点):
dis
是否能让你更接近你想要的?显然它不能实时记录。 - roganjosh