首页 > 编程问答 >调试用 C 编写的 Python 扩展

调试用 C 编写的 Python 扩展

时间:2024-07-21 03:08:20浏览次数:11  
标签:python c gdb lldb python-extensions

我非常熟悉编写 C 代码,并且很擅长编写 Python 代码。 我正在尝试学习如何用 C 编写可以从 OSX 10.15.7 上的 Python-3.9.X 调用的模块。我已经得到了几个“hello world”类型的示例,但是对于复杂的示例,我正在努力弄清楚如何调试我编写的 C 扩展。

MWE:

src/add.c

// The C function that actually does the work
static PyObject * add_c_func(PyObject *self, PyObject *args)
{
    int a=0;
    int b=0;
    int c=0;

    // This determines the number arguments used by add_c_func
    if (!PyArg_ParseTuple(args, "iii", &a, &b, &c))
    {
        return NULL;
    }

    printf("%i\n", a+b+c);

    Py_RETURN_NONE;
}

// This defines the function used by
static PyMethodDef AddMethods[] = {
    {"add_py_func", add_c_func, METH_VARARGS, "Add three numbers."},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef addpymod =
{
    PyModuleDef_HEAD_INIT,
    "addpymod",     /* name of module */
    "",          /* module documentation, may be NULL */
    -1,          /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
    AddMethods
};

PyMODINIT_FUNC PyInit_addpymod(void)
{
    return PyModule_Create(&addpymod);
}

setup.py :

from setuptools import setup, Extension
setup(
    name='addpymod',
    version='1.0',
    description='Python Package with Hello World C Extension',
    ext_modules=[
        Extension(
            'addpymod',
            sources=['src/add.c'],
            py_limited_api=True)
    ],
)

编译/安装(默认情况下它使用 clang ):

python setup.py install

尝试调试:

(py-ext-test) local: understand-python-c-ext $ gdb
GNU gdb (GDB) 10.1
.
.
.
(gdb) b add.c : 20
No symbol table is loaded.  Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (add.c : 20) pending.
(gdb) python
>import addpymod
>addpymod.add_py_func(10,10,10)   # Why didn't my breakpoint get hit?
>Quit
# This clearly failed, I'm not even sure where my stdout is

这里有多个级别的复杂性,我确信我被不止一个级别绊倒了。

问题 :

  1. 如何使用 add.c (首选)或可能 gdb 调试我的 lldb ,因为它是默认使用 clang -g 选项编译的?

以下是关于如何调试 C Python 扩展的逐步指南:

1. 使用调试符号进行编译

  • 必须使用 -g 标志编译 C 代码和扩展模块,以便包含调试符号。这允许调试器将机器代码映射回你的源代码,从而实现有意义的断点和代码步进。

    修改你的 setup.py 文件,将 extra_compile_args extra_link_args 选项添加到 Extension 中:

    ```python from setuptools import setup, Extension

    setup( name='addpymod', version='1.0', description='Python Package with Hello World C Extension', ext_modules=[ Extension( 'addpymod', sources=['src/add.c'], py_limited_api=True, extra_compile_args=['-g'], # 添加此行 extra_link_args=['-g'] # 添加此行 ) ], ) ```

    这将确保在编译和链接过程中传递 -g 标志。

  • 使用以下命令重新安装扩展模块:

    bash python setup.py install

2. 使用 GDB 进行调试

  • 启动 GDB 并加载 Python: 打开终端并运行:

    bash gdb python

  • 设置断点: 在你的 C 代码中设置一个断点。例如,要在 add_c_func 函数的开头设置一个断点:

    gdb (gdb) b add.c:6

  • 运行 Python 代码:

    gdb (gdb) run -m addpymod

    这将在 gdb 中启动 Python,加载你的模块并停止在你的断点处。

  • 使用 GDB 命令: 现在你可以使用 GDB 命令,例如:

    • n (next)单步执行下一行代码。
    • s (step)步入函数调用。
    • c (continue)继续执行,直到下一个断点或程序结束。
    • p (print)打印变量的值。
    • bt (backtrace)查看调用堆栈。

3. 使用 LLDB 进行调试(如果你坚持要这样做)

LLDB 的步骤类似于 GDB。以下是如何使用 LLDB 进行调试:

1.  **使用调试符号进行编译:** 就像使用 GDB 一样,你需要使用 `-g` 标志进行编译。

2.  **启动 LLDB:**
    ```bash
    lldb python
    ```

3.  **设置断点:**
    ```lldb
    (lldb) b add.c:6
    ```

4.  **运行 Python 代码:**
    ```lldb
    (lldb) run -m addpymod
    ```

5.  **使用 LLDB 命令:** LLDB 使用与 GDB 类似的命令,但有一些区别。例如:

    *   `n` 或 `next`:单步执行下一行代码。
    *   `s` 或 `step`:步入函数调用。
    *   `c` 或 `continue`:继续执行。
    *   `p` 或 `frame variable`:打印变量的值。
    *   `bt` 或 `thread backtrace`:查看调用堆栈。

其他提示

  • 打印 Python 对象: 当使用 p 在 GDB 或 LLDB 中打印 PyObject* 时,你只会看到原始指针值。要查看 Python 对象的实际内容,你需要使用 Python 调试器函数。将以下内容添加到你的 .gdbinit 中,以获得更易读的 Python 对象表示形式:

``` python import gdb

class PyPrintCommand(gdb.Command): "Print a Python object." def init (self): gdb.Command. init (self, "ppy", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)

   def invoke(self, arg, from_tty):
       frame = gdb.selected_frame()
       value = gdb.parse_and_eval(arg)
       if hasattr(value, '__class__'):
           print(value.__class__.__name__ + ':', value)
       else:
           print(value)

PyPrintCommand() end ```

现在你可以使用 ppy <variable_name> 来打印 Python 对象。 * 考虑使用 IDE: 如果你不习惯使用命令行调试器,许多 IDE(例如 Visual Studio Code、CLion 和 PyCharm)都支持调试 Python C 扩展。

调试 C Python 扩展可能具有挑战性,但通过遵循这些步骤并使用正确的工具,你可以有效地识别和修复代码中的问题。

标签:python,c,gdb,lldb,python-extensions
From: 71866037

相关文章

  • 具有块大小选项的 Python pandas read_sas 因索引不匹配而失败并出现值错误
    我有一个非常大的SAS文件,无法容纳在我的服务器内存中。我只需要转换为镶木地板格式的文件。为此,我使用pandas中chunksize方法的read_sas选项分块读取它。它主要是在工作/做它的工作。除此之外,一段时间后它会失败并出现以下错误。此特定SAS文件有794......
  • 使用 requests 包 python 时打开文件太多
    我正在使用Pythonrequests包向API发出大量请求。然而,在某些时候,我的程序由于“打开的文件太多”而崩溃。当我明确关闭我的会话时,我真的不知道这是怎么回事。我使用以下代码:importrequestsimportmultiprocessingimportnumpyasnps=requests.session()s.keep......
  • 在 Windows 中将 EOF 发送到 PyCharm 控制台
    我正在Windows上学习PyCharmIDE。如何将文件结束信号发送到其调试控制台?CTRL+Z不起作用。Ctrl+Z确实在PyCharm的控制台中发送EOF(文件结束)。但是,它的行为方式可能与的预期不同。在大多数控制台(例如cmd或PowerShell)中,Ctrl+Z......
  • Python 是一种选择性解释语言吗?为什么下面的代码不起作用?
    由于程序是从上到下运行的,为什么下面的代码不执行块中的第一行就直接抛出错误?if5>2:print("TwoislessthanFive!")print("Fiveisgreaterthantwo!")错误:文件“/Users/____/Desktop/Pythonpractise/practise.py”,第3行print("五比二大!")Indentati......
  • Picovoice Porcupine 自定义唤醒词不起作用,文件路径问题
    我在picovoice网站上训练了自定义唤醒词并下载了ZIP文件。然后我将其解压并复制文件路径。这是我的代码:importstructimportpyaudioimportpvporcupineporcupine=Nonepaud=Noneaudio_stream=Nonetry:porcupine=pvporcupine.create(access_key="blahblah",keyw......
  • 裁剪时间变量 Python Matplotlib Xarray
    我不确定这是否是一个愚蠢的问题,但我想按时间变量剪辑.nc文件。我在xarray中打开了数据集,但以下ds.sel行(之前已运行)仅返回错误。ds=xr.open_dataset('/Users/mia/Desktop/RMP/data/tracking/mcs_tracks_2015_11.nc')selected_days=ds.sel(time=slice('2015-11-22',......
  • 用于匹配两个数据列表中的项目的高效数据结构 - python
    我有两个列表,其中一个列表填充ID,另一个列表填充进程名称。多个进程名称可以共享一个ID。我希望能够创建一个可以使用特定ID的数据结构,然后返回与该ID关联的进程列表。我还希望能够使用特定的进程名称并返回与其连接的ID列表。我知道我可以为此创建一个字典,但是I......
  • 有人可以解决我的代码中的问题吗?而且我无法在我的电脑上安装 nsetools。如何在 python
    从nsetools导入Nseimportpandasaspdnse=Nse()all_stock_codes=nse.get_stock_codes()companies_with_low_pe=[]对于all_stock_codes中的代码:如果代码=='符号':继续尝试:stock_quote=nse.get_quote(代码)pe_ratio=stock_quote.get('priceT......
  • 没有名为“Crypto”的模块
    当我运行此代码时,fromfirebaseimportfirebase,出现以下错误:ModuleNotFoundError:Nomodulenamed'Crypto'我已尝试以下操作:pipinstallcryptoppip3installcryptopipinstallpycryptodomepipinstallpycryptodomex终端结果显示:Requirementalr......
  • 将 python 脚本的 stdin 重定向到 fifo 会导致 RuntimeError: input():lost sys.stdin
    我有这个python脚本,它的作用是充当服务器,它从重定向到fifo的stdin读取命令:test.py:whileTrue:try:line=input()exceptEOFError:breakprint(f'Received:{line}')在bash中运行命令:mkfifotestfifotest.py<testfifo......