python通过ctypes调用C/C++ SDK,当SDK异常时,同时打印C/C++/Python的栈信息

python通过ctypes调用C/C++ SDK,当SDK异常时,同时打印C/C++/Python的栈信息

本文演示了python通过ctypes调用C/C++ SDK,当SDK异常时,同时打印C/C++/Python的栈信息.基于traceback、addr2line、PyErr_SetString、backtrace_symbols

一.复现步骤

cat > print_backtrace.h <<-'EOF'
#ifndef __PRINT_BACKTRACE_H__
#define __PRINT_BACKTRACE_H__
#ifdef __cplusplus
extern "C" {
#endif
void print_backtrace();
#ifdef __cplusplus
}
#endif
#endif // __PRINT_BACKTRACE_H__
EOF

cat > print_backtrace.cpp <<-'EOF'
#include "print_backtrace.h"
#include <stdio.h>
#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <stdexcept>
#include <Python.h>

#define MAX_FRAMES   128
#define BUFFER_SIZE  512

void print_backtrace()
{
    // 1.打印调用栈
    void *buff[MAX_FRAMES] = { 0 };
    unsigned int depth = backtrace(buff, MAX_FRAMES);
    char **strings = backtrace_symbols(buff, depth);
	printf("-----------------------print_backtrace(c/c++)--------------------\n");
    for (int i = 0; i < MAX_FRAMES; i++)
    {
        if (i >= depth) 
        {
            break;
        }
        
        // 1.1 将地址转换为代码路径和行号
        {
            char cmdline[BUFFER_SIZE] = {0};
            snprintf(cmdline, BUFFER_SIZE, "bash ./parser.sh \"%s\"", strings[i]);
            FILE *pFd = popen(cmdline, "r");
            if ( pFd == nullptr )
            {
                printf("popen failed\n");
                return ;
            }
            char buf[BUFFER_SIZE] = {0};
            fread(buf, sizeof(buf)/sizeof (buf[0]), 1, pFd);
            printf("%s", buf);
            pclose(pFd); 
        }        
    }
    free(strings);
    
    // 2.触发python异常
    if (!Py_IsInitialized()) 
    {
        Py_Initialize();
    }        
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();
    PyErr_SetString(PyExc_RuntimeError, "print_backtrace trigger_error");
    PyGILState_Release(gstate);
}
EOF

cat > api.c <<-'EOF'
#include <stdio.h>
#include <stdlib.h>
#include "print_backtrace.h"

int function_a() {
    printf("%s %d\n",__FILE__,__LINE__);
    print_backtrace();
    return 0;
}

int function_b() {
    printf("%s %d\n",__FILE__,__LINE__);
    function_a();
    return 0;
}

int function_c() {
    printf("%s %d\n",__FILE__,__LINE__);
    function_b();
    return 0;
}

int function_d() {
    printf("%s %d\n",__FILE__,__LINE__);
    function_c();
    return 0;
}

int function_e() {
    printf("%s %d\n",__FILE__,__LINE__);
    function_d();
    return 0;
}
EOF

cat > parser.sh <<-'EOF'
#!/bin/bash

line=`echo $1 | awk '{print $1}' | sed 's/[()]/ /g'`
so_path=`echo $line | awk '{print $1}'`

if [ ! -f $so_path ];then
   exit 0
fi

addr_path=`echo $line | awk '{print $2}'`
function_name=`echo $addr_path | awk -F+ '{print $1}'`
offset=`echo $addr_path | awk -F+ '{print $2}'`

if [ $function_name ];then
	function_base=`nm $so_path | grep -w "$function_name" | awk '{print $1}'`
	abs_addr=`echo "$function_base $offset" | gawk --non-decimal-data '{A="0x"$1;B=$2;printf "0x%x\n",A+B}'`
	debug_info=`addr2line -f $abs_addr -e $so_path`
else
	debug_info=`addr2line -f $offset -e $so_path`
fi

echo $debug_info
EOF

cat > main.py <<-'EOF'
import ctypes
import traceback
    
def function_a():
    print("function_a")
    lib = ctypes.CDLL('./libapi.so')
    function_e = lib.function_e
    function_e()

def function_b():
    print("function_b")
    function_a()

def function_c():
    print("function_c")
    function_b()

def function_d():
    print("function_d")
    function_c()

try:
    function_d()
except:
    print("-----------------------print_backtrace(python)--------------------")
    traceback.print_exc()
EOF

g++ -fPIC -g -shared -o libprint_backtrace.so print_backtrace.cpp \
		-I /home/anaconda3/envs/demo/include/python3.10 \
		/home/anaconda3/envs/demo/lib/libpython3.10.so \
		-Wl,-rpath=/home/anaconda3/envs/demo/lib/

gcc -fPIC -g -shared -o libapi.so api.c  -L . -lprint_backtrace -Wl,-rpath=.
python3 main.py

二.输出

function_d
function_c
function_b
function_a
api.c 30
api.c 24
api.c 18
api.c 12
api.c 6
-----------------------print_backtrace(c/c++)--------------------
print_backtrace /home/print_backtrace.cpp:18
function_a /home/api.c:8
function_b /home/api.c:14
function_c /home/api.c:20
function_d /home/api.c:26
function_e /home/api.c:32
ffi_call_unix64 :?
ffi_call_int ffi64.c:?
ffi_call ??:?
addr2line: DWARF error: can't find .debug_ranges section.
_ctypes_callproc.cold :?
addr2line: DWARF error: can't find .debug_ranges section.
PyCFuncPtr_call.cold :?
nm: /lib/x86_64-linux-gnu/libc.so.6: no symbols
?? ??:0
-----------------------print_backtrace(python)--------------------
RuntimeError: print_backtrace trigger_error

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/main.py", line 39, in <module>
    function_d()
  File "/home/main.py", line 36, in function_d
    function_c()
  File "/home/main.py", line 32, in function_c
    function_b()
  File "/home/main.py", line 28, in function_b
    function_a()
  File "/home/main.py", line 24, in function_a
    function_e()
SystemError: <_FuncPtr object at 0x7f10c6d4cdc0> returned a result with an exception set
  • 34
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hi20240217

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值