我是一位有用的助手,可以为您翻译文本。
但是这个程序段出现了段错误:
唯一的区别是第一个创建了
SWIG只向PHP暴露函数,并生成用于与其交互的PHP代理类。这些代理类基本上持有一个资源,该资源与每个暴露的函数一起传递,以及那些函数通常会使用的其他参数。认为这些代理类可能是问题所在,我重写了代码,绕过它们,改为直接使用暴露的函数。与之前一样,这也有效:
我使用 SWIG 创建了一个PHP扩展,并且一切都运行正常,但是当链接方法调用时,我观察到一些奇怪的垃圾回收行为。例如,这个可以工作:
$results = $response->results();
$row = $results->get(0)->iterator()->next();
printf('%s %s' . "\n", $row->getString(0), $row->getString(1));
但是这个程序段出现了段错误:
$row = $response->results()->get(0)->iterator()->next();
printf('%s %s' . "\n", $row->getString(0), $row->getString(1));
唯一的区别是第一个创建了
$results
,而第二个则将调用链接在一起。SWIG只向PHP暴露函数,并生成用于与其交互的PHP代理类。这些代理类基本上持有一个资源,该资源与每个暴露的函数一起传递,以及那些函数通常会使用的其他参数。认为这些代理类可能是问题所在,我重写了代码,绕过它们,改为直接使用暴露的函数。与之前一样,这也有效:
$results = InvocationResponse_results($response->_cPtr);
$row = TableIterator_next(Table_iterator(Tables_get($results, 0)));
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1));
再次出现段错误:
$row = TableIterator_next(Table_iterator(Tables_get(InvocationResponse_results($response->_cPtr), 0)));
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1));
再次强调,唯一的区别在于第一个创建了$results
,而第二个将调用链接在一起。
此时,我花了一些时间在gdb/valgrind中进行调试,并确定当链接调用在一起时,InvocationResponse_results
返回的析构函数被过早地调用。为了观察,我在公开的C++函数及其析构函数的顶部插入了std::cout
语句。这是没有链接的输出:
InvocationResponse_results()
Tables_get()
Table_iterator()
TableIterator_next()
__wrap_delete_TableIterator
Row_getString()
Row_getString()
Hola Mundo
---
__wrap_delete_InvocationResponse
__wrap_delete_Row
__wrap_delete_Tables
我在脚本末尾打印了---
,以便区分脚本执行期间发生的事情和之后发生的事情。 Hola Mundo
来自printf
。其余部分来自C ++。正如您所看到的,所有内容都按预期顺序调用。析构函数仅在脚本执行后调用,尽管TableIterator
析构函数比我预期的要早。然而,这并没有引起任何问题,可能与此无关。现在将其与链接输出进行比较:
InvocationResponse_results()
Tables_get()
__wrap_delete_Tables
Table_iterator()
TableIterator_next()
__wrap_delete_TableIterator
Row_getString()
Segmentation fault (core dumped)
如果不将InvocationResponse_results
的返回值保存到$results
中,它会很快被垃圾回收,甚至在执行调用链(在Tables_get
和Table_iterator
之间)之前就被回收,这很快会导致问题,并最终导致段错误。
我还使用xdebug_debug_zval()
在各种地方检查引用计数,但没有发现任何异常。以下是在不链接的情况下对$results
和$row
输出的结果:
results: (refcount=1, is_ref=0)=resource(18) of type (_p_std__vectorT_voltdb__Table_t)
row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row)
使用链接的 $row
:
row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row)
我已经花了几天时间在这上面,现在我几乎没有什么想法了,所以任何关于如何解决这个问题的见解都将不胜感激。
__wrap_delete_Tables
被调用时窥视了一下_zend_list_delete
,在两种情况下(无段错误和段错误),它都被垃圾回收了,因为它的引用计数 (--le->refcount
) 是 -1。 - Ed Mazurrefcount
字段(在 5.3+ 中为refcount__gc
)设置数据断点。 - Artefactozval_copy_ctor
或直接使用_zend_list_addref
的调用),那么断点将无法捕获它。最好的方法是在原始zval的值上设置读取断点,希望在创建zval的浅拷贝时读取它。 - Artefacto