SWIG is used with different types of target languages including common scripting languages such as javascript, Perl, php, python, Tcl and Ruby.
上面那段是 SWIG 的官网介绍,简单地说,SWIG 是一个用于 C/C++ 和高层脚本语言交互的工具。脚本语言在 SWIG 的辅助下可以直接调 C/C++ 的程序。
简单试了一下 SWIG 在 Python 里面的用法,然后测试了一下 gdb 以及 VS 的远程调试 (红红火火恍恍惚惚,其实想记录的重点是这个)。
以下内容均在 WSL 里面完成。
Python-SWIG直接 apt-get 把 SWIG 装上,然后就可以开始瞎搞了。
首先随便写个 C 的函数官方的文档里面用的是个简单的阶乘的例子,这里也用这个好了。
cfact.h
intfact(intn);cfact.c
#include"cfact.h" intfact(intn) { if (n < 0) return 0; else if (n == 0) return 1; else return n * fact(n-1); } 写 SWIG 部分的内容,并且生成对应的封装代码创建一个 SWIG 的文件:
cfact.i
%module cfact %{ #defineSWIG_FILE_WITH_INIT #include"cfact.h" %} intfact(intn);然后用 swig 命令生成封装代码:
$ swig -python cfact.i调用完了之后会生成两个新的文件 cfact.py 以及 cfact_wrap.c 。
编译生成 Python 模块Python 与 C/C++ 的交互是以模块的方式进行的,C/C++ 代码编译生成运行库之后,以模块的方式链接到 Python 的运行时中。
Python 自带一个 distutils 工具,用于创建扩展模块,写一个配置文件:
setup.py
from distutils.core import setup, Extension cfact_module = Extension('_cfact', sources=['cfact_wrap.c', 'cfact.c'], ) setup (name = 'cfact', version = '0.1', author = "Chen Fan", description = """Simple swig test""", ext_modules = [cfact_module], py_modules = ["cfact"], )在目录下执行:
$ python setup.py build编译得到完整的 Python 模块。
$ python setup.py build running build running build_py creating build creating build/lib.linux-x86_64-2.7 copying cfact.py -> build/lib.linux-x86_64-2.7 running build_ext building '_cfact' extension creating build/temp.linux-x86_64-2.7 x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c cfact_wrap.c -o build/temp.linux-x86_64-2.7/cfact_wrap.o x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c cfact.c -o build/temp.linux-x86_64-2.7/cfact.o x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/cfact_wrap.o build/temp.linux-x86_64-2.7/cfact.o -o build/lib.linux-x86_64-2.7/_cfact.so可以看到 cfact_wrap.c 和 cfact.c 首先被编译成 build/temp.linux-x86_64-2.7/ 目录下的 cfact_wrap.o 和 cfact.o 两个文件。
之后再编译成一个完整的 _cfact.so 运行库。
切到 build/lib.linux-x86_64-2.7/ 目录下就能用 Python 测试啦:
# jcf @ J-CF-YOGA in ~/swig_test [21:36:17] $ cd build/lib.linux-x86_64-2.7 # jcf @ J-CF-YOGA in ~/swig_test/build/lib.linux-x86_64-2.7 [21:41:45] $ python Python 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import cfact >>> cfact.fact(5) 120 >>> 上 gdb 调试Python 进程是已经在运行了的,要用 gdb 调就只能附加上进程号来启动,可以在 Python 里面 import os 然后用 os.getpid() 获取进程号,或者用 top/htop 啥的直接看一下 Python 的进程号,然后在 gdb 里面启动即可。
$ gdb -p pidgdb 正常启动,然后这时候可能会报找不到库的调试信息的错误,直接添加 fact 的断点会加不上。用 info sharedlibrary 命令看一下现在追踪到的库信息:
(gdb) info sharedlibrary From To Syms Read Shared Object Library 0x00007fa4d2be59f0 0x00007fa4d2bf2471 Yes /lib/x86_64-linux-gnu/libpthread.so.0 0x00007fa4d282f520 0x00007fa4d2974183 Yes /lib/x86_64-linux-gnu/libc.so.6 0x00007fa4d2600ed0 0x00007fa4d26019ce Yes /lib/x86_64-linux-gnu/libdl.so.2 0x00007fa4d23f0f10 0x00007fa4d23f1804 Yes /lib/x86_64-linux-gnu/libutil.so.1 0x00007fa4d21d1e00 0x00007fa4d21e1bf8 Yes (*) /lib/x86_64-linux-gnu/libz.so.1 0x00007fa4d1ec5610 0x00007fa4d1f34056 Yes /lib/x86_64-linux-gnu/libm.so.6 0x00007fa4d2e00ae0 0x00007fa4d2e1b490 Yes /lib64/ld-linux-x86-64.so.2 0x00007fa4d1b32720 0x00007fa4d1b33ef6 Yes (*) /usr/lib/python2.7/lib-dynload/readline.x86_64-linux-gnu.so 0x00007fa4d18f30d0 0x00007fa4d1913ee5 Yes (*) /lib/x86_64-linux-gnu/libreadline.so.6 0x00007fa4d16bc3d0 0x00007fa4d16c8028 Yes (*) /lib/x86_64-linux-gnu/libtinfo.so.5 No ./_cfact.so (*): Shared library is missing debugging information._cfact.so 由于不在 gdb 的默认库搜索路径下,所以找不到,需要手动加上路径。
关于 gdb 的库搜索策略,参见 这篇博文 。
这里简单地在 solib-search-path 里面加上 _cfact.so 的位置就好啦:
(gdb) show solib-search-path The search path for loading non-absolute shared library symbol files is . (gdb) set solib-search-path :/home/jcf/swig_test/build/lib.linux-x86_64-2.7/ Reading symbols from /home/jcf/swig_test/build/lib.linux-x86_64-2.7/_cfact.so...done. Loaded symbols for /home/jcf/swig_test/build/lib.linux-x86_64-2.7/_cfact.so (gdb)可以看到 set 完之后 _cfact.so 已经自动被载入完成了。
这时候在 fact 这个函数上设断点就能够正常找到了:
(gdb) break fact Breakpoint 1 at 0x7fa4d14a3020: file cfact.c, line 5. (gdb) c Continuing.在 Python 窗口里面再调一次 cfact.fact 函数,gdb 成功断在了这里:
Breakpoint 1, fact (n=n@entry=6) at cfact.c:5 5 if (n < 0) return 0; (gdb) l 1 #include "cfact.h" 2 3 int fact(int n) 4 { 5 if (n < 0) return 0; 6 else if (n == 0) return 1; 7 else return n * fact(n-1); 8 }(gdb)调用栈也是比较清晰的,Python 通过生成的封装文件 cfact_wrap.c 最后才调用到最初写的 cfact.c 文件中去。
(gdb) bt #0 fact (n=n@entry=6) at cfact.c:5 #1 0x00007fa4d14a1c45 in _wrap_fact (self=<optimized out>, args=<optimized out>) at cfact_wrap.c:3119 #2 0x0000000000523f6d in PyEval_EvalFrameEx () #3 0x0000000000567d14 in ?? () #4 0x0000000000465a2d in PyRun_InteractiveOneFlags () #5 0x0000000000465b49 in PyRun_InteractiveLoopFlags () #6 0x00000000004661fe in PyRun_AnyFileExFlags () #7 0x0000000000466d92 in Py_Main () #8 0x00007fa4d2831f45 in __libc_start_main (main=0x466e50 <main>, argc=1, argv=0x7fffcd30af38, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffcd30af28) at libc-start.c:287 #9 0x0000000000577c2e in _start ()